All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
attrmap.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: d4bb7cdc2965969c32f5cbbd6cdb40800db1fc70 $
19  * @file ldap.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 SARL <info@networkradius.com>
24  * @copyright 2013 The FreeRADIUS Server Project.
25  */
26 
27 #include <freeradius-devel/rad_assert.h>
28 #include "ldap.h"
29 
30 /** Callback for map_to_request
31  *
32  * Performs exactly the same job as map_to_vp, but pulls attribute values from LDAP entries
33  *
34  * @see map_to_vp
35  */
36 int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
37 {
38  rlm_ldap_result_t *self = uctx;
39  VALUE_PAIR *head = NULL, *vp;
40  vp_cursor_t cursor;
41  int i;
42 
43  fr_cursor_init(&cursor, &head);
44 
45  switch (map->lhs->type) {
46  /*
47  * This is a mapping in the form of:
48  * <list>: += <ldap attr>
49  *
50  * Where <ldap attr> is:
51  * <list>:<attr> <op> <value>
52  *
53  * It is to allow for legacy installations which stored
54  * RADIUS control and reply attributes in separate LDAP
55  * attributes.
56  */
57  case TMPL_TYPE_LIST:
58  for (i = 0; i < self->count; i++) {
59  vp_map_t *attr = NULL;
60 
61  RDEBUG3("Parsing valuepair string \"%s\"", self->values[i]->bv_val);
62  if (map_afrom_attr_str(ctx, &attr, self->values[i]->bv_val,
63  map->lhs->tmpl_request, map->lhs->tmpl_list,
65  RWDEBUG("Failed parsing \"%s\" as valuepair (%s), skipping...", fr_strerror(),
66  self->values[i]->bv_val);
67  continue;
68  }
69 
70  if (attr->lhs->tmpl_request != map->lhs->tmpl_request) {
71  RWDEBUG("valuepair \"%s\" has conflicting request qualifier (%s vs %s), skipping...",
72  self->values[i]->bv_val,
73  fr_int2str(request_refs, attr->lhs->tmpl_request, "<INVALID>"),
74  fr_int2str(request_refs, map->lhs->tmpl_request, "<INVALID>"));
75  next_pair:
76  talloc_free(attr);
77  continue;
78  }
79 
80  if ((attr->lhs->tmpl_list != map->lhs->tmpl_list)) {
81  RWDEBUG("valuepair \"%s\" has conflicting list qualifier (%s vs %s), skipping...",
82  self->values[i]->bv_val,
83  fr_int2str(pair_lists, attr->lhs->tmpl_list, "<INVALID>"),
84  fr_int2str(pair_lists, map->lhs->tmpl_list, "<INVALID>"));
85  goto next_pair;
86  }
87 
88  if (map_to_vp(request, &vp, request, attr, NULL) < 0) {
89  RWDEBUG("Failed creating attribute for valuepair \"%s\", skipping...",
90  self->values[i]->bv_val);
91  goto next_pair;
92  }
93 
94  fr_cursor_merge(&cursor, vp);
95  talloc_free(attr);
96 
97  /*
98  * Only process the first value, unless the operator is +=
99  */
100  if (map->op != T_OP_ADD) break;
101  }
102  break;
103 
104  /*
105  * Iterate over all the retrieved values,
106  * don't try and be clever about changing operators
107  * just use whatever was set in the attribute map.
108  */
109  case TMPL_TYPE_ATTR:
110  for (i = 0; i < self->count; i++) {
111  if (!self->values[i]->bv_len) continue;
112 
113  vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
114  rad_assert(vp);
115 
116  if (fr_pair_value_from_str(vp, self->values[i]->bv_val, self->values[i]->bv_len) < 0) {
117  char *escaped;
118 
119  escaped = fr_asprint(vp, self->values[i]->bv_val, self->values[i]->bv_len, '"');
120  RWDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped,
121  map->lhs->tmpl_da->name, fr_strerror());
122 
123  talloc_free(vp); /* also frees escaped */
124  continue;
125  }
126 
127  vp->op = map->op;
128  fr_cursor_insert(&cursor, vp);
129 
130  /*
131  * Only process the first value, unless the operator is +=
132  */
133  if (map->op != T_OP_ADD) break;
134  }
135  break;
136 
137  default:
138  rad_assert(0);
139  }
140 
141  *out = head;
142 
143  return 0;
144 }
145 
146 int rlm_ldap_map_verify(vp_map_t *map, void *instance)
147 {
148  rlm_ldap_t *inst = instance;
149 
150  /*
151  * Destinations where we can put the VALUE_PAIRs we
152  * create using LDAP values.
153  */
154  switch (map->lhs->type) {
155  case TMPL_TYPE_LIST:
156  case TMPL_TYPE_ATTR:
157  break;
158 
160  cf_log_err(map->ci, "Unknown attribute %s", map->lhs->tmpl_unknown_name);
161  return -1;
162 
163  default:
164  cf_log_err(map->ci, "Left hand side of map must be an attribute or list, not a %s",
165  fr_int2str(tmpl_names, map->lhs->type, "<INVALID>"));
166  return -1;
167  }
168 
169  /*
170  * Sources we can use to get the name of the attribute
171  * we're retrieving from LDAP.
172  */
173  switch (map->rhs->type) {
174  case TMPL_TYPE_XLAT:
175  case TMPL_TYPE_ATTR:
176  case TMPL_TYPE_EXEC:
177  case TMPL_TYPE_UNPARSED:
178  break;
179 
181  cf_log_err(map->ci, "Unknown attribute %s", map->rhs->tmpl_unknown_name);
182  return -1;
183 
184  default:
185  cf_log_err(map->ci, "Right hand side of map must be an xlat, attribute, exec, or literal, not a %s",
186  fr_int2str(tmpl_names, map->rhs->type, "<INVALID>"));
187  return -1;
188  }
189 
190  /*
191  * Only =, :=, += and -= operators are supported for LDAP mappings.
192  */
193  switch (map->op) {
194  case T_OP_SET:
195  case T_OP_EQ:
196  case T_OP_SUB:
197  case T_OP_ADD:
198  break;
199 
200  default:
201  cf_log_err(map->ci, "Operator \"%s\" not allowed for LDAP mappings",
202  fr_int2str(fr_tokens_table, map->op, "<INVALID>"));
203  return -1;
204  }
205 
206  /*
207  * Be smart about whether we warn the user about missing passwords.
208  * If there are no password attributes in the mapping, then the user's either an idiot
209  * and has no idea what they're doing, or they're authenticating the user using a different
210  * method.
211  */
212  if (!inst->expect_password && (map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) {
213  switch (map->lhs->tmpl_da->attr) {
214  case PW_CLEARTEXT_PASSWORD:
215  case PW_NT_PASSWORD:
216  case PW_USER_PASSWORD:
217  case PW_PASSWORD_WITH_HEADER:
218  case PW_CRYPT_PASSWORD:
219  /*
220  * Because you just know someone is going to map NT-Password to the
221  * request list, and then complain it's not working...
222  */
223  if (map->lhs->tmpl_list != PAIR_LIST_CONTROL) {
224  LDAP_DBGW("Mapping LDAP (%s) attribute to \"known good\" password attribute "
225  "(%s) in %s list. This is probably *NOT* the correct list, "
226  "you should prepend \"control:\" to password attribute "
227  "(control:%s)",
228  map->rhs->name, map->lhs->tmpl_da->name,
229  fr_int2str(pair_lists, map->lhs->tmpl_list, "<invalid>"),
230  map->lhs->tmpl_da->name);
231  }
232 
233  inst->expect_password = true;
234  default:
235  break;
236  }
237  }
238 
239  return 0;
240 }
241 
242 /** Expand values in an attribute map where needed
243  *
244  * @param[out] expanded array of attributes. Need not be initialised (we'll initialise).
245  * @param[in] request The current request.
246  * @param[in] maps to expand.
247  * @return
248  * - 0 on success.
249  * - -1 on failure.
250  */
251 int rlm_ldap_map_expand(rlm_ldap_map_exp_t *expanded, REQUEST *request, vp_map_t const *maps)
252 {
253  vp_map_t const *map;
254  unsigned int total = 0;
255 
256  TALLOC_CTX *ctx = NULL;
257  char const *attr;
258  char attr_buff[1024 + 1]; /* X.501 says we need to support at least 1024 chars for attr names */
259 
260  for (map = maps; map != NULL; map = map->next) {
261  if (tmpl_expand(&attr, attr_buff, sizeof(attr_buff), request, map->rhs, NULL, NULL) < 0) {
262  RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->rhs->name);
263  TALLOC_FREE(ctx);
264  return -1;
265  }
266 
267  /*
268  * Dynamic value
269  */
270  if (attr == attr_buff) {
271  if (!ctx) ctx = talloc_new(NULL);
272  expanded->attrs[total++] = talloc_strdup(ctx, attr_buff);
273  continue;
274  }
275  expanded->attrs[total++] = attr;
276  }
277  expanded->attrs[total] = NULL;
278  expanded->ctx = ctx; /* Freeing this frees any dynamic values */
279  expanded->count = total;
280  expanded->maps = maps;
281 
282  return 0;
283 }
284 
285 
286 /** Convert attribute map into valuepairs
287  *
288  * Use the attribute map built earlier to convert LDAP values into valuepairs and insert them into whichever
289  * list they need to go into.
290  *
291  * This is *NOT* atomic, but there's no condition for which we should error out...
292  *
293  * @param[in] inst rlm_ldap configuration.
294  * @param[in] request Current request.
295  * @param[in] handle associated with entry.
296  * @param[in] expanded attributes (rhs of map).
297  * @param[in] entry to retrieve attributes from.
298  * @return
299  * - Number of maps successfully applied.
300  * - -1 on failure.
301  */
302 int rlm_ldap_map_do(const rlm_ldap_t *inst, REQUEST *request, LDAP *handle,
303  rlm_ldap_map_exp_t const *expanded, LDAPMessage *entry)
304 {
305  vp_map_t const *map;
306  unsigned int total = 0;
307  int applied = 0; /* How many maps have been applied to the current request */
308 
309  rlm_ldap_result_t result;
310  char const *name;
311 
312  for (map = expanded->maps; map != NULL; map = map->next) {
313  int ret;
314 
315  name = expanded->attrs[total++];
316 
317  /*
318  * Binary safe
319  */
320  result.values = ldap_get_values_len(handle, entry, name);
321  if (!result.values) {
322  RDEBUG3("Attribute \"%s\" not found in LDAP object", name);
323 
324  goto next;
325  }
326 
327  /*
328  * Find out how many values there are for the
329  * attribute and extract all of them.
330  */
331  result.count = ldap_count_values_len(result.values);
332 
333  /*
334  * If something bad happened, just skip, this is probably
335  * a case of the dst being incorrect for the current
336  * request context
337  */
338  ret = map_to_request(request, map, rlm_ldap_map_getvalue, &result);
339  if (ret == -1) return -1; /* Fail */
340 
341  /*
342  * How many maps we've processed
343  */
344  applied++;
345 
346  next:
347  ldap_value_free_len(result.values);
348  }
349 
350 
351  /*
352  * Retrieve any valuepair attributes from the result, these are generic values specifying
353  * a radius list, operator and value.
354  */
355  if (inst->valuepair_attr) {
356  struct berval **values;
357  int count, i;
358 
359  values = ldap_get_values_len(handle, entry, inst->valuepair_attr);
360  count = ldap_count_values_len(values);
361 
362  for (i = 0; i < count; i++) {
363  vp_map_t *attr;
364  char *value;
365 
366  value = rlm_ldap_berval_to_string(request, values[i]);
367  RDEBUG3("Parsing attribute string '%s'", value);
368  if (map_afrom_attr_str(request, &attr, value,
371  RWDEBUG("Failed parsing '%s' value \"%s\" as valuepair (%s), skipping...",
372  fr_strerror(), inst->valuepair_attr, value);
373  talloc_free(value);
374  continue;
375  }
376  if (map_to_request(request, attr, map_to_vp, NULL) < 0) {
377  RWDEBUG("Failed adding \"%s\" to request, skipping...", value);
378  } else {
379  applied++;
380  }
381  talloc_free(attr);
382  talloc_free(value);
383  }
384  ldap_value_free_len(values);
385  }
386 
387  return applied;
388 }
#define LDAP_DBGW(fmt,...)
Definition: ldap.h:423
ssize_t tmpl_expand(char const **out, char *buff, size_t outlen, REQUEST *request, vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
Expand a vp_tmpl_t to a string writing the result to a buffer.
Definition: tmpl.c:1479
int int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert vp_map_t to VALUE_PAIR (s) and add them to a REQUEST.
Definition: map.c:1019
char const * name
Raw string used to create the template.
Definition: tmpl.h:190
vp_tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:47
Dictionary attribute.
Definition: tmpl.h:133
static char const * name
vp_tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:48
Unparsed literal string.
Definition: tmpl.h:131
const FR_NAME_NUMBER fr_tokens_table[]
Definition: token.c:30
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
#define inst
Definition: token.h:46
static expr_map_t map[]
Definition: rlm_expr.c:169
vp_map_t const * maps
Head of list of maps we expanded the RHS of.
Definition: ldap.h:376
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
struct vp_map * next
The next valuepair map.
Definition: map.h:55
pair_lists
Definition: tmpl.h:80
Attribute not found in the global dictionary.
Definition: tmpl.h:134
#define rad_assert(expr)
Definition: rad_assert.h:38
char const * valuepair_attr
Generic dynamic mapping attribute, contains a RADIUS attribute and value.
Definition: ldap.h:238
const FR_NAME_NUMBER request_refs[]
Map keywords to request_refs_t values.
Definition: tmpl.c:74
Result of expanding the RHS of a set of maps.
Definition: ldap.h:375
void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *vp)
Merges multiple VALUE_PAIR into the cursor.
Definition: cursor.c:394
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
Definition: cursor.c:321
Definition: token.h:43
Attributes in incoming or internally proxied request.
Definition: tmpl.h:82
int count
Number of values.
Definition: ldap.h:389
Attribute list.
Definition: tmpl.h:135
Definition: token.h:44
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
Contains a collection of values.
Definition: ldap.h:386
The current request.
Definition: tmpl.h:113
Definition: token.h:45
int fr_pair_value_from_str(VALUE_PAIR *vp, char const *value, size_t len)
Convert string value to native attribute value.
Definition: pair.c:1840
tmpl_type_t type
What type of value tmpl refers to.
Definition: tmpl.h:188
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
int count
Index on next free element.
Definition: ldap.h:380
Callout to an external script or program.
Definition: tmpl.h:137
int rlm_ldap_map_expand(rlm_ldap_map_exp_t *expanded, REQUEST *request, vp_map_t const *maps)
Expand values in an attribute map where needed.
Definition: attrmap.c:251
void cf_log_err(CONF_ITEM const *ci, char const *fmt,...) CC_HINT(format(printf
int int map_afrom_attr_str(TALLOC_CTX *ctx, vp_map_t **out, char const *raw, request_refs_t dst_request_def, pair_lists_t dst_list_def, request_refs_t src_request_def, pair_lists_t src_list_def)
Convert a value pair string to valuepair map.
Definition: map.c:487
FR_TOKEN op
The operator that controls insertion of the dst attribute.
Definition: map.h:50
struct berval ** values
libldap struct containing bv_val (char *) and length bv_len.
Definition: ldap.h:387
int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
Callback for map_to_request.
Definition: attrmap.c:36
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
Definition: print.c:390
Attributes to send in the response.
Definition: tmpl.h:84
int rlm_ldap_map_do(const rlm_ldap_t *inst, REQUEST *request, LDAP *handle, rlm_ldap_map_exp_t const *expanded, LDAPMessage *entry)
Convert attribute map into valuepairs.
Definition: attrmap.c:302
char const * attrs[LDAP_MAX_ATTRMAP+LDAP_MAP_RESERVED+1]
Reserve some space for access attributes.
Definition: ldap.h:377
int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) CC_HINT(nonnull(2
TALLOC_CTX * ctx
Context to allocate new attributes in.
Definition: ldap.h:379
VALUE_PAIR * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute.
Definition: pair.c:58
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
Definition: token.c:506
XLAT expansion.
Definition: tmpl.h:132
char * rlm_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition: ldap.c:281
Attributes that change the behaviour of modules.
Definition: tmpl.h:85
#define RWDEBUG(fmt,...)
Definition: log.h:251
int rlm_ldap_map_verify(vp_map_t *map, void *instance)
Definition: attrmap.c:146
LDAP authorization and authentication module headers.
Value pair map.
Definition: map.h:46
bool expect_password
True if the user_map included a mapping between an LDAP attribute and one of our password reference a...
Definition: ldap.h:214
#define RDEBUG(fmt,...)
Definition: log.h:243
const FR_NAME_NUMBER tmpl_names[]
Map tmpl_type_t values to descriptive strings.
Definition: tmpl.c:36
CONF_ITEM * ci
Config item that the map was created from.
Definition: map.h:52
#define RDEBUG3(fmt,...)
Definition: log.h:245