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