2 * This program 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
5 * (at your option) any later version.
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.
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
20 * @brief Valuepair functions that are radiusd-specific and as such do not
21 * belong in the library.
22 * @file main/valuepair.c
26 * @copyright 2000,2006 The FreeRADIUS server project
27 * @copyright 2000 Alan DeKok <aland@ox.org>
34 #include <freeradius-devel/radiusd.h>
35 #include <freeradius-devel/rad_assert.h>
37 const FR_NAME_NUMBER vpt_types[] = {
38 {"unknown", VPT_TYPE_UNKNOWN },
39 {"literal", VPT_TYPE_LITERAL },
40 {"expanded", VPT_TYPE_XLAT },
41 {"attribute ref", VPT_TYPE_ATTR },
42 {"list", VPT_TYPE_LIST },
43 {"exec", VPT_TYPE_EXEC },
44 {"value-pair-data", VPT_TYPE_DATA }
49 DICT_ATTR const *from;
51 void *instance; /* module instance */
52 RAD_COMPARE_FUNC compare;
55 static struct cmp *cmp;
57 /** Compares check and vp by value.
59 * Does not call any per-attribute comparison function, but does honour
60 * check.operator. Basically does "vp.value check.op check.value".
62 * @param request Current request.
63 * @param check rvalue, and operator.
65 * @return 0 if check and vp are equal, -1 if vp value is less than check value, 1 is vp value is more than check
68 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
73 * Check for =* and !* and return appropriately
75 if (check->op == T_OP_CMP_TRUE) return 0;
76 if (check->op == T_OP_CMP_FALSE) return 1;
79 if (check->op == T_OP_REG_EQ) {
83 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
85 vp_prints_value(value, sizeof(value), vp, -1);
88 * Include substring matches.
90 compare = regcomp(®, check->vp_strvalue, REG_EXTENDED);
93 regerror(compare, ®, buffer, sizeof(buffer));
95 RDEBUG("Invalid regular expression %s: %s", check->vp_strvalue, buffer);
99 memset(&rxmatch, 0, sizeof(rxmatch)); /* regexec does not seem to initialise unused elements */
100 compare = regexec(®, value, REQUEST_MAX_REGEX + 1, rxmatch, 0);
102 rad_regcapture(request, compare, value, rxmatch);
104 ret = (compare == 0) ? 0 : -1;
108 if (check->op == T_OP_REG_NE) {
112 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
114 vp_prints_value(value, sizeof(value), vp, -1);
117 * Include substring matches.
119 compare = regcomp(®, check->vp_strvalue, REG_EXTENDED);
122 regerror(compare, ®, buffer, sizeof(buffer));
124 RDEBUG("Invalid regular expression %s: %s", check->vp_strvalue, buffer);
127 compare = regexec(®, value, REQUEST_MAX_REGEX + 1, rxmatch, 0);
130 ret = (compare != 0) ? 0 : -1;
135 * Attributes must be of the same type.
137 * FIXME: deal with type mismatch properly if one side contain
138 * ABINARY, OCTETS or STRING by converting the other side to
142 if (vp->da->type != check->da->type) return -1;
145 * Tagged attributes are equal if and only if both the
146 * tag AND value match.
148 if (check->da->flags.has_tag && !TAG_EQ(check->tag, vp->tag)) {
149 ret = ((int) vp->tag) - ((int) check->tag);
150 if (ret != 0) goto finish;
154 * Not a regular expression, compare the types.
156 switch(check->da->type) {
157 #ifdef WITH_ASCEND_BINARY
159 * Ascend binary attributes can be treated
160 * as opaque objects, I guess...
162 case PW_TYPE_ABINARY:
165 if (vp->length != check->length) {
166 ret = 1; /* NOT equal */
169 ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
174 ret = strcmp(vp->vp_strvalue,
180 case PW_TYPE_INTEGER:
181 ret = vp->vp_integer - check->vp_integer;
184 case PW_TYPE_INTEGER64:
186 * Don't want integer overflow!
188 if (vp->vp_integer64 < check->vp_integer64) {
190 } else if (vp->vp_integer64 > check->vp_integer64) {
198 if (vp->vp_signed < check->vp_signed) {
200 } else if (vp->vp_signed > check->vp_signed) {
208 ret = vp->vp_date - check->vp_date;
211 case PW_TYPE_IPV4_ADDR:
212 ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
215 case PW_TYPE_IPV6_ADDR:
216 ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
217 sizeof(vp->vp_ipv6addr));
220 case PW_TYPE_IPV6_PREFIX:
221 ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
222 sizeof(vp->vp_ipv6prefix));
226 ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
227 sizeof(vp->vp_ifid));
245 /** Compare check and vp. May call the attribute comparison function.
247 * Unlike radius_compare_vps() this function will call any attribute-specific
248 * comparison functions registered.
250 * @param request Current request.
251 * @param req list pairs.
252 * @param check item to compare.
253 * @param check_pairs list.
254 * @param reply_pairs list.
255 * @return 0 if check and vp are equal, -1 if vp value is less than check value, 1 is vp value is more than check
258 int radius_callback_compare(REQUEST *request, VALUE_PAIR *req,
259 VALUE_PAIR *check, VALUE_PAIR *check_pairs,
260 VALUE_PAIR **reply_pairs)
265 * Check for =* and !* and return appropriately
267 if (check->op == T_OP_CMP_TRUE) return 0;
268 if (check->op == T_OP_CMP_FALSE) return 1;
271 * See if there is a special compare function.
273 * FIXME: use new RB-Tree code.
275 for (c = cmp; c; c = c->next) {
276 if (c->da == check->da) {
277 return (c->compare)(c->instance, request, req, check,
278 check_pairs, reply_pairs);
282 if (!req) return -1; /* doesn't exist, don't compare it */
284 return radius_compare_vps(request, check, req);
288 /** Find a comparison function for two attributes.
290 * @param da to find comparison function for.
291 * @return true if a comparison function was found, else false.
293 int radius_find_compare(DICT_ATTR const *da)
297 for (c = cmp; c; c = c->next) {
307 /** See what attribute we want to compare with.
309 * @param da to find comparison function for.
310 * @param from reference to compare with
311 * @return true if the comparison callback require a matching attribue in the request, else false.
313 static bool otherattr(DICT_ATTR const *da, DICT_ATTR const **from)
317 for (c = cmp; c; c = c->next) {
320 return c->first_only;
328 /** Register a function as compare function.
330 * @param da to register comparison function for.
331 * @param from the attribute we want to compare with. Normally this is the same as attribute.
332 * If null call the comparison function on every attributes in the request if first_only is false
333 * @param first_only will decide if we loop over the request attributes or stop on the first one
334 * @param func comparison function
335 * @param instance argument to comparison function
338 int paircompare_register(DICT_ATTR const *da, DICT_ATTR const *from,
339 bool first_only, RAD_COMPARE_FUNC func, void *instance)
343 rad_assert(da != NULL);
345 paircompare_unregister(da, func);
347 c = rad_malloc(sizeof(struct cmp));
352 c->first_only = first_only;
353 c->instance = instance;
360 /** Unregister comparison function for an attribute
362 * @param da dict reference to unregister for.
363 * @param func comparison function to remove.
365 void paircompare_unregister(DICT_ATTR const *da, RAD_COMPARE_FUNC func)
367 struct cmp *c, *last;
370 for (c = cmp; c; c = c->next) {
371 if (c->da == da && c->compare == func) {
377 if (c == NULL) return;
380 last->next = c->next;
388 /** Unregister comparison function for a module
390 * All paircompare() functions for this module will be unregistered.
392 * @param instance the module instance
394 void paircompare_unregister_instance(void *instance)
396 struct cmp *c, **tail;
399 while ((c = *tail) != NULL) {
400 if (c->instance == instance) {
410 /** Compare two pair lists except for the password information.
412 * For every element in "check" at least one matching copy must be present
415 * @param[in] request Current request.
416 * @param[in] req_list request valuepairs.
417 * @param[in] check Check/control valuepairs.
418 * @param[in,out] rep_list Reply value pairs.
420 * @return 0 on match.
422 int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
423 VALUE_PAIR **rep_list)
426 VALUE_PAIR *check_item;
427 VALUE_PAIR *auth_item;
428 DICT_ATTR const *from;
434 for (check_item = fr_cursor_init(&cursor, &check);
436 check_item = fr_cursor_next(&cursor)) {
438 * If the user is setting a configuration value,
439 * then don't bother comparing it to any attributes
440 * sent to us by the user. It ALWAYS matches.
442 if ((check_item->op == T_OP_SET) ||
443 (check_item->op == T_OP_ADD)) {
447 if (!check_item->da->vendor) switch (check_item->da->attr) {
449 * Attributes we skip during comparison.
450 * These are "server" check items.
452 case PW_CRYPT_PASSWORD:
456 case PW_SESSION_TYPE:
457 case PW_STRIP_USER_NAME:
462 * IF the password attribute exists, THEN
463 * we can do comparisons against it. If not,
464 * then the request did NOT contain a
465 * User-Password attribute, so we CANNOT do
466 * comparisons against it.
468 * This hack makes CHAP-Password work..
470 case PW_USER_PASSWORD:
471 if (check_item->op == T_OP_CMP_EQ) {
472 WARN("Found User-Password == \"...\"");
473 WARN("Are you sure you don't mean Cleartext-Password?");
474 WARN("See \"man rlm_pap\" for more information");
476 if (pairfind(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
483 * See if this item is present in the request.
485 first_only = otherattr(check_item->da, &from);
487 auth_item = req_list;
490 while (auth_item != NULL) {
491 if ((auth_item->da == from) || (!from)) {
494 auth_item = auth_item->next;
499 * Not found, it's not a match.
501 if (auth_item == NULL) {
503 * Didn't find it. If we were *trying*
504 * to not find it, then we succeeded.
506 if (check_item->op == T_OP_CMP_FALSE) {
514 * Else we found it, but we were trying to not
515 * find it, so we failed.
517 if (check_item->op == T_OP_CMP_FALSE) {
523 * We've got to xlat the string before doing
526 radius_xlat_do(request, check_item);
529 * OK it is present now compare them.
531 compare = radius_callback_compare(request, auth_item,
532 check_item, check, rep_list);
534 switch (check_item->op) {
537 RWDEBUG("Invalid operator '%s' for item %s: reverting to '=='",
538 fr_int2str(fr_tokens, check_item->op, "<INVALID>"), check_item->da->name);
543 if (compare != 0) result = -1;
547 if (compare == 0) result = -1;
551 if (compare >= 0) result = -1;
555 if (compare <= 0) result = -1;
559 if (compare > 0) result = -1;
563 if (compare < 0) result = -1;
569 if (compare != 0) result = -1;
572 } /* switch over the operator of the check item */
575 * This attribute didn't match, but maybe there's
576 * another of the same attribute, which DOES match.
578 if ((result != 0) && (!first_only)) {
579 auth_item = auth_item->next;
584 } /* for every entry in the check item list */
589 /** Expands an attribute marked with pairmark_xlat
591 * Writes the new value to the vp.
593 * @param request Current request.
594 * @param vp to expand.
595 * @return 0 if successful else -1 (on xlat failure) or -2 (on parse failure).
596 * On failure pair will still no longer be marked for xlat expansion.
598 int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
604 if (vp->type != VT_XLAT) return 0;
608 len = radius_xlat(buffer, sizeof(buffer), request, vp->value.xlat, NULL, NULL);
610 rad_const_free(vp->value.xlat);
611 vp->value.xlat = NULL;
617 * Parse the string into a new value.
619 if (pairparsevalue(vp, buffer, 0) < 0){
626 /** Create a VALUE_PAIR and add it to a list of VALUE_PAIR s
628 * @note This function ALWAYS returns. If we're OOM, then it causes the
629 * @note server to exit, so you don't need to check the return value.
631 * @param[in] ctx for talloc
632 * @param[out] vps List to add new VALUE_PAIR to, if NULL will just
634 * @param[in] attribute number.
635 * @param[in] vendor number.
636 * @return a new VLAUE_PAIR or causes server to exit on error.
638 VALUE_PAIR *radius_paircreate(TALLOC_CTX *ctx, VALUE_PAIR **vps,
639 unsigned int attribute, unsigned int vendor)
643 vp = paircreate(ctx, attribute, vendor);
646 rad_assert("No memory" == NULL);
650 if (vps) pairadd(vps, vp);
655 /** Print a single valuepair to stderr or error log.
657 * @param[in] vp list to print.
659 void debug_pair(VALUE_PAIR *vp)
661 if (!vp || !debug_flag || !fr_log_fp) return;
663 vp_print(fr_log_fp, vp);
666 /** Print a list of valuepairs to stderr or error log.
668 * @param[in] vp to print.
670 void debug_pair_list(VALUE_PAIR *vp)
673 if (!vp || !debug_flag || !fr_log_fp) return;
675 for (vp = fr_cursor_init(&cursor, &vp);
677 vp = fr_cursor_next(&cursor)) {
678 vp_print(fr_log_fp, vp);
683 /** Print a list of valuepairs to the request list.
685 * @param[in] level Debug level (1-4).
686 * @param[in] request to read logging params from.
687 * @param[in] vp to print.
689 void rdebug_pair_list(int level, REQUEST *request, VALUE_PAIR *vp)
693 if (!vp || !request || !request->log.func) return;
695 for (vp = fr_cursor_init(&cursor, &vp);
697 vp = fr_cursor_next(&cursor)) {
699 * Take this opportunity to verify all the VALUE_PAIRs are still valid.
701 if (!talloc_get_type(vp, VALUE_PAIR)) {
702 REDEBUG("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
704 fr_log_talloc_report(vp);
708 vp_prints(buffer, sizeof(buffer), vp);
709 RDEBUGX(level, "\t%s", buffer);
713 /** Resolve attribute pair_lists_t value to an attribute list.
715 * The value returned is a pointer to the pointer of the HEAD of the list
716 * in the REQUEST. If the head of the list changes, the pointer will still
719 * @param[in] request containing the target lists.
720 * @param[in] list pair_list_t value to resolve to VALUE_PAIR list.
721 * Will be NULL if list name couldn't be resolved.
723 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
725 if (!request) return NULL;
728 case PAIR_LIST_UNKNOWN:
732 case PAIR_LIST_REQUEST:
733 return &request->packet->vps;
735 case PAIR_LIST_REPLY:
736 return &request->reply->vps;
738 case PAIR_LIST_CONTROL:
739 return &request->config_items;
742 case PAIR_LIST_PROXY_REQUEST:
743 if (!request->proxy) break;
744 return &request->proxy->vps;
746 case PAIR_LIST_PROXY_REPLY:
747 if (!request->proxy) break;
748 return &request->proxy_reply->vps;
753 (request->coa->proxy->code == PW_CODE_COA_REQUEST)) {
754 return &request->coa->proxy->vps;
758 case PAIR_LIST_COA_REPLY:
759 if (request->coa && /* match reply with request */
760 (request->coa->proxy->code == PW_CODE_COA_REQUEST) &&
761 request->coa->proxy_reply) {
762 return &request->coa->proxy_reply->vps;
768 (request->coa->proxy->code == PW_CODE_DISCONNECT_REQUEST)) {
769 return &request->coa->proxy->vps;
773 case PAIR_LIST_DM_REPLY:
774 if (request->coa && /* match reply with request */
775 (request->coa->proxy->code == PW_CODE_DISCONNECT_REQUEST) &&
776 request->coa->proxy_reply) {
777 return &request->coa->proxy->vps;
783 RWDEBUG2("List \"%s\" is not available",
784 fr_int2str(pair_lists, list, "<INVALID>"));
790 TALLOC_CTX *radius_list_ctx(REQUEST *request, pair_lists_t list_name)
792 if (!request) return NULL;
795 case PAIR_LIST_REQUEST:
796 return request->packet;
798 case PAIR_LIST_REPLY:
799 return request->reply;
801 case PAIR_LIST_CONTROL:
805 case PAIR_LIST_PROXY_REQUEST:
806 return request->proxy;
808 case PAIR_LIST_PROXY_REPLY:
809 return request->proxy_reply;
814 if (!request->coa) return NULL;
815 rad_assert(request->coa->proxy != NULL);
816 if (request->coa->proxy->code != PW_CODE_COA_REQUEST) return NULL;
817 return request->coa->proxy;
819 case PAIR_LIST_COA_REPLY:
820 if (!request->coa) return NULL;
821 rad_assert(request->coa->proxy != NULL);
822 if (request->coa->proxy->code != PW_CODE_COA_REQUEST) return NULL;
823 return request->coa->proxy_reply;
826 if (!request->coa) return NULL;
827 rad_assert(request->coa->proxy != NULL);
828 if (request->coa->proxy->code != PW_CODE_DISCONNECT_REQUEST) return NULL;
829 return request->coa->proxy;
831 case PAIR_LIST_DM_REPLY:
832 if (!request->coa) return NULL;
833 rad_assert(request->coa->proxy != NULL);
834 if (request->coa->proxy->code != PW_CODE_DISCONNECT_REQUEST) return NULL;
835 return request->coa->proxy_reply;
846 * Debug print a map / VP
848 void radius_map_debug(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR const *vp)
853 rad_assert(vp || (map->src->type == VPT_TYPE_NULL));
855 switch (map->src->type) {
857 * Just print the value being assigned
860 case VPT_TYPE_LITERAL:
861 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
866 case VPT_TYPE_XLAT_STRUCT:
867 vp_prints_value(buffer, sizeof(buffer), vp, '"');
872 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
877 * Just printing the value doesn't make sense, but we still
878 * want to know what it was...
881 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
883 if (map->src->vpt_request == REQUEST_OUTER) {
884 value = talloc_typed_asprintf(request, "&outer.%s:%s -> %s",
885 fr_int2str(pair_lists, map->src->vpt_list, "<INVALID>"),
886 vp->da->name, buffer);
888 value = talloc_typed_asprintf(request, "&%s:%s -> %s",
889 fr_int2str(pair_lists, map->src->vpt_list, "<INVALID>"),
890 vp->da->name, buffer);
895 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
896 value = talloc_typed_asprintf(request, "&%s -> %s", map->src->vpt_da->name, buffer);
900 strcpy(buffer, "ANY");
905 switch (map->dst->type) {
907 RDEBUG("\t%s%s %s %s", map->dst->name, vp ? vp->da->name : "",
908 fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
912 RDEBUG("\t%s %s %s", map->dst->name,
913 fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
920 if (value != buffer) talloc_free(value);
923 #define DEBUG_OVERWRITE(_old, _new) \
925 if (RDEBUG_ENABLED3) {\
926 char *old = vp_aprint_value(request, _old);\
927 char *new = vp_aprint_value(request, _new);\
928 RDEBUG3("Overwriting value \"%s\" with \"%s\"", old, new);\
934 /** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
936 * Takes a single value_pair_map_t, resolves request and list identifiers
937 * to pointers in the current request, then attempts to retrieve module
938 * specific value(s) using callback, and adds the resulting values to the
939 * correct request/list.
941 * @param request The current request.
942 * @param map specifying destination attribute and location and src identifier.
943 * @param func to retrieve module specific values and convert them to
945 * @param ctx to be passed to func.
946 * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
948 int radius_map2request(REQUEST *request, value_pair_map_t const *map, radius_tmpl_getvalue_t func, void *ctx)
952 VALUE_PAIR **list, *vp, *dst, *head = NULL;
956 vp_cursor_t dst_list, src_list;
959 * Sanity check inputs. We can have a list or attribute
962 if ((map->dst->type != VPT_TYPE_LIST) &&
963 (map->dst->type != VPT_TYPE_ATTR)) {
964 REDEBUG("Invalid mapping destination");
969 if (radius_request(&context, map->dst->vpt_request) < 0) {
970 REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
975 * If there's no CoA packet and we're updating it,
978 if (((map->dst->vpt_list == PAIR_LIST_COA) ||
979 (map->dst->vpt_list == PAIR_LIST_DM)) && !request->coa) {
980 request_alloc_coa(context);
981 context->coa->proxy->code = (map->dst->vpt_list == PAIR_LIST_COA) ?
982 PW_CODE_COA_REQUEST :
983 PW_CODE_DISCONNECT_REQUEST;
986 list = radius_list(context, map->dst->vpt_list);
988 REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
993 parent = radius_list_ctx(context, map->dst->vpt_list);
997 * The callback should either return -1 to signify operations error,
998 * -2 when it can't find the attribute or list being referenced, or
999 * 0 to signify success. It may return "sucess", but still have no
1002 if (map->src->type != VPT_TYPE_NULL) {
1003 rcode = func(&head, request, map, ctx);
1008 if (!head) return rcode;
1010 if (debug_flag) radius_map_debug(request, map, NULL);
1014 * Reparent the VPs (func may return multiple)
1016 for (vp = fr_cursor_init(&src_list, &head);
1018 vp = fr_cursor_next(&src_list)) {
1021 if (debug_flag) radius_map_debug(request, map, vp);
1022 (void) talloc_steal(parent, vp);
1026 * The destination is a list (which is a completely different set of operations)
1028 if (map->dst->type == VPT_TYPE_LIST) {
1030 case T_OP_CMP_FALSE:
1031 /* We don't need the src VPs (should just be 'ANY') */
1034 /* Clear the entire dst list */
1037 if (map->dst->vpt_list == PAIR_LIST_REQUEST) {
1038 context->username = NULL;
1039 context->password = NULL;
1044 if (map->src->type == VPT_TYPE_LIST) {
1049 rad_assert(map->src->type == VPT_TYPE_EXEC);
1050 pairmove(parent, list, &head);
1056 pairadd(list, head);
1067 * Find the destination attribute. We leave with either
1068 * the dst_list and vp pointing to the attribute or the VP
1069 * being NULL (no attribute at that index).
1071 num = map->dst->vpt_num;
1072 (void) fr_cursor_init(&dst_list, list);
1073 if (num != NUM_ANY) {
1074 while ((dst = fr_cursor_next_by_da(&dst_list, map->dst->vpt_da, map->dst->vpt_tag))) {
1075 if (num-- == 0) break;
1078 dst = fr_cursor_next_by_da(&dst_list, map->dst->vpt_da, map->dst->vpt_tag);
1080 rad_assert(!dst || (map->dst->vpt_da == dst->da));
1083 * The destination is an attribute
1089 * !* - Remove all attributes which match dst in the specified list.
1090 * This doesn't use attributes returned by the func(), and immediately frees them.
1092 case T_OP_CMP_FALSE:
1093 /* We don't need the src VPs (should just be 'ANY') */
1098 * Wildcard: delete all of the matching ones, based on tag.
1100 if (map->dst->vpt_num == NUM_ANY) {
1101 pairdelete(list, map->dst->vpt_da->attr, map->dst->vpt_da->vendor, map->dst->vpt_tag);
1104 * We've found the Nth one. Delete it, and only it.
1107 dst = fr_cursor_remove(&dst_list);
1112 * Check that the User-Name and User-Password
1113 * caches point to the correct attribute.
1118 * -= - Delete attributes in the dst list which match any of the
1119 * src_list attributes.
1121 * This operation has two modes:
1122 * - If map->dst->vpt_num > 0, we check each of the src_list attributes against
1123 * the dst attribute, to see if any of their values match.
1124 * - If map->dst->vpt_num == NUM_ANY, we compare all instances of the dst attribute
1125 * against each of the src_list attributes.
1128 /* We didn't find any attributes earlier */
1135 * Instance specific[n] delete
1137 if (map->dst->vpt_num != NUM_ANY) {
1138 for (vp = fr_cursor_first(&src_list);
1140 vp = fr_cursor_next(&src_list)) {
1141 head->op = T_OP_CMP_EQ;
1142 rcode = radius_compare_vps(request, vp, dst);
1144 dst = fr_cursor_remove(&dst_list);
1150 if (!found) return 0;
1155 * All instances[*] delete
1157 for (dst = fr_cursor_current(&dst_list);
1159 dst = fr_cursor_next_by_da(&dst_list, map->dst->vpt_da, map->dst->vpt_tag)) {
1160 for (vp = fr_cursor_first(&src_list);
1162 vp = fr_cursor_next(&src_list)) {
1163 head->op = T_OP_CMP_EQ;
1164 rcode = radius_compare_vps(request, vp, dst);
1166 dst = fr_cursor_remove(&dst_list);
1173 if (!found) return 0;
1178 * Another fixup pass to set tags on attributes were about to insert
1180 if (map->dst->vpt_tag != TAG_ANY) {
1181 for (vp = fr_cursor_init(&src_list, &head);
1183 vp = fr_cursor_next(&src_list)) {
1184 vp->tag = map->dst->vpt_tag;
1190 * = - Set only if not already set
1194 RDEBUG3("Refusing to overwrite (use :=)");
1199 /* Insert first instance (if multiple) */
1200 fr_cursor_first(&src_list);
1201 fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1202 /* Free any we didn't insert */
1207 * := - Overwrite existing attribute with last src_list attribute
1210 /* Wind to last instance */
1211 fr_cursor_last(&src_list);
1213 dst = fr_cursor_remove(&dst_list);
1214 DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
1217 fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1218 /* Free any we didn't insert */
1223 * += - Add all src_list attributes to the destination
1226 /* Insert all the instances! (if multiple) */
1227 pairadd(list, head);
1232 * Filtering operators
1236 * If the dst doesn't exist, the filters will add
1237 * it with the given value.
1240 RDEBUG3("No existing attribute to filter, adding instead");
1241 fr_cursor_insert(&dst_list, head);
1247 * The LHS exists. We need to limit it's value based on
1248 * the operator, and the value of the RHS.
1251 for (vp = fr_cursor_first(&src_list);
1253 vp = fr_cursor_next(&src_list)) {
1255 rcode = radius_compare_vps(request, vp, dst);
1260 if (rcode == 0) continue;
1262 dst = fr_cursor_remove(&dst_list);
1263 DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
1265 fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1270 if (rcode <= 0) continue;
1274 if (rcode >= 0) continue;
1283 if (!found) return 0;
1291 if (map->dst->vpt_list == PAIR_LIST_REQUEST) {
1292 context->username = pairfind(*list, PW_USER_NAME, 0, TAG_ANY);
1293 context->password = pairfind(*list, PW_USER_PASSWORD, 0, TAG_ANY);
1298 /** Process map which has exec as a src
1300 * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so
1301 * has been broken out into it's own function.
1303 * @param[out] out Where to write the VALUE_PAIR(s).
1304 * @param[in] request structure (used only for talloc).
1305 * @param[in] map the map. The LHS (dst) must be VPT_TYPE_ATTR or VPT_TYPE_LIST. The RHS (src) must be VPT_TYPE_EXEC.
1306 * @return -1 on failure, 0 on success.
1308 int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map)
1311 char *expanded = NULL;
1313 VALUE_PAIR **input_pairs = NULL;
1314 VALUE_PAIR *output_pairs = NULL;
1318 rad_assert(map->src->type == VPT_TYPE_EXEC);
1319 rad_assert((map->dst->type == VPT_TYPE_ATTR) || (map->dst->type == VPT_TYPE_LIST));
1322 * We always put the request pairs into the environment
1324 input_pairs = radius_list(request, PAIR_LIST_REQUEST);
1327 * Automagically switch output type depending on our destination
1328 * If dst is a list, then we create attributes from the output of the program
1329 * if dst is an attribute, then we create an attribute of that type and then
1330 * call pairparsevalue on the output of the script.
1332 result = radius_exec_program(request, map->src->name, true, true,
1333 answer, sizeof(answer), EXEC_TIMEOUT,
1334 input_pairs ? *input_pairs : NULL,
1335 (map->dst->type == VPT_TYPE_LIST) ? &output_pairs : NULL);
1336 talloc_free(expanded);
1338 talloc_free(output_pairs);
1342 switch (map->dst->type) {
1344 if (!output_pairs) {
1345 REDEBUG("No valid attributes received from program");
1348 *out = output_pairs;
1355 vp = pairalloc(request, map->dst->vpt_da);
1358 if (pairparsevalue(vp, answer, 0) < 0) {
1374 /** Convert a map to a VALUE_PAIR.
1376 * @param[out] out Where to write the VALUE_PAIR(s), which may be NULL if not found
1377 * @param[in] request structure (used only for talloc)
1378 * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST.
1379 * @param[in] ctx unused
1380 * @return 0 on success, -1 on failure
1382 int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx)
1385 VALUE_PAIR *vp = NULL, *new, *found = NULL;
1386 DICT_ATTR const *da;
1387 REQUEST *context = request;
1393 * Special case for !*, we don't need to parse RHS as this is a unary operator.
1395 if (map->op == T_OP_CMP_FALSE) return 0;
1398 * List to list found, this is a special case because we don't need
1399 * to allocate any attributes, just finding the current list, and change
1402 if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) {
1403 VALUE_PAIR **from = NULL;
1405 if (radius_request(&context, map->src->vpt_request) == 0) {
1406 from = radius_list(context, map->src->vpt_list);
1408 if (!from) return 0;
1410 found = paircopy(request, *from);
1413 * List to list copy is empty if the src list has no attributes.
1415 if (!found) return 0;
1417 for (vp = fr_cursor_init(&cursor, &found);
1419 vp = fr_cursor_next(&cursor)) {
1429 * Deal with all non-list operations.
1431 da = map->dst->vpt_da ? map->dst->vpt_da : map->src->vpt_da;
1436 switch (map->src->type) {
1440 case VPT_TYPE_XLAT_STRUCT:
1441 rad_assert(map->dst->vpt_da); /* Need to know where were going to write the new attribute */
1442 rad_assert(map->src->vpt_xlat != NULL);
1444 new = pairalloc(request, da);
1445 if (!new) return -1;
1448 slen = radius_axlat_struct(&str, request, map->src->vpt_xlat, NULL, NULL);
1455 * We do the debug printing because radius_axlat_struct
1456 * doesn't have access to the original string. It's been
1457 * mangled during the parsing to xlat_exp_t
1459 RDEBUG2("EXPAND %s", map->src->name);
1460 RDEBUG2(" --> %s", str);
1462 rcode = pairparsevalue(new, str, 0);
1473 rad_assert(map->dst->vpt_da); /* Need to know where were going to write the new attribute */
1475 new = pairalloc(request, da);
1476 if (!new) return -1;
1479 slen = radius_axlat(&str, request, map->src->name, NULL, NULL);
1485 rcode = pairparsevalue(new, str, 0);
1495 case VPT_TYPE_LITERAL:
1496 new = pairalloc(request, da);
1497 if (!new) return -1;
1499 if (pairparsevalue(new, map->src->name, 0) < 0) {
1511 rad_assert(!map->dst->vpt_da ||
1512 (map->src->vpt_da->type == map->dst->vpt_da->type) ||
1513 (map->src->vpt_da->type == PW_TYPE_OCTETS) ||
1514 (map->dst->vpt_da->type == PW_TYPE_OCTETS));
1517 * @todo should log error, and return -1 for v3.1 (causes update to fail)
1519 if (radius_tmpl_copy_vp(request, &found, request, map->src) < 0) return 0;
1521 vp = fr_cursor_init(&from, &found);
1523 * Src/Dst attributes don't match, convert src attributes
1526 if (map->src->vpt_da->type != map->dst->vpt_da->type) {
1529 (void) fr_cursor_init(&to, out);
1530 for (; vp; vp = fr_cursor_next(&from)) {
1531 new = pairalloc(request, da);
1532 if (!new) return -1;
1533 if (pairdatacpy(new, vp->da, &vp->data, vp->length) < 0) {
1534 REDEBUG("Attribute conversion failed: %s", fr_strerror());
1539 vp = fr_cursor_remove(&from);
1543 fr_cursor_insert(&to, new);
1549 * Otherwise we just need to fixup the attribute types
1552 for (; vp; vp = fr_cursor_next(&from)) {
1561 rad_assert(map->src && map->src->vpt_da);
1562 rad_assert(map->dst && map->dst->vpt_da);
1563 rad_assert(map->src->vpt_da->type == map->dst->vpt_da->type);
1565 new = pairalloc(request, da);
1566 if (!new) return -1;
1568 if (pairdatacpy(new, map->src->vpt_da, map->src->vpt_value, map->src->vpt_length) < 0) goto error;
1574 * This essentially does the same as rlm_exec xlat, except it's non-configurable.
1575 * It's only really here as a convenience for people who expect the contents of
1576 * backticks to be executed in a shell.
1578 * exec string is xlat expanded and arguments are shell escaped.
1581 return radius_mapexec(out, request, map);
1584 rad_assert(0); /* Should have been caught at parse time */
1594 /** Convert a valuepair string to valuepair map
1596 * Takes a valuepair string with list and request qualifiers, converts it into a
1597 * value_pair_map_t and inserts it into the appropriate list.
1599 * @param out Where to write the new map (must be freed with talloc_free()).
1600 * @param request Current request.
1601 * @param raw string to parse.
1602 * @param dst_request_def to use if attribute isn't qualified.
1603 * @param dst_list_def to use if attribute isn't qualified.
1604 * @param src_request_def to use if attribute isn't qualified.
1605 * @param src_list_def to use if attribute isn't qualified.
1606 * @return 0 on success, < 0 on error.
1608 int radius_strpair2map(value_pair_map_t **out, REQUEST *request, char const *raw,
1609 request_refs_t dst_request_def, pair_lists_t dst_list_def,
1610 request_refs_t src_request_def, pair_lists_t src_list_def)
1612 char const *p = raw;
1615 VALUE_PAIR_RAW tokens;
1616 value_pair_map_t *map;
1618 ret = pairread(&p, &tokens);
1620 REDEBUG("Failed tokenising attribute string: %s", fr_strerror());
1624 map = radius_str2map(request, tokens.l_opand, T_BARE_WORD, tokens.op, tokens.r_opand, tokens.quote,
1625 dst_request_def, dst_list_def, src_request_def, src_list_def);
1627 REDEBUG("Failed parsing attribute string: %s", fr_strerror());
1635 /** Check whether the destination of a map is currently valid
1637 * @param request The current request.
1638 * @param map to check.
1639 * @return true if the map resolves to a request and list else false.
1641 bool radius_map_dst_valid(REQUEST *request, value_pair_map_t const *map)
1643 REQUEST *context = request;
1645 if (radius_request(&context, map->dst->vpt_request) < 0) return false;
1646 if (!radius_list(context, map->dst->vpt_list)) return false;
1651 /** Return a VP from a value_pair_tmpl_t
1653 * @param out where to write the retrieved vp.
1654 * @param request current request.
1655 * @param vpt the value pair template
1656 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
1658 int radius_tmpl_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
1660 VALUE_PAIR **vps, *vp;
1662 if (out) *out = NULL;
1664 if (radius_request(&request, vpt->vpt_request) < 0) {
1668 vps = radius_list(request, vpt->vpt_list);
1673 switch (vpt->type) {
1675 * May not may not be found, but it *is* a known
1683 if (vpt->vpt_num == NUM_ANY) {
1684 vp = pairfind(*vps, vpt->vpt_da->attr, vpt->vpt_da->vendor, vpt->vpt_tag);
1689 (void) fr_cursor_init(&cursor, vps);
1691 while ((vp = fr_cursor_next_by_da(&cursor, vpt->vpt_da, vpt->vpt_tag))) {
1693 if (num-- <= 0) goto finish;
1704 * literal, xlat, regex, exec, data.
1716 /** Return a VP from the specified request.
1718 * @param out where to write the pointer to the resolved VP.
1719 * Will be NULL if the attribute couldn't be resolved.
1720 * @param request current request.
1721 * @param name attribute name including qualifiers.
1722 * @return -4 if either the attribute or qualifier were invalid, and the same error codes as radius_tmpl_get_vp for other
1725 int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
1727 value_pair_tmpl_t vpt;
1731 if (radius_parse_attr(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
1735 return radius_tmpl_get_vp(out, request, &vpt);
1738 /** Copy pairs matching a VPT in the current request
1740 * @param out where to write the copied vps.
1741 * @param request current request.
1742 * @param vpt the value pair template
1743 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
1745 int radius_tmpl_copy_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
1747 VALUE_PAIR **vps, *vp;
1748 REQUEST *current = request;
1749 vp_cursor_t from, to;
1751 if (out) *out = NULL;
1753 if (radius_request(¤t, vpt->vpt_request) < 0) {
1757 vps = radius_list(request, vpt->vpt_list);
1762 switch (vpt->type) {
1764 * May not may not be found, but it *is* a known name.
1770 (void) fr_cursor_init(&to, out);
1771 (void) fr_cursor_init(&from, vps);
1773 vp = fr_cursor_next_by_da(&from, vpt->vpt_da, vpt->vpt_tag);
1776 switch (vpt->vpt_num) {
1777 /* Copy all pairs of this type (and tag) */
1781 vp = paircopyvp(ctx, vp);
1786 fr_cursor_insert(&to, vp);
1787 } while ((vp = fr_cursor_next_by_da(&from, vpt->vpt_da, vpt->vpt_tag)));
1790 /* Specific attribute number */
1792 for (num = vpt->vpt_num;
1794 num--, vp = fr_cursor_next_by_da(&from, vpt->vpt_da, vpt->vpt_tag)) {
1800 /* Just copy the first pair */
1802 vp = paircopyvp(ctx, vp);
1807 fr_cursor_insert(&to, vp);
1813 vp = paircopy(ctx, *vps);
1816 fr_cursor_insert(&to, vp);
1821 * literal, xlat, regex, exec, data.
1830 /** Copy VP(s) from the specified request.
1832 * @param ctx to alloc new VALUE_PAIRs in.
1833 * @param out where to write the pointer to the copied VP.
1834 * Will be NULL if the attribute couldn't be resolved.
1835 * @param request current request.
1836 * @param name attribute name including qualifiers.
1837 * @return -4 if either the attribute or qualifier were invalid, and the same error codes as radius_tmpl_get_vp for other
1840 int radius_copy_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, char const *name)
1842 value_pair_tmpl_t vpt;
1846 if (radius_parse_attr(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
1850 return radius_tmpl_copy_vp(ctx, out, request, &vpt);
1853 void module_failure_msg(REQUEST *request, char const *fmt, ...)
1858 vmodule_failure_msg(request, fmt, ap);
1862 /** Add a module failure message VALUE_PAIR to the request
1864 void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap)
1870 if (!fmt || !request->packet) {
1875 * If we don't copy the original ap we get a segfault from vasprintf. This is apparently
1876 * due to ap sometimes being implemented with a stack offset which is invalidated if
1877 * ap is passed into another function. See here:
1878 * http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
1880 * I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
1881 * running unit tests which generate errors under CI.
1884 p = talloc_vasprintf(request, fmt, aq);
1887 MEM(vp = pairmake_packet("Module-Failure-Message", NULL, T_OP_ADD));
1888 if (request->module && (request->module[0] != '\0')) {
1889 pairsprintf(vp, "%s: %s", request->module, p);
1891 pairsprintf(vp, "%s", p);