The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
filter.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: f62246f98c32488ea3287c3b5732984cd1033607 $
19  * @file lib/ldap/filter.c
20  * @brief Functions to handle basic LDAP filter parsing and filtering
21  *
22  * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
23  */
24 
25 #include <freeradius-devel/ldap/base.h>
26 
28  { L("<="), LDAP_FILTER_OP_LE },
29  { L("="), LDAP_FILTER_OP_EQ },
30  { L(">="), LDAP_FILTER_OP_GE }
31 };
33 
34 static bool const fr_ldap_attr_allowed_chars[UINT8_MAX + 1] = {
35  ['-'] = true,
37 };
38 
39 #define FILTER_ATTR_MAX_LEN 256
40 #define FILTER_VALUE_MAX_LEN 256
41 
44 
45 /** Parse LDAP filter logic group
46  *
47  * @param[in,out] node to populate with parsed filter.
48  * @param[in] sbuff pointing to filter to parse.
49  * @param[in] depth to indent debug output, indicating nesting of groups.
50  * @param[in] attr_check callback to check if required attributes are in the query.
51  * @param[in] uctx passed to attribute check callback.
52  * @return
53  * - number of bytes parsed on success
54  * - < 0 on error
55  */
58 {
59  ldap_filter_t *child_node;
60  fr_slen_t ret = 0;
61  fr_slen_t parsed = 0;
62 
63  fr_sbuff_switch(sbuff, '\0') {
64  case '&':
65  node->logic_op = LDAP_FILTER_LOGIC_AND;
66  node->orig = talloc_typed_strdup(node, "&");
67  break;
68 
69  case '|':
70  node->logic_op = LDAP_FILTER_LOGIC_OR;
71  node->orig = talloc_typed_strdup(node, "|");
72  break;
73 
74  case '!':
75  node->logic_op = LDAP_FILTER_LOGIC_NOT;
76  node->orig = talloc_typed_strdup(node, "!");
77  break;
78  }
79  parsed += fr_sbuff_advance(sbuff, 1);
80 
81  DEBUG3("%*sCreating LDAP filter group %s", depth, "", node->orig);
83  fr_dlist_init(&node->children, ldap_filter_t, entry);
84  MEM(child_node = talloc_zero(node, ldap_filter_t));
85  fr_dlist_insert_head(&node->children, child_node);
86 
87  depth += 2;
88  ret = ldap_filter_parse_node(child_node, sbuff, depth, attr_check, uctx);
89  if (ret < 0) return ret;
90  parsed += ret;
91 
92  /*
93  * Look for sibling nodes to the child just processed
94  */
95  while (fr_sbuff_is_char(sbuff, '(')) {
96  if (node->logic_op == LDAP_FILTER_LOGIC_NOT) {
97  fr_strerror_const("'!' operator can only apply to one filter");
98  FR_SBUFF_ERROR_RETURN(sbuff);
99  }
100  MEM(child_node = talloc_zero(node, ldap_filter_t));
101  fr_dlist_insert_tail(&node->children, child_node);
102  ret = ldap_filter_parse_node(child_node, sbuff, depth, attr_check, uctx);
103  if (ret < 0) return ret;
104  parsed += ret;
105  }
106 
107  return parsed;
108 }
109 
110 /** Parse individual LDAP filter
111  *
112  * @param[in,out] node to populate with parsed filter.
113  * @param[in] sbuff pointing to filter to parse.
114  * @param[in] depth to indent debug output, indicating nesting of groups.
115  * @param[in] attr_check callback to check if required attributes are in the query.
116  * @param[in] uctx passed to attribute check callback.
117  * @return
118  * - number of bytes parsed on success
119  * - < 0 on error
120  */
123 {
124  char attr_buffer[FILTER_ATTR_MAX_LEN], val_buffer[FILTER_VALUE_MAX_LEN];
125  fr_sbuff_t attr_sbuff = FR_SBUFF_IN(attr_buffer, FILTER_ATTR_MAX_LEN);
126  fr_sbuff_t val_sbuff = FR_SBUFF_IN(val_buffer, FILTER_VALUE_MAX_LEN);
127  size_t len;
128  ssize_t slen;
129  ldap_filter_op_t op;
130  fr_sbuff_marker_t marker;
131 
132  fr_sbuff_marker(&marker, sbuff);
133 
134  /*
135  * Extract the attribute name, blanking the buffer first.
136  */
137  memset(attr_buffer, 0, FILTER_ATTR_MAX_LEN);
139  if (len == 0) {
140  fr_strerror_const("Missing attribute name");
141  FR_SBUFF_ERROR_RETURN(sbuff);
142  }
143 
144  MEM(node->attr = talloc_zero_array(node, char, len+1));
145  memcpy(node->attr, attr_buffer, len);
146 
147  /*
148  * Check for the attribute needed for the filter using the
149  * provided callback.
150  */
151  if (attr_check) attr_check(node->attr, uctx);
152 
153  /*
154  * If the attribute name is followed by ':' there is an
155  * extended match rule. We only support two of them.
156  */
157  if (fr_sbuff_next_if_char(sbuff, ':')) {
159  node->op = LDAP_FILTER_OP_BIT_AND;
160  goto found_op;
161  }
163  node->op = LDAP_FILTER_OP_BIT_OR;
164  goto found_op;
165  }
166 
167  fr_strerror_const("Unsupported extended match rule");
168  FR_SBUFF_ERROR_RETURN(sbuff);
169 
170  found_op:
171  if(!(fr_sbuff_next_if_char(sbuff, ':'))) {
172  fr_strerror_const("Missing ':' after extended match rule");
173  FR_SBUFF_ERROR_RETURN(sbuff);
174  }
175  }
176 
178 
179  switch(op) {
180  case LDAP_FILTER_OP_EQ:
181  if (node->op == LDAP_FILTER_OP_UNSET) node->op = op;
182  break;
183 
184  case LDAP_FILTER_OP_LE:
185  case LDAP_FILTER_OP_GE:
186  node->op = op;
187  break;
188 
189  default:
190  fr_strerror_const("Incorrect operator");
191  FR_SBUFF_ERROR_RETURN(sbuff);
192  }
193 
194  if (((node->op == LDAP_FILTER_OP_BIT_AND) || (node->op == LDAP_FILTER_OP_BIT_OR)) &&
195  (op != LDAP_FILTER_OP_EQ)) {
196  fr_strerror_const("Extended match rule only valid with '=' operator");
197  FR_SBUFF_ERROR_RETURN(sbuff);
198  }
199 
200  /*
201  * Capture everything up to the next ')' as the value, blanking the buffer first.
202  */
203  memset(val_buffer, 0, FILTER_VALUE_MAX_LEN);
204  len = fr_sbuff_out_bstrncpy_until(&val_sbuff, sbuff, FILTER_VALUE_MAX_LEN - 1, &FR_SBUFF_TERM(")"), NULL);
205 
206  if (len == 0) {
207  fr_strerror_const("Missing filter value");
208  FR_SBUFF_ERROR_RETURN(sbuff);
209  }
210 
211  /*
212  * An equality test with a value of '*' is a present test
213  */
214  if ((len == 1) && (*val_buffer == '*') && (node->op == LDAP_FILTER_OP_EQ)) node->op = LDAP_FILTER_OP_PRESENT;
215 
216  /*
217  * Equality tests with '*' in the value are substring matches
218  */
219  fr_sbuff_set_to_start(&val_sbuff);
220  if ((node->op == LDAP_FILTER_OP_EQ) && (fr_sbuff_adv_to_chr(&val_sbuff, SIZE_MAX, '*'))) {
221  node->op = LDAP_FILTER_OP_SUBSTR;
222  }
223 
224  MEM(node->value = fr_value_box_alloc_null(node));
225 
226  switch (node->op) {
227  case LDAP_FILTER_OP_EQ:
230  if (fr_value_box_bstrndup(node, node->value, NULL, val_buffer, len, false) < 0) {
231  fr_strerror_const("Failed parsing value for filter");
232  FR_SBUFF_ERROR_RETURN(sbuff);
233  }
234  break;
235 
236  /*
237  * Since we don't have the LDAP schema, we make an assumption that <=, >= and
238  * bitwise operators are going to be used with numeric attributes
239  */
240  case LDAP_FILTER_OP_GE:
241  case LDAP_FILTER_OP_LE:
244  if (fr_value_box_from_str(node, node->value, FR_TYPE_UINT32, NULL,
245  val_buffer, len, NULL, false) < 0) {
246  fr_strerror_const("Failed parsing value for filter");
247  FR_SBUFF_ERROR_RETURN(sbuff);
248  }
249  break;
250 
251  /*
252  * Operator should not be unset at the end of a filter
253  */
255  fr_assert(0);
256  break;
257  }
258 
259  /*
260  * Take a copy of the original filter for debug output
261  */
262  MEM(node->orig = talloc_zero_array(node, char, fr_sbuff_diff(sbuff, &marker) + 1));
263  memcpy(node->orig, fr_sbuff_current(&marker), fr_sbuff_diff(sbuff, &marker));
264  DEBUG3("%*sParsed LDAP filter (%s)", depth, "", node->orig);
265 
266  return fr_sbuff_diff(sbuff, &marker);
267 }
268 
269 /** Parse individual LDAP filter nodes
270  *
271  * A node can either be a group of nodes joined with a logical operator
272  * or an individual filter.
273  *
274  * @param[in,out] node to populate with parsed filter.
275  * @param[in] sbuff pointing to filter to parse.
276  * @param[in] depth to indent debug output, indicating nesting of groups.
277  * @param[in] attr_check callback to check if required attributes are in the query.
278  * @param[in] uctx passed to attribute check callback.
279  * @return
280  * - number of bytes parsed on success
281  * - < 0 on error
282  */
285 {
286  fr_sbuff_marker_t marker;
287  fr_slen_t ret;
288  fr_slen_t parsed = 0;
289 
290  static bool const logical_op_chars[UINT8_MAX +1] = {
291  ['!'] = true, ['&'] = true, ['|'] = true,
292  };
293 
294  if (!fr_sbuff_next_if_char(sbuff, '(')) {
295  fr_strerror_const("Missing '('");
296  FR_SBUFF_ERROR_RETURN(sbuff);
297  }
298 
299  /*
300  * Firstly, look for the characters which indicate the start of a group of filters
301  * to be combined with a logical operator.
302  */
303  fr_sbuff_marker(&marker, sbuff);
304  if (fr_sbuff_adv_past_allowed(sbuff, 1, logical_op_chars, NULL)) {
305  fr_sbuff_set(sbuff, &marker);
306  ret = ldap_filter_parse_logic(node, sbuff, depth, attr_check, uctx);
307  } else {
308  ret = ldap_filter_parse_filter(node, sbuff, depth, attr_check, uctx);
309  }
310 
311  if (ret < 0) return ret;
312  parsed += ret;
313 
314  if (!fr_sbuff_next_if_char(sbuff, ')')) {
315  fr_strerror_const("Missing ')'");
316  FR_SBUFF_ERROR_RETURN(sbuff);
317  }
318  parsed ++;
319 
320  /*
321  * If we're at the very top level we should be at the end
322  * of the buffer
323  */
324  if ((depth == 0) && (fr_sbuff_extend(sbuff))) {
325  fr_strerror_const("Extra characters at the end of LDAP filter");
326  FR_SBUFF_ERROR_RETURN(sbuff);
327  }
328 
329  return parsed;
330 }
331 
332 /** Parse an LDAP filter into its component nodes
333  *
334  * @param[in] ctx to allocate nodes in.
335  * @param[in,out] root where to allocate the root of the parsed filter.
336  * @param[in] filter to parse.
337  * @param[in] attr_check callback to check if required attributes are in the query.
338  * @param[in] uctx passed to attribute check callback.
339  * @return
340  * - number of bytes parsed on success
341  * < 0 on failure
342  */
343 fr_slen_t fr_ldap_filter_parse(TALLOC_CTX *ctx, fr_dlist_head_t **root, fr_sbuff_t *filter,
345 {
346  ldap_filter_t *node;
347  fr_slen_t ret;
348 
349  MEM(*root = talloc_zero(ctx, fr_dlist_head_t));
350  fr_dlist_init(*root, ldap_filter_t, entry);
351 
352  MEM(node = talloc_zero(*root, ldap_filter_t));
353  fr_dlist_insert_head(*root, node);
354 
355  ret = ldap_filter_parse_node(node, filter, 0, attr_check, uctx);
356  if (ret < 0) {
357  talloc_free(*root);
358  *root = NULL;
359  return ret;
360  }
361 
362  return ret;
363 }
364 
365 static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth);
366 
367 /** Evaluate a group of LDAP filters
368  *
369  * Groups have a logical operator of &, | or !
370  *
371  * @param[in] group to evaluate.
372  * @param[in] conn LDAP connection the message being filtered was returned on
373  * @param[in] msg to filter
374  * @param[in] depth to indent debug messages, reflecting group nesting
375  * @return true or false result of the group evaluation
376  */
377 static bool ldap_filter_group_eval(ldap_filter_t *group, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
378 {
379  ldap_filter_t *node = NULL;
380  bool filter_state = false;
381 
382  DEBUG3("%*sEvaluating LDAP filter group %s", depth, "", group->orig);
383  depth += 2;
384  while ((node = fr_dlist_next(&group->children, node))) {
385  switch (node->filter_type) {
386  case LDAP_FILTER_GROUP:
387  filter_state = ldap_filter_group_eval(node, conn, msg, depth);
388  break;
389  case LDAP_FILTER_NODE:
390  filter_state = ldap_filter_node_eval(node, conn, msg, depth);
391  break;
392  }
393 
394  /*
395  * Short circuit the group depending on the logical operator
396  * and the return state of the last node
397  */
398  if (((group->logic_op == LDAP_FILTER_LOGIC_OR) && filter_state) ||
399  ((group->logic_op == LDAP_FILTER_LOGIC_AND) && !filter_state)) {
400  break;
401  }
402  }
403 
404  filter_state = (group->logic_op == LDAP_FILTER_LOGIC_NOT ? !filter_state : filter_state);
405 
406  depth -= 2;
407  DEBUG3("%*sLDAP filter group %s results in %s", depth, "", group->orig, (filter_state ? "TRUE" : "FALSE"));
408  return filter_state;
409 }
410 
411 #define DEBUG_LDAP_ATTR_VAL if (DEBUG_ENABLED3) { \
412  fr_value_box_t value_box; \
413  fr_ldap_berval_to_value_str_shallow(&value_box, values[i]); \
414  DEBUG3("%*s Evaluating attribute \"%s\", value \"%pV\"", depth, "", node->attr, &value_box); \
415 }
416 
417 /** Evaluate a single LDAP filter node
418  *
419  * @param[in] node to evaluate.
420  * @param[in] conn LDAP connection the message being filtered was returned on.
421  * @param[in] msg to filter.
422  * @param[in] depth to indent debug messages, reflecting group nesting.
423  * @return true or false result of the node evaluation.
424  */
425 static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
426 {
427  struct berval **values;
428  int count, i;
429  bool filter_state = false;
430 
431  switch (node->filter_type) {
432  case LDAP_FILTER_GROUP:
433  return ldap_filter_group_eval(node, conn, msg, depth);
434 
435  case LDAP_FILTER_NODE:
436  DEBUG3("%*sEvaluating LDAP filter (%s)", depth, "", node->orig);
437  values = ldap_get_values_len(conn->handle, msg, node->attr);
438  count = ldap_count_values_len(values);
439 
440  switch (node->op) {
442  filter_state = (count > 0 ? true : false);
443  break;
444 
445  case LDAP_FILTER_OP_EQ:
446  for (i = 0; i < count; i++) {
448  if ((node->value->vb_length == values[i]->bv_len) &&
449  (strncasecmp(values[i]->bv_val, node->value->vb_strvalue, values[i]->bv_len) == 0)) {
450  filter_state = true;
451  break;
452  }
453  }
454  break;
455 
456  /*
457  * LDAP filters only use one wildcard character '*' for zero or more
458  * character matches.
459  */
461  {
462  char const *v, *t, *v_end, *t_end;
463  bool skip;
464 
465  /*
466  * Point t_end at the final character of the filter value
467  * - not the NULL - so we can check for trailing '*'
468  */
469  t_end = node->value->vb_strvalue + node->value->vb_length - 1;
470 
471  for (i = 0; i < count; i++) {
473  t = node->value->vb_strvalue;
474  v = values[i]->bv_val;
475  v_end = values[i]->bv_val + values[i]->bv_len - 1;
476  skip = false;
477 
478  /*
479  * Walk the value (v) and test (t), comparing until
480  * there is a mismatch or the end of one is reached.
481  */
482  while ((v <= v_end) && (t <= t_end)) {
483  /*
484  * If a wildcard is found in the test,
485  * indicate that we can skip non-matching
486  * characters in the value
487  */
488  if (*t == '*'){
489  skip = true;
490  t++;
491  continue;
492  }
493  if (skip) {
494  while ((tolower((uint8_t) *t) != tolower((uint8_t) *v)) && (v <= v_end)) v++;
495  }
496  if (tolower((uint8_t) *t) != tolower((uint8_t) *v)) break;
497  skip = false;
498  t++;
499  v++;
500  }
501 
502  /*
503  * If we've got to the end of both the test and value,
504  * or we've used all of the test and the last character is '*'
505  * then we've matched the pattern.
506  */
507  if (((v > v_end) && (t > t_end)) || ((t >= t_end) && (*t_end == '*'))) {
508  filter_state = true;
509  break;
510  }
511  }
512  }
513  break;
514 
515  /*
516  * For >=, <= and bitwise operators, we assume numeric values
517  */
518  case LDAP_FILTER_OP_GE:
519  case LDAP_FILTER_OP_LE:
522  {
523  char buffer[11]; /* Max uint32_t + 1 */
524  uint32_t value;
525  for (i = 0; i < count; i++) {
527  /*
528  * String too long for max uint32
529  */
530  if (values[i]->bv_len > 10) continue;
531 
532  /*
533  * bv_val is not NULL terminated - so copy to a
534  * NULL terminated string before parsing.
535  */
536  memcpy(buffer, values[i]->bv_val, values[i]->bv_len);
537  buffer[values[i]->bv_len] = '\0';
538 
539  value = (uint32_t)strtol(buffer, NULL, 10);
540  switch (node->op) {
541  case LDAP_FILTER_OP_GE:
542  if (value >= node->value->vb_uint32) filter_state = true;
543  break;
544  case LDAP_FILTER_OP_LE:
545  if (value <= node->value->vb_uint32) filter_state = true;
546  break;
548  if (value & node->value->vb_uint32) filter_state = true;
549  break;
551  if (value | node->value->vb_uint32) filter_state = true;
552  break;
553  default:
554  fr_assert(0);
555  break;
556  }
557  if (filter_state) break;
558  }
559  }
560  break;
561 
562  default:
563  fr_assert(0);
564  break;
565 
566  }
567 
568  ldap_value_free_len(values);
569  }
570 
571  DEBUG3("%*sLDAP filter returns %s", depth, "", (filter_state ? "TRUE" : "FALSE"));
572 
573  return filter_state;
574 }
575 
576 /** Evaluate an LDAP filter
577  *
578  * @param[in] root of the LDAP filter to evaluate.
579  * @param[in] conn LDAP connection the message being filtered was returned on.
580  * @param[in] msg to filter.
581  * @return true or false result of the node evaluation.
582  */
584  return ldap_filter_node_eval(fr_dlist_head(root), conn, msg, 0);
585 }
#define true
Definition: abinary.c:57
static int const char char buffer[256]
Definition: acutest.h:574
log_entry msg
Definition: acutest.h:794
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define NUM_ELEMENTS(_t)
Definition: build.h:335
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
Test enumeration values.
Definition: dict_test.h:92
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition: dlist.h:486
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition: dlist.h:338
Head of a doubly linked list.
Definition: dlist.h:51
fr_slen_t fr_ldap_filter_parse(TALLOC_CTX *ctx, fr_dlist_head_t **root, fr_sbuff_t *filter, filter_attr_check_t attr_check, void *uctx)
Parse an LDAP filter into its component nodes.
Definition: filter.c:343
static fr_slen_t ldap_filter_parse_filter(ldap_filter_t *node, fr_sbuff_t *sbuff, int depth, filter_attr_check_t attr_check, void *uctx)
Parse individual LDAP filter.
Definition: filter.c:121
static size_t ldap_filter_op_table_len
Definition: filter.c:32
bool fr_ldap_filter_eval(fr_dlist_head_t *root, fr_ldap_connection_t *conn, LDAPMessage *msg)
Evaluate an LDAP filter.
Definition: filter.c:583
static bool const fr_ldap_attr_allowed_chars[UINT8_MAX+1]
Definition: filter.c:34
static bool ldap_filter_group_eval(ldap_filter_t *group, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
Evaluate a group of LDAP filters.
Definition: filter.c:377
#define FILTER_VALUE_MAX_LEN
Definition: filter.c:40
#define DEBUG_LDAP_ATTR_VAL
Definition: filter.c:411
#define FILTER_ATTR_MAX_LEN
Definition: filter.c:39
static fr_slen_t ldap_filter_parse_node(ldap_filter_t *node, fr_sbuff_t *sbuff, int depth, filter_attr_check_t attr_check, void *uctx)
Parse individual LDAP filter nodes.
Definition: filter.c:283
static fr_table_num_sorted_t const ldap_filter_op_table[]
Definition: filter.c:27
static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
Evaluate a single LDAP filter node.
Definition: filter.c:425
static fr_slen_t ldap_filter_parse_logic(ldap_filter_t *node, fr_sbuff_t *sbuff, int depth, filter_attr_check_t attr_check, void *uctx)
Parse LDAP filter logic group.
Definition: filter.c:56
@ LDAP_FILTER_LOGIC_NOT
Definition: base.h:544
@ LDAP_FILTER_LOGIC_OR
Definition: base.h:543
@ LDAP_FILTER_LOGIC_AND
Definition: base.h:542
LDAP * handle
libldap handle.
Definition: base.h:333
#define LDAP_MATCHING_RULE_BIT_OR
OID of bit-wise OR LDAP match rule.
Definition: base.h:120
@ LDAP_FILTER_GROUP
The filter node is a parent of a group which will be combined using a logical operator.
Definition: base.h:535
@ LDAP_FILTER_NODE
The filter node is an individual one to be evaluated against an attribute.
Definition: base.h:533
ldap_filter_op_t
Operators for use in LDAP filters.
Definition: base.h:549
@ LDAP_FILTER_OP_BIT_AND
Bitwise AND comparison.
Definition: base.h:556
@ LDAP_FILTER_OP_PRESENT
Attribute present.
Definition: base.h:553
@ LDAP_FILTER_OP_SUBSTR
Attribute matches string with wildcards.
Definition: base.h:552
@ LDAP_FILTER_OP_EQ
Attribute equals value.
Definition: base.h:551
@ LDAP_FILTER_OP_LE
Attribute less than or equal to value.
Definition: base.h:555
@ LDAP_FILTER_OP_BIT_OR
Bitwise OR comparison.
Definition: base.h:557
@ LDAP_FILTER_OP_GE
Attribute greater than or equal to value.
Definition: base.h:554
@ LDAP_FILTER_OP_UNSET
Attribute not set yet.
Definition: base.h:550
char * orig
Text representation of filter for debug messages,.
Definition: base.h:565
ldap_filter_type_t filter_type
Type of this filter node.
Definition: base.h:564
#define LDAP_MATCHING_RULE_BIT_AND
OID of bit-wise AND LDAP match rule.
Definition: base.h:119
int(* filter_attr_check_t)(char const *attr, void *uctx)
Definition: base.h:971
Tracks the state of a libldap connection handle.
Definition: base.h:332
Structure to hold parsed details of LDAP filters.
Definition: base.h:562
#define DEBUG3(_fmt,...)
Definition: log.h:266
talloc_free(reap)
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
Definition: merged_model.c:166
unsigned char uint8_t
Definition: merged_model.c:30
ssize_t fr_slen_t
Definition: merged_model.c:35
#define UINT8_MAX
Definition: merged_model.c:32
size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, bool const allowed[static UINT8_MAX+1])
Definition: merged_model.c:151
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
int strncasecmp(char *s1, char *s2, int n)
Definition: missing.c:36
static int attr_check(CONF_SECTION *conf, tmpl_t *tmpl, char const *name, fr_dict_attr_flags_t *flags)
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static UINT8_MAX+1], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition: sbuff.c:1755
char * fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c)
Wind position to first instance of specified char.
Definition: sbuff.c:1934
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition: sbuff.c:2066
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_diff(_a, _b)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_extend(_sbuff_or_marker)
#define SBUFF_CHAR_CLASS_ALPHA_NUM
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_switch(_sbuff_or_marker, _eob)
#define FR_SBUFF_TERM(_str)
Initialise a terminal structure with a single string.
Definition: sbuff.h:155
return count
Definition: module.c:163
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:49
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:445
#define fr_strerror_const(_msg)
Definition: strerror.h:223
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
Definition: value.c:5315
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition: value.c:4148
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:632