The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
map.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: de9c1b74923382bbaef88f8c8808dc8844c61704 $
19  * @file src/lib/ldap/map.c
20  * @brief Functions for mapping between LDAP and FreeRADIUS attributes.
21  *
22  * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  * @copyright 2013 Network RADIUS SAS (legal@networkradius.com)
24  * @copyright 2013 The FreeRADIUS Server Project.
25  */
26 RCSID("$Id: de9c1b74923382bbaef88f8c8808dc8844c61704 $")
27 
29 
30 #include <freeradius-devel/util/debug.h>
31 #include <freeradius-devel/ldap/base.h>
32 
33 /** Callback for map_to_request
34  *
35  * Performs exactly the same job as map_to_vp, but pulls attribute values from LDAP entries
36  *
37  * @see map_to_vp
38  */
39 int fr_ldap_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
40 {
41  fr_ldap_result_t *self = uctx;
43  fr_pair_list_t tmp_list;
44  fr_pair_t *vp;
45  int i;
46 
48  fr_pair_list_init(&tmp_list);
49 
50  fr_assert(map->lhs->type == TMPL_TYPE_ATTR);
51 
52  /*
53  * This is a mapping in the form of:
54  * <list>. += <ldap attr>
55  *
56  * Where <ldap attr> is:
57  * <list>.<attr> <op> <value>
58  *
59  * It is to allow for legacy installations which stored
60  * RADIUS control and reply attributes in separate LDAP
61  * attributes.
62  */
63  if (tmpl_is_list(map->lhs)) {
64  for (i = 0; i < self->count; i++) {
65  map_t *attr = NULL;
66  char *attr_str;
67 
68  tmpl_rules_t lhs_rules = {
69  .attr = {
70  .dict_def = request->dict,
71  .request_def = tmpl_request(map->lhs),
72  .list_def = tmpl_list(map->lhs),
74  },
75  .xlat = {
76  .runtime_el = unlang_interpret_event_list(request),
77  },
78  .at_runtime = true,
79  };
80 
81  tmpl_rules_t rhs_rules = {
82  .attr = {
83  .dict_def = request->dict
84  },
85  .xlat = {
86  .runtime_el = lhs_rules.xlat.runtime_el,
87  },
88  .at_runtime = true,
89  };
90 
91  RDEBUG3("Parsing valuepair string \"%pV\"",
92  fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
93 
94  /*
95  * bv_val is NOT \0 terminated, so we need to make it
96  * safe (\0 terminate it) before passing it to any
97  * functions which take C strings and no lengths.
98  */
99  attr_str = talloc_bstrndup(NULL, self->values[i]->bv_val, self->values[i]->bv_len);
100  if (!attr_str) {
101  RWDEBUG("Failed making attribute string safe");
102  continue;
103  }
104 
105  if (map_afrom_attr_str(ctx, &attr,
106  attr_str,
107  &lhs_rules, &rhs_rules) < 0) {
108  RPWDEBUG("Failed parsing \"%pV\" as valuepair, skipping...",
109  fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
110  talloc_free(attr_str);
111  continue;
112  }
113 
114  talloc_free(attr_str);
115 
116  if (tmpl_is_data_unresolved(attr->lhs)) {
117  RWDEBUG("Failed parsing left side of \"%pV\", skipping...",
118  fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
119  talloc_free(attr);
120  continue;
121  }
122 
123  if (tmpl_request_ref_list_cmp(tmpl_request(attr->lhs), tmpl_request(map->lhs)) != 0) {
124  char *attr_request;
125  char *map_request;
126 
127  tmpl_request_ref_list_aprint(NULL, &attr_request, tmpl_request(attr->lhs));
128  tmpl_request_ref_list_aprint(NULL, &map_request, tmpl_request(map->lhs));
129 
130  RWDEBUG("valuepair \"%pV\" has conflicting request qualifier (%s vs %s), skipping...",
131  fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len),
132  attr_request, map_request);
133 
134  talloc_free(attr_request);
135  talloc_free(map_request);
136 
137  next_pair:
138  talloc_free(attr);
139  continue;
140  }
141 
142  if ((tmpl_list(attr->lhs) != tmpl_list(map->lhs))) {
143  RWDEBUG("valuepair \"%pV\" has conflicting list qualifier (%s vs %s), skipping...",
144  fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len),
145  tmpl_list_name(tmpl_list(attr->lhs), "<INVALID>"),
146  tmpl_list_name(tmpl_list(map->lhs), "<INVALID>"));
147  goto next_pair;
148  }
149 
150  if (map_to_request(request, attr, map_to_vp, NULL) < 0) {
151  RWDEBUG("Failed creating attribute for valuepair \"%pV\", skipping...",
152  fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
153  goto next_pair;
154  }
155 
156  talloc_free(attr);
157 
158  /*
159  * Only process the first value, unless the operator is +=
160  */
161  if (map->op != T_OP_ADD_EQ) break;
162  }
163  goto finish;
164  }
165 
166  /*
167  * Iterate over all the retrieved values,
168  * don't try and be clever about changing operators
169  * just use whatever was set in the attribute map.
170  */
171  for (i = 0; i < self->count; i++) {
172  if (!self->values[i]->bv_len) continue;
173 
175 
176  if (fr_pair_value_from_str(vp, self->values[i]->bv_val,
177  self->values[i]->bv_len, NULL, true) < 0) {
178  RPWDEBUG("Failed parsing value \"%pV\" for attribute %s",
179  fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len),
180  tmpl_attr_tail_da(map->lhs)->name);
181 
182  talloc_free(vp); /* also frees escaped */
183  continue;
184  }
185 
187 
188  /*
189  * Only process the first value, unless the operator is +=
190  */
191  if (map->op != T_OP_ADD_EQ) break;
192  }
193 
194 finish:
196 
197  return 0;
198 }
199 
200 int fr_ldap_map_verify(map_t *map, UNUSED void *instance)
201 {
202  /*
203  * Destinations where we can put the fr_pair_ts we
204  * create using LDAP values.
205  */
206  switch (map->lhs->type) {
207  case TMPL_TYPE_ATTR:
208  break;
209 
211  cf_log_err(map->ci, "Unknown attribute %s", tmpl_attr_tail_unresolved(map->lhs));
212  return -1;
213 
214  default:
215  cf_log_err(map->ci, "Left hand side of map must be an attribute or list, not a %s",
216  tmpl_type_to_str(map->lhs->type));
217  return -1;
218  }
219 
220  /*
221  * Sources we can use to get the name of the attribute
222  * we're retrieving from LDAP.
223  */
224  switch (map->rhs->type) {
226  case TMPL_TYPE_ATTR:
227  case TMPL_TYPE_EXEC:
229  break;
230 
232  cf_log_err(map->ci, "Unknown attribute %s", tmpl_attr_tail_unresolved(map->rhs));
233  return -1;
234 
235  default:
236  cf_log_err(map->ci, "Right hand side of map must be an xlat, attribute, exec, or literal, not a %s",
237  tmpl_type_to_str(map->rhs->type));
238  return -1;
239  }
240 
241  /*
242  * Only =, :=, += and -= operators are supported for LDAP mappings.
243  */
244  switch (map->op) {
245  case T_OP_SET:
246  case T_OP_EQ:
247  case T_OP_SUB_EQ:
248  case T_OP_ADD_EQ:
249  break;
250 
251  default:
252  cf_log_err(map->ci, "Operator \"%s\" not allowed for LDAP mappings",
253  fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
254  return -1;
255  }
256 
257  return 0;
258 }
259 
260 /** Expand values in an attribute map where needed
261  *
262  * @param[in] ctx o allocate any dynamic expansions in.
263  * @param[out] expanded array of attributes. Need not be initialised (we'll initialise).
264  * @param[in] request The current request.
265  * @param[in] maps to expand.
266  * @param[in] generic_attr name to append to the attribute list.
267  * @return
268  * - 0 on success.
269  * - -1 on failure.
270  */
271 int fr_ldap_map_expand(TALLOC_CTX *ctx, fr_ldap_map_exp_t *expanded, request_t *request, map_list_t const *maps, char const *generic_attr)
272 {
273  map_t const *map = NULL;
274  unsigned int total = 0;
275 
276  TALLOC_CTX *our_ctx = NULL;
277  char const *attr;
278  char attr_buff[1024 + 1]; /* X.501 says we need to support at least 1024 chars for attr names */
279 
280  while ((map = map_list_next(maps, map))) {
281  if (tmpl_expand(&attr, attr_buff, sizeof(attr_buff), request, map->rhs, NULL, NULL) < 0) {
282  REDEBUG("Expansion of LDAP attribute \"%s\" failed", map->rhs->name);
283  TALLOC_FREE(our_ctx);
284  return -1;
285  }
286 
287  /*
288  * Dynamic value
289  */
290  if (attr == attr_buff) {
291  if (!our_ctx) our_ctx = talloc_new(ctx);
292  expanded->attrs[total++] = talloc_strdup(our_ctx, attr_buff);
293  continue;
294  }
295  expanded->attrs[total++] = attr;
296  }
297 
298  if (generic_attr) expanded->attrs[total++] = generic_attr;
299 
300  expanded->attrs[total] = NULL;
301  expanded->count = total;
302  expanded->maps = maps;
303 
304  return 0;
305 }
306 
307 
308 /** Convert attribute map into valuepairs
309  *
310  * Use the attribute map built earlier to convert LDAP values into valuepairs and insert them into whichever
311  * list they need to go into.
312  *
313  * This is *NOT* atomic, but there's no condition for which we should error out...
314  *
315  * @param[in] request Current request.
316  * @param[in] valuepair_attr Treat attribute with this name as holding complete AVP definitions.
317  * @param[in] expanded attributes (rhs of map).
318  * @param[in] entry to retrieve attributes from.
319  * @return
320  * - Number of maps successfully applied.
321  * - -1 on failure.
322  */
324  char const *valuepair_attr, fr_ldap_map_exp_t const *expanded, LDAPMessage *entry)
325 {
326  map_t const *map = NULL;
327  unsigned int total = 0;
328  int applied = 0; /* How many maps have been applied to the current request */
329 
330  fr_ldap_result_t result;
331  char const *name;
332  LDAP *handle = fr_ldap_handle_thread_local();
333 
334  while ((map = map_list_next(expanded->maps, map))) {
335  int ret;
336 
337  name = expanded->attrs[total++];
338 
339  /*
340  * Binary safe
341  */
342  result.values = ldap_get_values_len(handle, entry, name);
343  if (!result.values) {
344  RDEBUG3("Attribute \"%s\" not found in LDAP object", name);
345 
346  goto next;
347  }
348 
349  /*
350  * Find out how many values there are for the
351  * attribute and extract all of them.
352  */
353  result.count = ldap_count_values_len(result.values);
354 
355  /*
356  * If something bad happened, just skip, this is probably
357  * a case of the dst being incorrect for the current
358  * request context
359  */
360  ret = map_to_request(request, map, fr_ldap_map_getvalue, &result);
361  if (ret == -1) return -1; /* Fail */
362 
363  /*
364  * How many maps we've processed
365  */
366  applied++;
367 
368  next:
369  ldap_value_free_len(result.values);
370  }
371 
372 
373  /*
374  * Retrieve any valuepair attributes from the result, these are generic values specifying
375  * a radius list, operator and value.
376  */
377  if (valuepair_attr) {
378  struct berval **values;
379  int count, i;
380 
381  values = ldap_get_values_len(handle, entry, valuepair_attr);
382  count = ldap_count_values_len(values);
383 
384  for (i = 0; i < count; i++) {
385  map_t *attr;
386  char *value;
387 
388  tmpl_rules_t const parse_rules = {
389  .attr = {
390  .dict_def = request->dict,
391  .list_def = request_attr_request,
392  .prefix = TMPL_ATTR_REF_PREFIX_AUTO
393  },
394  .xlat = {
395  .runtime_el = unlang_interpret_event_list(request),
396  },
397  .at_runtime = true,
398  };
399 
400  value = fr_ldap_berval_to_string(request, values[i]);
401  RDEBUG3("Parsing attribute string '%s'", value);
402  if (map_afrom_attr_str(request, &attr, value,
403  &parse_rules, &parse_rules) < 0) {
404  RPWDEBUG("Failed parsing '%s' value \"%s\" as valuepair, skipping...",
405  valuepair_attr, value);
407  continue;
408  }
409  if (map_to_request(request, attr, map_to_vp, NULL) < 0) {
410  RWDEBUG("Failed adding \"%s\" to request, skipping...", value);
411  } else {
412  applied++;
413  }
414  talloc_free(attr);
416  }
417  ldap_value_free_len(values);
418  }
419 
420  return applied;
421 }
#define USES_APPLE_DEPRECATED_API
Definition: build.h:468
#define RCSID(id)
Definition: build.h:481
#define UNUSED
Definition: build.h:313
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:289
next
Definition: dcursor.h:178
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
Test enumeration values.
Definition: dict_test.h:92
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition: interpret.c:1764
struct berval ** values
libldap struct containing bv_val (char *) and length bv_len.
Definition: base.h:361
int count
Index on next free element.
Definition: base.h:375
map_list_t const * maps
Head of list of maps we expanded the RHS of.
Definition: base.h:371
int count
Number of values.
Definition: base.h:363
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition: util.c:390
char const * attrs[LDAP_MAX_ATTRMAP+LDAP_MAP_RESERVED+1]
Reserve some space for access attributes.
Definition: base.h:372
Result of expanding the RHS of a set of maps.
Definition: base.h:370
Contains a collection of values.
Definition: base.h:360
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition: base.c:1105
USES_APPLE_DEPRECATED_API int fr_ldap_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
Callback for map_to_request.
Definition: map.c:39
int fr_ldap_map_verify(map_t *map, UNUSED void *instance)
Definition: map.c:200
int fr_ldap_map_expand(TALLOC_CTX *ctx, fr_ldap_map_exp_t *expanded, request_t *request, map_list_t const *maps, char const *generic_attr)
Expand values in an attribute map where needed.
Definition: map.c:271
int fr_ldap_map_do(request_t *request, char const *valuepair_attr, fr_ldap_map_exp_t const *expanded, LDAPMessage *entry)
Convert attribute map into valuepairs.
Definition: map.c:323
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RPWDEBUG(fmt,...)
Definition: log.h:366
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition: map.c:1487
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition: map.c:1781
int map_afrom_attr_str(TALLOC_CTX *ctx, map_t **out, char const *vp_str, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules)
Convert a value pair string to valuepair map.
Definition: map.c:1319
talloc_free(reap)
@ TMPL_ATTR_REF_PREFIX_AUTO
Attribute refs may have a '&' prefix.
Definition: merged_model.c:231
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:283
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:1345
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
Definition: pair.c:2589
#define REDEBUG(fmt,...)
Definition: radclient.h:52
fr_dict_attr_t const * request_attr_request
Definition: request.c:45
static char const * name
int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b)
Compare a list of request qualifiers.
static char const * tmpl_list_name(fr_dict_attr_t const *list, char const *def)
Return the name of a tmpl list or def if list not provided.
Definition: tmpl.h:926
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:812
static fr_slen_t tmpl_request_ref_list_aprint(TALLOC_CTX *ctx, char **out, FR_DLIST_HEAD(tmpl_request_list) const *rql) 1(tmpl_request_ref_list_print
@ TMPL_TYPE_ATTR_UNRESOLVED
An attribute reference that we couldn't resolve but looked valid.
Definition: tmpl.h:189
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition: tmpl.h:146
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition: tmpl.h:154
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition: tmpl.h:183
@ TMPL_TYPE_XLAT_UNRESOLVED
A xlat expansion with unresolved xlat functions or attribute references.
Definition: tmpl.h:197
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition: tmpl.h:345
static bool tmpl_is_list(tmpl_t const *vpt)
Definition: tmpl.h:931
#define tmpl_is_data_unresolved(vpt)
Definition: tmpl.h:222
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition: tmpl.h:344
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition: tmpl.h:880
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition: tmpl.h:915
#define tmpl_expand(_out, _buff, _buff_len, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, using existing storage to hold variably sized types.
Definition: tmpl.h:1060
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition: tmpl.h:333
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition: tmpl.h:645
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
return count
Definition: module.c:163
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
Value pair map.
Definition: map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition: map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:79
CONF_ITEM * ci
Config item that the map was created from.
Definition: map.h:85
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
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:772
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition: talloc.c:564
fr_table_num_ordered_t const fr_tokens_table[]
Definition: token.c:33
@ T_OP_SUB_EQ
Definition: token.h:70
@ T_OP_EQ
Definition: token.h:83
@ T_OP_SET
Definition: token.h:84
@ T_OP_ADD_EQ
Definition: token.h:69
static fr_slen_t head
Definition: xlat.h:406
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
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:286
static size_t char ** out
Definition: value.h:997