The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_csv.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: 853bde97c05416d2b4df74c6f09b3a019a81517e $
19 * @file rlm_csv.c
20 * @brief Read and map CSV files
21 *
22 * @copyright 2019 The FreeRADIUS server project
23 * @copyright 2019 Alan DeKok (aland@freeradius.org)
24 */
25RCSID("$Id: 853bde97c05416d2b4df74c6f09b3a019a81517e $")
26
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/server/module_rlm.h>
29#include <freeradius-devel/util/htrie.h>
30#include <freeradius-devel/util/debug.h>
31
32#include <freeradius-devel/server/map_proc.h>
33
34static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request,
35 fr_value_box_list_t *key, map_list_t const *maps);
36
37/*
38 * Define a structure for our module configuration.
39 *
40 * These variables do not need to be in a structure, but it's
41 * a lot cleaner to do so, and a pointer to the structure can
42 * be used as the instance handle.
43 */
44typedef struct {
45 char const *filename;
46 char const *delimiter;
47 char const *fields;
48 char const *index_field_name;
49
50 bool header;
53
57
58 char const **field_names;
59 int *field_offsets; /* field X from the file maps to array entry Y here */
63
66
67 map_list_t map; //!< if there is an "update" section in the configuration.
68} rlm_csv_t;
69
77
78/*
79 * A mapping of configuration file names to internal variables.
80 */
81static const conf_parser_t module_config[] = {
83 { FR_CONF_OFFSET_FLAGS("delimiter", CONF_FLAG_NOT_EMPTY, rlm_csv_t, delimiter), .dflt = "," },
84 { FR_CONF_OFFSET("fields", rlm_csv_t, fields) },
85 { FR_CONF_OFFSET("header", rlm_csv_t, header) },
86 { FR_CONF_OFFSET("allow_multiple_keys", rlm_csv_t, allow_multiple_keys) },
87 { FR_CONF_OFFSET_FLAGS("index_field", CONF_FLAG_REQUIRED | CONF_FLAG_NOT_EMPTY, rlm_csv_t, index_field_name) },
88 { FR_CONF_OFFSET("key", rlm_csv_t, key) },
90};
91
92/*
93 * Allow for quotation marks.
94 */
95static bool buf2entry(rlm_csv_t *inst, char *buf, char **out)
96{
97 char *p, *q;
98
99 if (*buf != '"') {
100 *out = strchr(buf + 1, *inst->delimiter);
101
102 if (!*out) { /* mash CR / LF */
103 for (p = buf; *p != '\0'; p++) {
104 if (*p < ' ') {
105 *p = '\0';
106 break;
107 }
108 }
109 }
110
111 return true;
112 }
113
114 p = buf + 1;
115 q = buf;
116
117 while (*p) {
118 if (*p < ' ') {
119 *q = '\0';
120 *out = NULL;
121 return true;
122 }
123
124 /*
125 * Double quotes to single quotes.
126 */
127 if ((*p == '"') && (p[1] == '"')) {
128 *(q++) = '"';
129 p += 2;
130 continue;
131 }
132
133 /*
134 * Double quotes and EOL mean we're done.
135 */
136 if ((*p == '"') && (p[1] < ' ')) {
137 *(q++) = '\0';
138
139 *out = NULL;
140 return true;
141 }
142
143 /*
144 * Double quotes and delimiter: point to the delimiter.
145 */
146 if ((*p == '"') && (p[1] == *inst->delimiter)) {
147 *(q++) = '\0';
148
149 *out = p + 1;
150 return true;
151 }
152
153 /*
154 * Everything else gets copied over verbatim
155 */
156 *(q++) = *(p++);
157 *q = '\0';
158 }
159
160 return false;
161}
162
163
164static int8_t csv_cmp(void const *one, void const *two)
165{
166 rlm_csv_entry_t const *a = (rlm_csv_entry_t const *) one; /* may not be talloc'd! */
167 rlm_csv_entry_t const *b = (rlm_csv_entry_t const *) two; /* may not be talloc'd! */
168
169 return fr_value_box_cmp(a->key, b->key);
170}
171
172static uint32_t csv_hash(void const *data)
173{
174 rlm_csv_entry_t const *a = (rlm_csv_entry_t const *) data; /* may not be talloc'd! */
175
176 return fr_value_box_hash(a->key);
177}
178
179static int csv_to_key(uint8_t **out, size_t *outlen, void const *data)
180{
181 rlm_csv_entry_t const *a = (rlm_csv_entry_t const *) data; /* may not be talloc'd! */
182
183 return fr_value_box_to_key(out, outlen, a->key);
184
185}
186
187
189{
190 rlm_csv_entry_t *old;
191
192 fr_assert(e != NULL);
193
194 old = fr_htrie_find(inst->trie, e);
195 if (old) {
196 if (!inst->allow_multiple_keys && !inst->multiple_index_fields) {
197 cf_log_err(conf, "%s[%d]: Multiple entries are disallowed", inst->filename, lineno);
198 goto fail;
199 }
200
201 /*
202 * Save this entry at the tail of the matching
203 * entries.
204 */
205 while (old->next) old = old->next;
206 old->next = e;
207 return true;
208 }
209
210 if (!fr_htrie_insert(inst->trie, e)) {
211 cf_log_err(conf, "Failed inserting entry for file %s line %d: %s",
212 inst->filename, lineno, fr_strerror());
213fail:
214 talloc_free(e);
215 return false;
216 }
217
218 return true;
219}
220
221
222static bool duplicate_entry(CONF_SECTION *conf, rlm_csv_t *inst, rlm_csv_entry_t *old, char *p, int lineno)
223{
224 int i;
225 fr_type_t type = inst->key_data_type;
227
228 MEM(e = (rlm_csv_entry_t *)talloc_zero_array(inst, uint8_t,
229 sizeof(*e) + (inst->used_fields * sizeof(e->data[0]))));
230 talloc_set_type(e, rlm_csv_entry_t);
231
233 if (!e->key) goto fail;
234
235 if (fr_value_box_from_str(e->key, e->key, type, NULL, p, strlen(p), NULL, false) < 0) {
236 cf_log_err(conf, "Failed parsing key field in file %s line %d - %s", inst->filename, lineno,
237 fr_strerror());
238 fail:
239 talloc_free(e);
240 return false;
241 }
242
243 /*
244 * Copy the other fields;
245 */
246 for (i = 0; i < inst->used_fields; i++) {
247 if (old->data[i]) e->data[i] = old->data[i]; /* no need to dup it, it's never freed... */
248 }
249
250 return insert_entry(conf, inst, e, lineno);
251}
252
253/*
254 * Convert a buffer to a CSV entry
255 */
256static bool file2csv(CONF_SECTION *conf, rlm_csv_t *inst, int lineno, char *buffer)
257{
259 int i;
260 char *p, *q;
261
262 MEM(e = (rlm_csv_entry_t *)talloc_zero_array(inst, uint8_t,
263 sizeof(*e) + (inst->used_fields * sizeof(e->data[0]))));
264 talloc_set_type(e, rlm_csv_entry_t);
265
266 for (p = buffer, i = 0; p != NULL; p = q, i++) {
267 if (!buf2entry(inst, p, &q)) {
268 cf_log_err(conf, "Malformed entry in file %s line %d", inst->filename, lineno);
269 return false;
270 }
271
272 if (q) *(q++) = '\0';
273
274 if (i >= inst->num_fields) {
275 cf_log_err(conf, "Too many fields at file %s line %d", inst->filename, lineno);
276 return false;
277 }
278
279 /*
280 * This is the key field.
281 */
282 if (i == inst->index_field) {
283 fr_type_t type = inst->key_data_type;
284
285 /*
286 * Check for /etc/group style keys.
287 */
288 if (inst->multiple_index_fields) {
289 char *l;
290
291 /*
292 * Silently omit empty entries.
293 */
294 if (!*p) {
295 talloc_free(e);
296 return true;
297 }
298
299 /*
300 * Check & smash ','. duplicate
301 * 'e', and insert it into the
302 * hash table / trie.
303 */
304 l = strchr(p, ',');
305 while (l) {
306 *l = '\0';
307
308 if (!duplicate_entry(conf, inst, e, p, lineno)) goto fail;
309
310 p = l + 1;
311 l = strchr(p, ',');
312 }
313 }
314
315 /*
316 * Set the last entry to use 'e'
317 */
319 if (!e->key) goto fail;
320
321 if (fr_value_box_from_str(e->key, e->key, type, NULL,
322 p, strlen(p), NULL, false) < 0) {
323 cf_log_err(conf, "Failed parsing key field in file %s line %d - %s", inst->filename, lineno,
324 fr_strerror());
325 fail:
326 talloc_free(e);
327 return false;
328 }
329 continue;
330 }
331
332 /*
333 * This field is unused. Ignore it.
334 */
335 if (inst->field_offsets[i] < 0) continue;
336
337 /*
338 * Try to parse fields as data types if the data type is defined.
339 */
340 if (inst->field_types[i] != FR_TYPE_NULL) {
341 fr_value_box_t box;
342 fr_type_t type = inst->field_types[i];
343
344 if (fr_value_box_from_str(e, &box, type, NULL,
345 p, strlen(p), NULL, false) < 0) {
346 cf_log_err(conf, "Failed parsing field '%s' in file %s line %d - %s", inst->field_names[i],
347 inst->filename, lineno, fr_strerror());
348 goto fail;
349 }
350
351 fr_value_box_clear(&box);
352 }
353
354 MEM(e->data[inst->field_offsets[i]] = talloc_typed_strdup(e, p));
355 }
356
357 if (i < inst->num_fields) {
358 cf_log_err(conf, "Too few fields in file %s at line %d (%d < %d)", inst->filename, lineno, i, inst->num_fields);
359 goto fail;
360 }
361
362 return insert_entry(conf, inst, e, lineno);
363}
364
365
366static int fieldname2offset(rlm_csv_t const *inst, char const *field_name, int *array_offset)
367{
368 int i;
369
370 /*
371 * Find out which field the RHS maps to.
372 *
373 * For maps of less than 32 entries or so, an
374 * array is faster than more complex solutions.
375 */
376 for (i = 0; i < inst->num_fields; i++) {
377 if (strcmp(field_name, inst->field_names[i]) == 0) {
378 if (array_offset) *array_offset = i;
379 return inst->field_offsets[i];
380 }
381 }
382
383 return -1;
384}
385
386#define CSV_MAX_ATTRMAP (128)
387
388/*
389 * Verify one map entry.
390 */
391static int csv_map_verify(map_t *map, void *instance)
392{
393 rlm_csv_t *inst = talloc_get_type_abort(instance, rlm_csv_t);
394 int offset;
396
397 /*
398 * Destinations where we can put the fr_pair_ts we
399 * create using CSV values.
400 */
401 switch (map->lhs->type) {
402 case TMPL_TYPE_ATTR:
403 type = tmpl_attr_tail_da(map->lhs)->type;
404 break;
405
407 cf_log_err(map->ci, "Unknown attribute %s", tmpl_attr_tail_unresolved(map->lhs));
408 return -1;
409
410 default:
411 cf_log_err(map->ci, "Left hand side of map must be an attribute or list, not a %s",
412 tmpl_type_to_str(map->lhs->type));
413 return -1;
414 }
415
416 /*
417 * Sources we can use to get the name of the attribute
418 * we're retrieving from LDAP.
419 */
420 switch (map->rhs->type) {
422 offset = -1;
423 if (fieldname2offset(inst, map->rhs->name, &offset) < 0) {
424 cf_log_err(map->ci, "Unknown field '%s'", map->rhs->name);
425 return -1;
426 }
427
428 if (fr_type_is_null(type) || (offset < 0)) break;
429
430 /*
431 * Try to set the data type of the field. But if
432 * they map the same field to two different data
433 * types, that's an error.
434 */
435 if (fr_type_is_null(inst->field_types[offset])) {
436 inst->field_types[offset] = type;
437 break;
438 }
439
440 if (inst->field_types[offset] != type) {
441 cf_log_err(map->ci, "Field '%s' maps to two different data types, making it impossible to parse it.", map->rhs->name);
442 return -1;
443 }
444
445 break;
446
448 cf_log_err(map->ci, "Unknown attribute %s", tmpl_attr_tail_unresolved(map->rhs));
449 return -1;
450
451 default:
452 cf_log_err(map->ci, "Right hand side of map must be a field name, not a %s",
453 tmpl_type_to_str(map->rhs->type));
454 return -1;
455 }
456
457 /*
458 * Only some operators are allowed.
459 */
460 switch (map->op) {
461 case T_OP_SET:
462 case T_OP_EQ:
463 case T_OP_SUB_EQ:
464 case T_OP_ADD_EQ:
465 case T_OP_PREPEND:
466 case T_OP_LT:
467 case T_OP_GT:
468 case T_OP_LE:
469 case T_OP_GE:
470 break;
471
472 default:
473 cf_log_err(map->ci, "Operator \"%s\" not allowed for CSV mappings",
474 fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
475 return -1;
476 }
477
478 return 0;
479}
480
481/*
482 * Verify the result of the map.
483 */
484static int csv_maps_verify(CONF_SECTION *cs, void const *mod_inst, UNUSED void *proc_inst,
485 tmpl_t const *src, map_list_t const *maps)
486{
487 map_t const *map = NULL;
488
489 if (!src) {
490 cf_log_err(cs, "Missing key expansion");
491
492 return -1;
493 }
494
495 while ((map = map_list_next(maps, map))) {
496 /*
497 * This function doesn't change the map, so it's OK.
498 */
499 if (csv_map_verify(UNCONST(map_t *, map), UNCONST(void *, mod_inst)) < 0) return -1;
500 }
501
502 return 0;
503}
504
505/*
506 * Do any per-module initialization that is separate to each
507 * configured instance of the module. e.g. set up connections
508 * to external databases, read configuration files, set up
509 * dictionary entries, etc.
510 *
511 * If configuration information is given in the config section
512 * that must be referenced in later calls, store a handle to it
513 * in *instance otherwise put a null pointer there.
514 */
515static int mod_bootstrap(module_inst_ctx_t const *mctx)
516{
517 rlm_csv_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_csv_t);
518 CONF_SECTION *conf = mctx->mi->conf;
519 int i;
520 char const *p;
521 char *q;
522 char *fields;
523 fr_htrie_type_t htype;
524
525 if (inst->delimiter[1]) {
526 cf_log_err(conf, "'delimiter' must be one character long");
527 return -1;
528 }
529
530 if (inst->key) {
531 inst->key_data_type = tmpl_expanded_type(inst->key);
532 switch (inst->key_data_type) {
533 case FR_TYPE_LEAF:
534 break;
535
536 case FR_TYPE_NULL:
537 cf_log_err(conf, "Can't determine key data_type. 'key' must be an expression of type "
538 "xlat, attr, or data");
539 return -1;
540
541 default:
542 cf_log_err(conf, "Invalid key data type '%s'",
543 fr_type_to_str(inst->key_data_type));
544 return -1;
545 }
546 } else {
547 inst->key_data_type = FR_TYPE_STRING;
548 }
549
550 /*
551 * IP addresses go into tries. Everything else into binary tries.
552 */
553 htype = fr_htrie_hint(inst->key_data_type);
554 if (htype == FR_HTRIE_INVALID) {
555 cf_log_err(conf, "Invalid data type '%s' used for CSV file.",
556 fr_type_to_str(inst->key_data_type));
557 return -1;
558 }
559
560 inst->trie = fr_htrie_alloc(inst, htype,
564 NULL);
565 if (!inst->trie) {
566 cf_log_err(conf, "Failed creating internal trie: %s", fr_strerror());
567 return -1;
568 }
569
570 if ((*inst->index_field_name == ',') || (*inst->index_field_name == *inst->delimiter)) {
571 cf_log_err(conf, "Field names cannot begin with the '%c' character", *inst->index_field_name);
572 return -1;
573 }
574
575 if (inst->delimiter[1] != '\0') {
576 cf_log_err(conf, "The 'delimiter' field MUST be one character long");
577 return -1;
578 }
579
580 /*
581 * If there is a header in the file, then read that first.
582 */
583 if (inst->header) {
584 FILE *fp;
585 char buffer[8192];
586
587 fp = fopen(inst->filename, "r");
588 if (!fp) {
589 cf_log_err(conf, "Error opening filename %s: %s", inst->filename, fr_syserror(errno));
590 return -1;
591 }
592
593 p = fgets(buffer, sizeof(buffer), fp);
594 if (!p) {
595 error_eof:
596 cf_log_err(conf, "Error reading filename %s: Unexpected EOF", inst->filename);
597 fclose(fp);
598 return -1;
599 }
600
601 q = strchr(buffer, '\n');
602 if (!q) goto error_eof;
603
604 *q = '\0';
605
606 /*
607 * Over-write whatever is in the config with the
608 * header from the file.
609 */
610 inst->fields = talloc_strdup(inst, buffer);
611 fclose(fp);
612 }
613
614 /*
615 * Parse the field names AFTER opening the file. Because
616 * the field names might be taken from the header.
617 */
618 for (p = inst->fields; p != NULL; p = strchr(p + 1, *inst->delimiter)) {
619 inst->num_fields++;
620 }
621
622 if (inst->num_fields < 2) {
623 cf_log_err(conf, "The CSV file MUST have at least a key field and data field");
624 return -1;
625 }
626
627 MEM(inst->field_names = talloc_zero_array(inst, const char *, inst->num_fields));
628 MEM(inst->field_offsets = talloc_array(inst, int, inst->num_fields));
629 MEM(inst->field_types = talloc_array(inst, fr_type_t, inst->num_fields));
630
631 for (i = 0; i < inst->num_fields; i++) {
632 inst->field_offsets[i] = -1; /* unused */
633 inst->field_types[i] = FR_TYPE_NULL;
634 }
635
636 /*
637 * Get a writable copy of the fields definition
638 */
639 MEM(fields = talloc_typed_strdup(inst, inst->fields));
640
641 /*
642 * Mark up the field names. Note that they can be empty,
643 * in which case they don't map to anything.
644 */
645 inst->index_field = -1;
646
647 /*
648 * Parse the field names
649 */
650 i = 0;
651 p = q = fields;
652 while (*q) {
653 bool last_field;
654
655 /*
656 * Skip the field name
657 */
658 while (*q && (*q != *inst->delimiter)) {
659 if ((*q == '\'') || (*q == '"')) {
660 cf_log_err(conf, "Field %d name cannot have quotation marks.",
661 i + 1);
662 return -1;
663 }
664
665 if (*q < ' ') {
666 *q = '\0';
667 break;
668 }
669
670 if (isspace((uint8_t) *q)) {
671 cf_log_err(conf, "Field %d name cannot have spaces.",
672 i + 1);
673 return -1;
674 }
675
676 q++;
677 }
678
679 /*
680 * Check for the last field.
681 */
682 if (!*q) {
683 last_field = true;
684 } else {
685 *q = '\0';
686 last_field = false;
687 }
688
689 /*
690 * Track which field is the key, and which fields
691 * map to which entries.
692 *
693 * Some fields are unused, so there isn't a 1-1
694 * mapping between CSV file fields, and fields
695 * in the map.
696 */
697 if (last_field && (*p == ',') && (strcmp(p + 1, inst->index_field_name) == 0)) {
698 inst->index_field = i;
699 p++;
700
701 inst->multiple_index_fields = true;
702
703 } else if (strcmp(p, inst->index_field_name) == 0) {
704 inst->index_field = i;
705
706 } else if ((*p == ',') || (*p == *inst->delimiter)) {
707 cf_log_err(conf, "Field names MUST NOT begin with the '%c' character", *p);
708 return -1;
709
710 } else {
711 inst->field_offsets[i] = inst->used_fields++;
712 }
713
714 /*
715 * Save the field names, even when the field names are empty.
716 */
717 inst->field_names[i] = p;
718
719 if (last_field) break;
720
721 q++;
722 i++;
723 p = q;
724 }
725
726 if (inst->index_field < 0) {
727 cf_log_err(conf, "index_field '%s' does not appear in the list of field names",
728 inst->index_field_name);
729 return -1;
730 }
731
732 /*
733 * Set the data type of the index field.
734 */
735 inst->field_types[inst->index_field] = inst->key_data_type;
736
737 /*
738 * And register the `map csv <key> { ... }` function.
739 */
741
742 return 0;
743}
744
745/** Instantiate the module
746 *
747 * Creates a new instance of the module reading parameters from a configuration section.
748 *
749 * @param[in] mctx configuration data.
750 * @return
751 * - 0 on success.
752 * - < 0 on failure.
753 */
754static int mod_instantiate(module_inst_ctx_t const *mctx)
755{
756 rlm_csv_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_csv_t);
757 CONF_SECTION *conf = mctx->mi->conf;
758 CONF_SECTION *cs;
759 int lineno;
760 FILE *fp;
761 tmpl_rules_t parse_rules = {
762 .attr = {
763 .allow_foreign = true /* Because we don't know where we'll be called */
764 }
765 };
766 char buffer[8192];
767
768 map_list_init(&inst->map);
769 /*
770 * "update" without "key" is invalid, as we can't run the
771 * module.
772 *
773 * "key" without "update" means we just ignore the key.
774 */
775 cs = cf_section_find(conf, "update", CF_IDENT_ANY);
776 if (cs) {
777 if (!inst->key) {
778 cf_log_err(conf, "There is no 'key' defined for the 'update' section");
779 return -1;
780 }
781
782 /*
783 * Parse the "update" section. This parsing includes
784 * setting inst->field_types[], base on the attribute
785 * mappings.
786 */
787 if (map_afrom_cs(inst, &inst->map, cs,
788 &parse_rules, &parse_rules, csv_map_verify, inst,
789 CSV_MAX_ATTRMAP) < 0) {
790 return -1;
791 }
792 } else if (inst->key) {
793 cf_log_warn(conf, "Ignoring 'key', as no 'update' section has been defined.");
794 }
795
796 /*
797 * Re-open the file and read it all.
798 */
799 fp = fopen(inst->filename, "r");
800 if (!fp) {
801 cf_log_err(conf, "Error opening filename %s: %s", inst->filename, fr_syserror(errno));
802 return -1;
803 }
804 lineno = 1;
805
806 /*
807 * If there is a header in the file, then read that first.
808 * This time we just ignore it.
809 */
810 if (inst->header) {
811 char *p = fgets(buffer, sizeof(buffer), fp);
812 if (!p) {
813 cf_log_err(conf, "Error reading filename %s: Unexpected EOF", inst->filename);
814 fclose(fp);
815 return -1;
816 }
817 lineno++;
818 }
819
820 /*
821 * Read the rest of the file.
822 */
823 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
824 if (!file2csv(conf, inst, lineno, buffer)) {
825 fclose(fp);
826 return -1;
827 }
828
829 lineno++;
830 }
831 fclose(fp);
832
833 return 0;
834}
835
836
837/*
838 * Convert field X to a VP.
839 */
840static int csv_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
841{
842 char const *str = uctx;
843 fr_pair_t *vp;
844 fr_dict_attr_t const *da;
845
846 fr_assert(ctx != NULL);
847
848 if (tmpl_is_attr(map->lhs)) {
849 da = tmpl_attr_tail_da(map->lhs);
850
851 } else {
852 char *attr;
853
854 if (tmpl_aexpand(ctx, &attr, request, map->lhs, NULL, NULL) <= 0) {
855 RWDEBUG("Failed expanding string");
856 return -1;
857 }
858
859
860 da = fr_dict_attr_by_name(NULL, fr_dict_root(request->dict), attr);
861 if (!da) {
862 RWDEBUG("No such attribute '%s'", attr);
863 talloc_free(attr);
864 return -1;
865 }
866
867 talloc_free(attr);
868 }
869
870 vp = fr_pair_afrom_da(ctx, da);
871 fr_assert(vp);
872
873 if (fr_pair_value_from_str(vp, str, talloc_array_length(str) - 1, NULL, true) < 0) {
874 RPWDEBUG("Failed parsing value \"%pV\" for attribute %s", fr_box_strvalue_buffer(str),
875 tmpl_attr_tail_da(map->lhs)->name);
877
878 return -1;
879 }
880
882
883 return 0;
884}
885
886
887/** Perform a search and map the result of the search to server attributes
888 *
889 * @param[in] inst #rlm_csv_t.
890 * @param[in,out] request The current request.
891 * @param[in] key key to look for
892 * @param[in] maps Head of the map list.
893 * @return
894 * - #RLM_MODULE_NOOP no rows were returned.
895 * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
896 * - #RLM_MODULE_FAIL if an error occurred.
897 */
899 fr_value_box_t const *key, map_list_t const *maps)
900{
903 map_t const *map = NULL;
904
905 e = fr_htrie_find(inst->trie, &(rlm_csv_entry_t) { .key = UNCONST(fr_value_box_t *, key) } );
906 if (!e) {
907 rcode = RLM_MODULE_NOOP;
908 goto finish;
909 }
910
911redo:
912 RINDENT();
913 while ((map = map_list_next(maps, map))) {
914 int field;
915 char *field_name;
916
917 /*
918 * Avoid memory allocations if possible.
919 */
920 if (!tmpl_is_data_unresolved(map->rhs)) {
921 if (tmpl_aexpand(request, &field_name, request, map->rhs, NULL, NULL) < 0) {
922 REXDENT();
923 REDEBUG("Failed expanding RHS at %s", map->lhs->name);
924 rcode = RLM_MODULE_FAIL;
925 goto finish;
926 }
927 } else {
928 field_name = UNCONST(char *, map->rhs->name);
929 }
930
931 field = fieldname2offset(inst, field_name, NULL);
932
933 if (field_name != map->rhs->name) talloc_free(field_name);
934
935 if (field < 0) {
936 REXDENT();
937 REDEBUG("No such field name %s", map->rhs->name);
938 rcode = RLM_MODULE_FAIL;
939 goto finish;
940 }
941
942 /*
943 * Pass the raw data to the callback, which will
944 * create the VP and add it to the map.
945 */
946 if (map_to_request(request, map, csv_map_getvalue, e->data[field]) < 0) {
947 REXDENT();
948 rcode = RLM_MODULE_FAIL;
949 goto finish;
950 }
951 }
952
953 REXDENT();
954
955 if (e->next) {
956 e = e->next;
957 goto redo;
958 }
959
960finish:
961 return rcode;
962}
963
964
965/** Perform a search and map the result of the search to server attributes
966 *
967* @param[out] p_result Result of applying map:
968 * - #RLM_MODULE_NOOP no rows were returned.
969 * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
970 * - #RLM_MODULE_FAIL if an error occurred.
971 * @param[in] mod_inst #rlm_csv_t.
972 * @param[in] proc_inst mapping map entries to field numbers.
973 * @param[in,out] request The current request.
974 * @param[in] key key to look for
975 * @param[in] maps Head of the map list.
976 * @return UNLANG_ACTION_CALCULATE_RESULT
977 */
978static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request,
979 fr_value_box_list_t *key, map_list_t const *maps)
980{
982 fr_value_box_t *key_head = fr_value_box_list_head(key);
983
984 if (!key_head) {
985 REDEBUG("CSV key cannot be (null)");
987 }
988
989 if ((inst->key_data_type == FR_TYPE_OCTETS) || (inst->key_data_type == FR_TYPE_STRING)) {
991 key_head, key, inst->key_data_type,
993 SIZE_MAX) < 0) {
994 REDEBUG("Failed parsing key");
996 }
997 }
998
999 RETURN_MODULE_RCODE(mod_map_apply(inst, request, key_head, maps));
1000}
1001
1002
1003static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1004{
1006 rlm_rcode_t rcode;
1007 ssize_t slen;
1008 fr_value_box_t *key;
1009
1010 if (map_list_empty(&inst->map) || !inst->key) RETURN_MODULE_NOOP;
1011
1012 /*
1013 * Expand the key to whatever it is. For attributes,
1014 * this usually just means copying the value box.
1015 */
1016 slen = tmpl_aexpand_type(request, &key, FR_TYPE_VALUE_BOX, request, inst->key, NULL, NULL);
1017 if (slen < 0) {
1018 DEBUG("Failed expanding key '%s'", inst->key->name);
1020 }
1021
1022 /*
1023 * If the output data was string and we wanted non-string
1024 * data, convert it now.
1025 */
1026 if (key->type != inst->key_data_type) {
1027 fr_value_box_t tmp;
1028
1029 fr_value_box_copy(request, &tmp, key);
1030
1031 slen = fr_value_box_cast(request, key, inst->key_data_type, NULL, &tmp);
1032 fr_value_box_clear(&tmp);
1033 if (slen < 0) {
1034 talloc_free(key);
1035 DEBUG("Failed casting %pV to data type '%s'",
1036 &key, fr_type_to_str(inst->key_data_type));
1038 }
1039 }
1040
1041 RDEBUG2("Processing CVS map with key %pV", key);
1042 RINDENT();
1043 rcode = mod_map_apply(inst, request, key, &inst->map);
1044 REXDENT();
1045
1046 talloc_free(key);
1047 RETURN_MODULE_RCODE(rcode);
1048}
1049
1050extern module_rlm_t rlm_csv;
1052 .common = {
1053 .magic = MODULE_MAGIC_INIT,
1054 .name = "csv",
1056 .inst_size = sizeof(rlm_csv_t),
1058 .bootstrap = mod_bootstrap,
1060 },
1061 .method_group = {
1062 .bindings = (module_method_binding_t[]){
1063 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_process },
1065 }
1066 }
1067};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
static int const char char buffer[256]
Definition acutest.h:576
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:256
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:418
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
Definition cf_parse.h:424
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition cf_parse.h:433
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1028
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:290
#define CF_IDENT_ANY
Definition cf_util.h:78
#define MEM(x)
Definition debug.h:36
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3263
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
uint32_t(* fr_hash_t)(void const *)
Definition hash.h:36
fr_htrie_t * fr_htrie_alloc(TALLOC_CTX *ctx, fr_htrie_type_t type, fr_hash_t hash_data, fr_cmp_t cmp_data, fr_trie_key_t get_key, fr_free_t free_data)
An abstraction over our internal hashes, rb trees, and prefix tries.
Definition htrie.c:92
fr_htrie_type_t
Definition htrie.h:49
@ FR_HTRIE_INVALID
Definition htrie.h:50
static fr_htrie_type_t fr_htrie_hint(fr_type_t type)
Definition htrie.h:149
static bool fr_htrie_insert(fr_htrie_t *ht, void const *data)
Insert data into a htrie.
Definition htrie.h:112
static void * fr_htrie_find(fr_htrie_t *ht, void const *data)
Find data in a htrie.
Definition htrie.h:104
A hash/rb/prefix trie abstraction.
Definition htrie.h:80
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RPWDEBUG(fmt,...)
Definition log.h:366
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map.
Definition map.c:1014
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
talloc_free(reap)
int map_proc_register(TALLOC_CTX *ctx, void const *mod_inst, char const *name, map_proc_func_t evaluate, map_proc_instantiate_t instantiate, size_t inst_size, fr_value_box_safe_for_t literals_safe_for)
Register a map processor.
Definition map_proc.c:131
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VALUE_BOX
A boxed value.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
int8_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:38
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
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
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_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
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
static rs_t * conf
Definition radsniff.c:53
The main red black tree structure.
Definition rb.h:73
#define RETURN_MODULE_NOOP
Definition rcode.h:62
#define RETURN_MODULE_RCODE(_rcode)
Definition rcode.h:64
#define RETURN_MODULE_FAIL
Definition rcode.h:56
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
static int8_t csv_cmp(void const *one, void const *two)
Definition rlm_csv.c:164
static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_csv.c:1003
char * data[]
Definition rlm_csv.c:75
static int csv_maps_verify(CONF_SECTION *cs, void const *mod_inst, UNUSED void *proc_inst, tmpl_t const *src, map_list_t const *maps)
Definition rlm_csv.c:484
char const * index_field_name
Definition rlm_csv.c:48
fr_htrie_t * trie
Definition rlm_csv.c:62
fr_rb_node_t node
Definition rlm_csv.c:72
int index_field
Definition rlm_csv.c:56
fr_value_box_t * key
Definition rlm_csv.c:74
char const ** field_names
Definition rlm_csv.c:58
int used_fields
Definition rlm_csv.c:55
bool allow_multiple_keys
Definition rlm_csv.c:51
tmpl_t * key
Definition rlm_csv.c:64
static int fieldname2offset(rlm_csv_t const *inst, char const *field_name, int *array_offset)
Definition rlm_csv.c:366
static bool buf2entry(rlm_csv_t *inst, char *buf, char **out)
Definition rlm_csv.c:95
static bool file2csv(CONF_SECTION *conf, rlm_csv_t *inst, int lineno, char *buffer)
Definition rlm_csv.c:256
#define CSV_MAX_ATTRMAP
Definition rlm_csv.c:386
static int csv_to_key(uint8_t **out, size_t *outlen, void const *data)
Definition rlm_csv.c:179
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_csv.c:515
static int csv_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
Definition rlm_csv.c:840
fr_type_t * field_types
Definition rlm_csv.c:60
int num_fields
Definition rlm_csv.c:54
static uint32_t csv_hash(void const *data)
Definition rlm_csv.c:172
module_rlm_t rlm_csv
Definition rlm_csv.c:1051
char const * filename
Definition rlm_csv.c:45
static bool insert_entry(CONF_SECTION *conf, rlm_csv_t *inst, rlm_csv_entry_t *e, int lineno)
Definition rlm_csv.c:188
char const * delimiter
Definition rlm_csv.c:46
bool multiple_index_fields
Definition rlm_csv.c:52
map_list_t map
if there is an "update" section in the configuration.
Definition rlm_csv.c:67
static bool duplicate_entry(CONF_SECTION *conf, rlm_csv_t *inst, rlm_csv_entry_t *old, char *p, int lineno)
Definition rlm_csv.c:222
fr_rb_tree_t * tree
Definition rlm_csv.c:61
char const * fields
Definition rlm_csv.c:47
static int csv_map_verify(map_t *map, void *instance)
Definition rlm_csv.c:391
rlm_csv_entry_t * next
Definition rlm_csv.c:73
int * field_offsets
Definition rlm_csv.c:59
static rlm_rcode_t mod_map_apply(rlm_csv_t const *inst, request_t *request, fr_value_box_t const *key, map_list_t const *maps)
Perform a search and map the result of the search to server attributes.
Definition rlm_csv.c:898
static const conf_parser_t module_config[]
Definition rlm_csv.c:81
bool header
Definition rlm_csv.c:50
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
Definition rlm_csv.c:754
fr_type_t key_data_type
Definition rlm_csv.c:65
static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request, fr_value_box_list_t *key, map_list_t const *maps)
Perform a search and map the result of the search to server attributes.
Definition rlm_csv.c:978
Definition rlm_csv.c:71
static int instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1310
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
char const * name
Instance name e.g. user_database.
Definition module.h:335
@ MODULE_TYPE_DYNAMIC_UNSAFE
Instances of this module cannot be created at runtime.
Definition module.h:52
module_flags_t flags
Flags that control how a module starts up and how a module is called.
Definition module.h:227
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:329
void * data
Module's instance data.
Definition module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:151
Named methods exported by a module.
Definition module.h:173
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition tmpl.h:645
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
#define tmpl_aexpand_type(_ctx, _out, _type, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition tmpl.h:1079
@ 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_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:183
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition tmpl.h:880
#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 fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
#define tmpl_aexpand(_ctx, _out, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition tmpl.h:1070
fr_type_t tmpl_expanded_type(tmpl_t const *vpt)
Return the native data type of the expression.
Definition tmpl_eval.c:209
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
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
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:327
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
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 talloc_get_type_abort_const
Definition talloc.h:282
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
@ T_OP_LE
Definition token.h:100
@ T_OP_GE
Definition token.h:98
@ T_OP_GT
Definition token.h:99
@ T_OP_LT
Definition token.h:101
@ T_OP_PREPEND
Definition token.h:85
int(* fr_trie_key_t)(uint8_t **out, size_t *outlen, void const *data)
Definition trie.h:56
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:554
#define fr_type_is_null(_x)
Definition types.h:326
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
#define FR_TYPE_LEAF
Definition types.h:297
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
uint32_t fr_value_box_hash(fr_value_box_t const *vb)
Hash the contents of a value box.
Definition value.c:6129
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3352
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:676
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:3740
int fr_value_box_to_key(uint8_t **out, size_t *outlen, fr_value_box_t const *value)
Get a key from a value box.
Definition value.c:2084
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:3723
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition value.c:5777
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:221
#define fr_box_strvalue_buffer(_val)
Definition value.h:289
static fr_slen_t data
Definition value.h:1265
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:632
static size_t char ** out
Definition value.h:997