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: 3bd6ab7750a8b9846e1ed76c09e5a888f72dea21 $
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: 3bd6ab7750a8b9846e1ed76c09e5a888f72dea21 $")
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_uri_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 **values = NULL;
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 values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.obj_name_attr);
308 if (!values) {
309 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
310 rcode = RLM_MODULE_INVALID;
311 goto finish;
312 }
313
314 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
315 fr_pair_value_bstrndup(vp, values[0]->bv_val, values[0]->bv_len, true);
316 fr_pair_append(&group_ctx->groups, vp);
317 RDEBUG2("Group DN \"%s\" resolves to name \"%pV\"", *group_ctx->dn, &vp->data);
318
319finish:
320 /*
321 * Walk the pointer to the DN being resolved forward
322 * ready for the next resolution.
323 */
324 group_ctx->dn++;
325
326 if (values) ldap_value_free_len(values);
327 talloc_free(query);
328
329 RETURN_UNLANG_RCODE(rcode);
330}
331
332/** Move user object group attributes to the control list
333 *
334 * @param p_result The result of adding user object group attributes
335 * @param request Current request.
336 * @param group_ctx Context used to evaluate group attributes
337 * @return RLM_MODULE_OK
338 */
340 ldap_group_userobj_ctx_t *group_ctx)
341{
342 fr_pair_t *vp;
343 fr_pair_list_t *list;
344
345 list = tmpl_list_head(request, request_attr_control);
346 fr_assert(list != NULL);
347
348 RDEBUG2("Adding cacheable user object memberships");
349 RINDENT();
350 if (RDEBUG_ENABLED) {
351 for (vp = fr_pair_list_head(&group_ctx->groups);
352 vp;
353 vp = fr_pair_list_next(&group_ctx->groups, vp)) {
354 RDEBUG2("control.%s += \"%pV\"", group_ctx->inst->group.cache_da->name, &vp->data);
355 }
356 }
357
358 fr_pair_list_append(list, &group_ctx->groups);
359 REXDENT();
360
361 talloc_free(group_ctx);
363}
364
365/** Initiate DN to name and name to DN group lookups
366 *
367 * Called repeatedly until there are no more lookups to perform
368 * or an unresolved lookup causes the module to fail.
369 *
370 * @param p_result The result of the previous expansion.
371 * @param request Current request.
372 * @param uctx The group context being processed.
373 * @return One of the RLM_MODULE_* values.
374 */
376{
377 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
378
379 /*
380 * If we've previously failed to expand, fail the group section
381 */
382 switch (p_result->rcode) {
383 case RLM_MODULE_FAIL:
385 talloc_free(group_ctx);
387 default:
388 break;
389 }
390
391 /*
392 * Are there any DN to resolve to names?
393 * These are resolved one at a time as most directories don't allow for
394 * filters on the DN.
395 */
396 if (*group_ctx->dn) {
398 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
403 group_ctx) < 0) RETURN_UNLANG_FAIL;
405 }
406
407 /*
408 * Are there any names to resolve to DN?
409 */
410 if (*group_ctx->group_name) {
412 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
417 group_ctx) < 0) RETURN_UNLANG_FAIL;
419 }
420
421 /*
422 * Nothing left to resolve, move the resulting attributes to
423 * the control list.
424 */
425 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
426}
427
428/** Convert group membership information into attributes
429 *
430 * This may just be able to parse attribute values in the user object
431 * or it may need to yield to other LDAP searches depending on what was
432 * returned and what is set to be cached.
433 *
434 * @param[out] p_result The result of trying to resolve a dn to a group name.
435 * @param[in] request Current request.
436 * @param[in] autz_ctx LDAP authorization context being processed.
437 * @param[in] attr membership attribute to look for in the entry.
438 * @return One of the RLM_MODULE_* values.
439 */
441 char const *attr)
442{
443 rlm_ldap_t const *inst = autz_ctx->inst;
444 LDAPMessage *entry = autz_ctx->entry;
445 fr_ldap_thread_trunk_t *ttrunk = autz_ctx->ttrunk;
446 ldap_group_userobj_ctx_t *group_ctx;
447 struct berval **values;
448 char **name_p;
449 char **dn_p;
450 fr_pair_t *vp;
451 int is_dn, i, count, name2dn = 0, dn2name = 0;
452
453 fr_assert(entry);
454 fr_assert(attr);
455
456 /*
457 * Parse the membership information we got in the initial user query.
458 */
459 values = ldap_get_values_len(fr_ldap_handle_thread_local(), entry, attr);
460 if (!values) {
461 RDEBUG2("No cacheable group memberships found in user object");
462
464 }
465 count = ldap_count_values_len(values);
466
467 /*
468 * Set up context for managing group membership attribute resolution.
469 */
470 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_userobj_ctx_t));
471 group_ctx->inst = inst;
472 group_ctx->ttrunk = ttrunk;
473 group_ctx->base_dn = &autz_ctx->call_env->group_base;
474 group_ctx->list_ctx = tmpl_list_ctx(request, request_attr_control);
475 fr_assert(group_ctx->list_ctx != NULL);
476
477 /*
478 * Set up pointers to entries in arrays of names / DNs to resolve.
479 */
480 name_p = group_ctx->group_name;
481 group_ctx->dn = dn_p = group_ctx->group_dn;
482
483 /*
484 * Temporary list to hold new group VPs, will be merged
485 * once all group info has been gathered/resolved
486 * successfully.
487 */
488 fr_pair_list_init(&group_ctx->groups);
489
490 for (i = 0; (i < count); i++) {
491 is_dn = fr_ldap_util_is_dn(values[i]->bv_val, values[i]->bv_len);
492
493 if (inst->group.cacheable_dn) {
494 /*
495 * The easy case, we're caching DNs and we got a DN.
496 */
497 if (is_dn) {
498 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
499 fr_pair_value_bstrndup(vp, values[i]->bv_val, values[i]->bv_len, true);
500 fr_pair_append(&group_ctx->groups, vp);
501 /*
502 * We were told to cache DNs but we got a name, we now need to resolve
503 * this to a DN. Store all the group names in an array so we can do one query.
504 */
505 } else {
506 if (++name2dn > LDAP_MAX_CACHEABLE) {
507 REDEBUG("Too many groups require name to DN resolution");
508 invalid:
509 ldap_value_free_len(values);
510 talloc_free(group_ctx);
512 }
513 *name_p++ = fr_ldap_berval_to_string(group_ctx, values[i]);
514 }
515 }
516
517 if (inst->group.cacheable_name) {
518 /*
519 * The easy case, we're caching names and we got a name.
520 */
521 if (!is_dn) {
522 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
523 fr_pair_value_bstrndup(vp, values[i]->bv_val, values[i]->bv_len, true);
524 fr_pair_append(&group_ctx->groups, vp);
525 /*
526 * We were told to cache names but we got a DN, we now need to resolve
527 * this to a name. Store group DNs which need resolving to names.
528 */
529 } else {
530 if (++dn2name > LDAP_MAX_CACHEABLE) {
531 REDEBUG("Too many groups require DN to name resolution");
532 goto invalid;
533 }
534 *dn_p++ = fr_ldap_berval_to_string(group_ctx, values[i]);
535 }
536 }
537 }
538
539 ldap_value_free_len(values);
540
541 /*
542 * We either have group names which need converting to DNs or
543 * DNs which need resolving to names. Push a function which will
544 * do the resolution.
545 */
546 if ((name_p != group_ctx->group_name) || (dn_p != group_ctx->group_dn)) {
547 group_ctx->attrs[0] = inst->group.obj_name_attr;
548 if (unlang_function_push_with_result(p_result, request,
550 NULL,
553 group_ctx) < 0) {
554 talloc_free(group_ctx);
556 }
558 }
559
560 /*
561 * No additional queries needed, just process the context to
562 * move any generated pairs into the correct list.
563 */
564 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
565}
566
567/** Initiate an LDAP search for group membership looking at the group objects
568 *
569 * @param[out] p_result Result of submitting LDAP search
570 * @param[in] request Current request.
571 * @param[in] uctx Group lookup context.
572 * @return One of the RLM_MODULE_* values.
573 */
575{
576 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
577 rlm_ldap_t const *inst = group_ctx->inst;
578 fr_value_box_t *filter;
579
580 filter = fr_value_box_list_head(&group_ctx->expanded_filter);
581
582 if (!filter || filter->type != FR_TYPE_STRING) RETURN_UNLANG_FAIL;
583
584 group_ctx->attrs[0] = inst->group.obj_name_attr;
585 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
586 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope,
587 filter->vb_strvalue, group_ctx->attrs, NULL, NULL);
588}
589
590/** Cancel a pending group object lookup.
591 *
592 */
593static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
594{
595 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
596
597 /*
598 * If the query is not in flight, just return
599 */
600 if (!group_ctx->query || !group_ctx->query->treq) return;
601
603}
604
605/** Process the results of a group object lookup.
606 *
607 * @param[out] p_result Result of processing group lookup.
608 * @param[in] request Current request.
609 * @param[in] uctx Group lookup context.
610 * @return One of the RLM_MODULE_* values.
611 */
613{
614 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
615 rlm_ldap_t const *inst = group_ctx->inst;
616 fr_ldap_query_t *query = group_ctx->query;
618 LDAPMessage *entry;
619 int ldap_errno;
620 char *dn;
621 fr_pair_t *vp;
622
623 switch (query->ret) {
624 case LDAP_SUCCESS:
625 break;
626
629 RDEBUG2("No cacheable group memberships found in group objects");
630 rcode = RLM_MODULE_NOTFOUND;
631 goto finish;
632
633 default:
634 rcode = RLM_MODULE_FAIL;
635 goto finish;
636 }
637
638 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
639 if (!entry) {
640 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
641 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
642
643 goto finish;
644 }
645
646 RDEBUG2("Adding cacheable group object memberships");
647 do {
648 if (inst->group.cacheable_dn) {
649 dn = ldap_get_dn(query->ldap_conn->handle, entry);
650 if (!dn) {
651 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
652 REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
653
654 goto finish;
655 }
657
658 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
659 fr_pair_value_strdup(vp, dn, false);
660
661 RINDENT();
662 RDEBUG2("control.%pP", vp);
663 REXDENT();
664 ldap_memfree(dn);
665 }
666
667 if (inst->group.cacheable_name) {
668 struct berval **values;
669
670 values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.obj_name_attr);
671 if (!values) continue;
672
673 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
674 fr_pair_value_bstrndup(vp, values[0]->bv_val, values[0]->bv_len, true);
675
676 RINDENT();
677 RDEBUG2("control.%pP", vp);
678 REXDENT();
679
680 ldap_value_free_len(values);
681 }
682 } while ((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
683
684finish:
685 talloc_free(group_ctx);
686
687 RETURN_UNLANG_RCODE(rcode);
688}
689
690/** Convert group membership information into attributes
691 *
692 * @param[out] p_result The result of trying to resolve a dn to a group name.
693 * @param[in] request Current request.
694 * @param[in] autz_ctx Authentication context being processed.
695 * @return One of the RLM_MODULE_* values.
696 */
698{
699 rlm_ldap_t const *inst = autz_ctx->inst;
700 ldap_group_groupobj_ctx_t *group_ctx;
701
702 if (!inst->group.obj_membership_filter) {
703 RDEBUG2("Skipping caching group objects as directive 'group.membership_filter' is not set");
705 }
706
707 if (autz_ctx->call_env->group_base.type != FR_TYPE_STRING) {
708 REDEBUG("Missing group base_dn");
710 }
711
712 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_groupobj_ctx_t));
713 group_ctx->inst = inst;
714 group_ctx->ttrunk = autz_ctx->ttrunk;
715 group_ctx->base_dn = &autz_ctx->call_env->group_base;
716 fr_value_box_list_init(&group_ctx->expanded_filter);
717
719 request,
724 group_ctx) < 0) {
725 error:
726 talloc_free(group_ctx);
728 }
729
730 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;
731
733}
734
735/** Process the results of a group object lookup.
736 *
737 * @param[out] p_result Result of processing group lookup.
738 * @param[in] request Current request.
739 * @param[in] uctx Group lookup context.
740 * @return One of the RLM_MODULE_* values.
741 */
743{
744 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
745 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->uctx, ldap_group_xlat_ctx_t);
746 fr_ldap_query_t *query = group_ctx->query;
748
749 switch (query->ret) {
750 case LDAP_SUCCESS:
751 xlat_ctx->found = true;
752 if (RDEBUG_ENABLED2) {
753 LDAPMessage *entry = NULL;
754 char *dn = NULL;
755 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
756 if (entry) {
757 dn = ldap_get_dn(query->ldap_conn->handle, entry);
758 RDEBUG2("User found in group object \"%pV\"", fr_box_strvalue(dn));
759 ldap_memfree(dn);
760 }
761 }
762 break;
763
766 rcode = RLM_MODULE_NOTFOUND;
767 break;
768
769 default:
770 rcode = RLM_MODULE_FAIL;
771 break;
772 }
773
774 talloc_free(group_ctx);
775 RETURN_UNLANG_RCODE(rcode);
776}
777
778/** Initiate an LDAP search to determine group membership, querying group objects
779 *
780 * Used by LDAP group membership xlat
781 *
782 * @param p_result Current module result code.
783 * @param request Current request.
784 * @param xlat_ctx xlat context being processed.
785 */
788{
789 rlm_ldap_t const *inst = xlat_ctx->inst;
790 ldap_group_groupobj_ctx_t *group_ctx;
791
793 *group_ctx = (ldap_group_groupobj_ctx_t) {
794 .inst = inst,
795 .ttrunk = xlat_ctx->ttrunk,
796 .uctx = xlat_ctx
797 };
798 fr_value_box_list_init(&group_ctx->expanded_filter);
799
800 if (fr_ldap_util_is_dn(xlat_ctx->group->vb_strvalue, xlat_ctx->group->vb_length)) {
801 group_ctx->filter_tmpl = xlat_ctx->env_data->group_filter;
802 group_ctx->base_dn = xlat_ctx->group;
803 } else {
804 char name_filter[LDAP_MAX_FILTER_STR_LEN];
805 char const *filters[] = { name_filter, inst->group.obj_filter, inst->group.obj_membership_filter };
806 tmpl_rules_t t_rules;
807
808 if (!inst->group.obj_name_attr) {
809 REDEBUG("Told to search for group by name, but missing 'group.name_attribute' "
810 "directive");
811 invalid:
812 talloc_free(group_ctx);
814 }
815
816 t_rules = (tmpl_rules_t){
817 .attr = {
818 .dict_def = request->local_dict,
819 .list_def = request_attr_request,
820 },
821 .xlat = {
822 .runtime_el = unlang_interpret_event_list(request),
823 },
824 .at_runtime = true,
825 .escape.box_escape = (fr_value_box_escape_t) {
826 .func = fr_ldap_box_escape,
828 .always_escape = false,
829 },
830 .escape.mode = TMPL_ESCAPE_PRE_CONCAT,
831 .literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
832 .cast = FR_TYPE_STRING,
833 };
834
835 snprintf(name_filter, sizeof(name_filter), "(%s=%s)",
836 inst->group.obj_name_attr, xlat_ctx->group->vb_strvalue);
837
838 if (fr_ldap_filter_to_tmpl(group_ctx, &t_rules, filters, NUM_ELEMENTS(filters),
839 &group_ctx->filter_tmpl) < 0) goto invalid;
840
841 fr_assert(xlat_ctx->env_data);
842 group_ctx->base_dn = &xlat_ctx->env_data->group_base;
843 }
844
846 request,
851 group_ctx) < 0) {
852 error:
853 talloc_free(group_ctx);
855 }
856
857 if (unlang_tmpl_push(group_ctx, NULL, &group_ctx->expanded_filter, request, group_ctx->filter_tmpl, NULL, UNLANG_SUB_FRAME) < 0) goto error;
858
860}
861
862/** Initiate resolving a group DN to its name
863 *
864 */
865static unlang_action_t ldap_dn2name_start(unlang_result_t *p_result, request_t *request, void *uctx)
866{
867 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
869 rlm_ldap_t const *inst = xlat_ctx->inst;
870
871 if (!inst->group.obj_name_attr) {
872 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
874 }
875
876 RDEBUG2("Resolving group DN \"%pV\" to group name", fr_box_strvalue_buffer(group_ctx->lookup_dn));
877
878 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, xlat_ctx->ttrunk,
879 group_ctx->lookup_dn, LDAP_SCOPE_BASE, NULL, group_ctx->attrs,
880 NULL, NULL);
881}
882
883/** Cancel an in-progress DN to name lookup.
884 *
885 */
886static void ldap_dn2name_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
887{
888 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
889
890 if (!group_ctx->query || !group_ctx->query->treq) return;
891
893}
894
895/** Initiate a user lookup to check membership.
896 *
897 * Used when the user's DN is already known but cached group membership has not been stored
898 *
899 */
901 request_t *request, void *uctx)
902{
903 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
904 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
905
906 return fr_ldap_trunk_search(xlat_ctx, &xlat_ctx->query, request, xlat_ctx->ttrunk, xlat_ctx->dn,
907 LDAP_SCOPE_BASE, NULL, xlat_ctx->attrs, NULL, NULL);
908}
909
910/** Process the results of evaluating a user object when checking group membership
911 *
912 * Any information relating to the user's group memberships should be evailable in the group_ctx
913 * structure before this is called.
914 */
916{
917 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
918 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
919 rlm_ldap_t const *inst = xlat_ctx->inst;
920 fr_ldap_query_t *query = xlat_ctx->query;
921 LDAPMessage *entry;
922 int ldap_errno;
923 bool value_is_dn = false;
924 fr_value_box_t *group = xlat_ctx->group;
925 char *value_name = NULL;
926
927 /*
928 * If group_ctx->values is not populated, this is the first call
929 * - extract the returned values if any.
930 */
931 if (!group_ctx->values) {
932 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
933 if (!entry) {
934 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
935 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
937 }
938
939 group_ctx->values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.userobj_membership_attr);
940 if (!group_ctx->values) {
941 RDEBUG2("User object contains no group membership information in attribute \"%s\"",
942 inst->group.userobj_membership_attr);
944 }
945
946 /*
947 * To avoid re-assessing after each call out to do a DN -> name
948 * lookup, cache this.
949 */
950 group_ctx->count = ldap_count_values_len(group_ctx->values);
951 }
952
953 /*
954 * Following a call out to do a DN -> name lookup, group_ctx->query will be
955 * populated - process the results.
956 */
957 if (group_ctx->query) {
958 char *buff;
959 struct berval **values = NULL;
960
961 switch (group_ctx->query->ret) {
963 break;
964
967 REDEBUG("Group DN \"%pV\" did not resolve to an object",
970
971 default:
973 }
974
975 entry = ldap_first_entry(group_ctx->query->ldap_conn->handle, group_ctx->query->result);
976 if (!entry) {
977 ldap_get_option(group_ctx->query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
978 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
980 }
981
982 values = ldap_get_values_len(group_ctx->query->ldap_conn->handle, entry, inst->group.obj_name_attr);
983 if (!values) {
984 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
986 }
987
988 MEM(buff = talloc_bstrndup(group_ctx, values[0]->bv_val, values[0]->bv_len));
989 RDEBUG2("Group DN \"%pV\" resolves to name \"%pV\"", fr_box_strvalue_buffer(group_ctx->lookup_dn),
990 fr_box_strvalue_len(values[0]->bv_val, values[0]->bv_len));
991 ldap_value_free_len(values);
992
993 if (group_ctx->resolving_value) {
994 value_name = buff;
995 } else {
996 group_ctx->group_name = buff;
997 }
998 }
999
1000 /*
1001 * Loop over the list of groups the user is a member of, looking for a match.
1002 */
1003 while (group_ctx->value_no < group_ctx->count) {
1004 struct berval *value = group_ctx->values[group_ctx->value_no];
1005
1006 /*
1007 * We have come back from resolving a membership DN to its name,
1008 * compare to the provided name.
1009 */
1010 if (value_name && group_ctx->resolving_value) {
1011 if (((talloc_strlen(value_name)) == group->vb_length) &&
1012 (memcmp(group->vb_strvalue, value_name, group->vb_length) == 0)) {
1013 RDEBUG2("User found in group \"%pV\". Comparison between membership: name "
1014 "(resolved from DN \"%pV\"), check: name", group,
1015 fr_box_strvalue_buffer(group_ctx->lookup_dn));
1016 talloc_free(value_name);
1017 goto found;
1018 }
1019 talloc_const_free(group_ctx->lookup_dn);
1020 TALLOC_FREE(value_name);
1021 group_ctx->resolving_value = false;
1022 group_ctx->value_no++;
1023 continue;
1024 }
1025
1026 value_is_dn = fr_ldap_util_is_dn(value->bv_val, value->bv_len);
1027
1028 RDEBUG2("Processing %s value \"%pV\" as a %s", inst->group.userobj_membership_attr,
1029 fr_box_strvalue_len(value->bv_val, value->bv_len),
1030 value_is_dn ? "DN" : "group name");
1031
1032 /*
1033 * Both literal group names, do case sensitive comparison
1034 */
1035 if (!xlat_ctx->group_is_dn && !value_is_dn) {
1036 if ((group->vb_length == value->bv_len) &&
1037 (memcmp(value->bv_val, group->vb_strvalue, value->bv_len) == 0)) {
1038 RDEBUG2("User found in group \"%pV\". Comparison between membership: name, check: name",
1039 group);
1040 goto found;
1041 }
1042 group_ctx->value_no++;
1043 continue;
1044 }
1045
1046 /*
1047 * Both DNs, do case insensitive, binary safe comparison
1048 */
1049 if (xlat_ctx->group_is_dn && value_is_dn) {
1050 if (fr_ldap_berval_strncasecmp(value, group->vb_strvalue, group->vb_length) == 0) {
1051 RDEBUG2("User found in group DN \"%pV\". "
1052 "Comparison between membership: dn, check: dn", group);
1053 goto found;
1054 }
1055 group_ctx->value_no++;
1056 continue;
1057 }
1058
1059 /*
1060 * If the value is not a DN, and the name we were given is a dn
1061 * convert the value to a DN and do a comparison.
1062 */
1063 if (!value_is_dn && xlat_ctx->group_is_dn) {
1064 /*
1065 * So we only do the DN -> name lookup once, regardless of how many
1066 * group values we have to check, the resolved name is put in group_ctx->group_name
1067 */
1068 if (!group_ctx->group_name) {
1069 group_ctx->lookup_dn = group->vb_strvalue;
1070
1072
1073 /* Need to push this for the custom cancellation function */
1074 return unlang_function_push_with_result(p_result,
1075 request,
1077 NULL,
1080 group_ctx);
1081 }
1082
1083 if (((talloc_strlen(group_ctx->group_name)) == value->bv_len) &&
1084 (memcmp(value->bv_val, group_ctx->group_name, value->bv_len) == 0)) {
1085 RDEBUG2("User found in group \"%pV\". Comparison between membership: "
1086 "name, check: name (resolved from DN \"%pV\")",
1087 fr_box_strvalue_len(value->bv_val, value->bv_len), group);
1088 goto found;
1089 }
1090 group_ctx->value_no++;
1091 continue;
1092 }
1093
1094 /*
1095 * We have a value which is a DN, and a check item which specifies the name of a group,
1096 * convert the value to a name so we can do a comparison.
1097 */
1098 if (value_is_dn && !xlat_ctx->group_is_dn) {
1099 group_ctx->lookup_dn = fr_ldap_berval_to_string(group_ctx, value);
1100 group_ctx->resolving_value = true;
1101
1103
1104 /* Need to push this for the custom cancellation function */
1105 return unlang_function_push_with_result(p_result,
1106 request,
1108 NULL,
1110 UNLANG_SUB_FRAME, group_ctx);
1111 }
1112
1113 fr_assert(0);
1114 }
1116
1117found:
1118 xlat_ctx->found = true;
1120}
1121
1122/** Ensure retrieved LDAP values are cleared up
1123 *
1124 */
1126{
1127 if (group_ctx->values) ldap_value_free_len(group_ctx->values);
1128 return 0;
1129}
1130
1131/** Query the LDAP directory to check if a user object is a member of a group
1132 *
1133 * @param[out] p_result Result of calling the module.
1134 * @param[in] request Current request.
1135 * @param[in] xlat_ctx Context of the xlat being evaluated.
1136 */
1139{
1140 rlm_ldap_t const *inst = xlat_ctx->inst;
1142
1144 talloc_set_destructor(group_ctx, userobj_dyn_free);
1145
1146 *group_ctx = (ldap_group_userobj_dyn_ctx_t) {
1147 .xlat_ctx = xlat_ctx,
1148 .attrs = { inst->group.obj_name_attr, NULL }
1149 };
1150
1151 RDEBUG2("Checking user object's %s attributes", inst->group.userobj_membership_attr);
1152
1153 /*
1154 * If a previous query was required to find the user DN, that will have
1155 * retrieved the user object membership attribute and the resulting values
1156 * can be checked.
1157 * If not then a query is needed to retrieve the user object.
1158 */
1160 request,
1161 xlat_ctx->query ? NULL : ldap_check_userobj_start,
1163 ldap_group_userobj_cancel, ~FR_SIGNAL_CANCEL,
1165 group_ctx) < 0) {
1166 talloc_free(group_ctx);
1168 }
1169
1171}
1172
1173/** Check group membership attributes to see if a user is a member.
1174 *
1175 * @param[out] p_result Result of calling the module.
1176 * @param[in] inst rlm_ldap configuration.
1177 * @param[in] request Current request.
1178 * @param[in] check vb containing the group value (name or dn).
1179 */
1181 rlm_ldap_t const *inst, request_t *request, fr_value_box_t const *check)
1182{
1183 fr_pair_t *vp;
1184 int ret;
1185 fr_dcursor_t cursor;
1186
1187 /*
1188 * We return RLM_MODULE_INVALID here as an indication
1189 * the caller should try a dynamic group lookup instead.
1190 */
1191 vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, inst->group.cache_da);
1193
1194 for (vp = fr_dcursor_current(&cursor);
1195 vp;
1196 vp = fr_dcursor_next(&cursor)) {
1197 ret = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, check);
1198 if (ret == 1) {
1199 RDEBUG2("User found. Matched cached membership");
1201 }
1202
1203 if (ret < -1) RETURN_UNLANG_FAIL;
1204 }
1205
1206 RDEBUG2("Cached membership not found");
1207
1209}
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:475
#define RCSID(id)
Definition build.h:488
#define UNUSED
Definition build.h:318
#define NUM_ELEMENTS(_t)
Definition build.h:340
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:46
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:900
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:697
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:574
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
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:786
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:1125
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:915
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:440
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:375
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:1137
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:612
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:339
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
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:865
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 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:593
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:742
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:886
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:1180
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
char const * lookup_dn
The DN currently being looked up, when resolving DN to name.
Definition groups.c:80
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 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:1681
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2053
#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:491
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:71
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:569
#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:205
LDAP * handle
libldap handle.
Definition base.h:333
fr_ldap_result_code_t ret
Result code.
Definition base.h:472
#define LDAP_MAX_CACHEABLE
Maximum number of groups we retrieve from the server for a given user which need resolving from name ...
Definition base.h:103
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:458
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:442
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
Definition util.c:111
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:459
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:190
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:194
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:193
static int fr_ldap_berval_strncasecmp(struct berval *value, char const *str, size_t strlen)
Compare a berval with a C string of a known length using case insensitive comparison.
Definition base.h:679
LDAPMessage * result
Head of LDAP results list.
Definition base.h:470
#define LDAP_MAX_GROUP_NAME_LEN
Maximum name of a group name.
Definition base.h:108
LDAP query structure.
Definition base.h:424
Thread LDAP trunk structure.
Definition base.h:401
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1129
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:717
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:455
#define RWDEBUG(fmt,...)
Definition log.h:373
#define RINDENT()
Indent R* messages by one level.
Definition log.h:442
@ 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:2667
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:2816
#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.
ldap_autz_call_env_t * call_env
Definition rlm_ldap.h:207
fr_ldap_thread_trunk_t * ttrunk
Definition rlm_ldap.h:206
rlm_ldap_t const * inst
Definition rlm_ldap.h:203
tmpl_t * group_filter
tmpl to expand as group membership filter.
Definition rlm_ldap.h:149
LDAPMessage * entry
Definition rlm_ldap.h:208
struct rlm_ldap_t::@177 group
fr_value_box_t group_base
Base DN in which to search for groups.
Definition rlm_ldap.h:148
char const * attrs[2]
Definition rlm_ldap.h:235
Holds state of in progress async authorization.
Definition rlm_ldap.h:201
Holds state of in progress group membership check xlat.
Definition rlm_ldap.h:229
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: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:545
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:617
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:253
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:136
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:2170
#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:1022
#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