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