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) {
149 ret = ((int) vp->tag) - ((int) check->tag);
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;
212 ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
215 case PW_TYPE_IPV6ADDR:
216 ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
217 sizeof(vp->vp_ipv6addr));
220 case PW_TYPE_IPV6PREFIX:
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 * @todo this should probably take DA's.
291 * @param da to find comparison function for.
292 * @return true if a comparison function was found, else false.
294 int radius_find_compare(DICT_ATTR const *da)
298 for (c = cmp; c; c = c->next) {
308 /** See what attribute we want to compare with.
310 * @param da to find comparison function for.
311 * @param from reference to compare with
312 * @return true if the comparison callback require a matching attribue in the request, else false.
314 static bool otherattr(DICT_ATTR const *da, DICT_ATTR const **from)
318 for (c = cmp; c; c = c->next) {
321 return c->first_only;
329 /** Register a function as compare function.
331 * @param da to register comparison function for.
332 * @param from the attribute we want to compare with. Normally this is the same as attribute.
333 * If null call the comparison function on every attributes in the request if first_only is false
334 * @param first_only will decide if we loop over the request attributes or stop on the first one
335 * @param func comparison function
336 * @param instance argument to comparison function
339 int paircompare_register(DICT_ATTR const *da, DICT_ATTR const *from,
340 bool first_only, RAD_COMPARE_FUNC func, void *instance)
344 rad_assert(da != NULL);
346 paircompare_unregister(da, func);
348 c = rad_malloc(sizeof(struct cmp));
353 c->first_only = first_only;
354 c->instance = instance;
361 /** Unregister comparison function for an attribute
363 * @param da dict reference to unregister for.
364 * @param func comparison function to remove.
366 void paircompare_unregister(DICT_ATTR const *da, RAD_COMPARE_FUNC func)
368 struct cmp *c, *last;
371 for (c = cmp; c; c = c->next) {
372 if (c->da == da && c->compare == func) {
378 if (c == NULL) return;
381 last->next = c->next;
389 /** Unregister comparison function for a module
391 * All paircompare() functions for this module will be unregistered.
393 * @param instance the module instance
395 void paircompare_unregister_instance(void *instance)
397 struct cmp *c, **tail;
400 while ((c = *tail) != NULL) {
401 if (c->instance == instance) {
411 /** Compare two pair lists except for the password information.
413 * For every element in "check" at least one matching copy must be present
416 * @param[in] request Current request.
417 * @param[in] req_list request valuepairs.
418 * @param[in] check Check/control valuepairs.
419 * @param[in,out] rep_list Reply value pairs.
421 * @return 0 on match.
423 int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
424 VALUE_PAIR **rep_list)
427 VALUE_PAIR *check_item;
428 VALUE_PAIR *auth_item;
429 DICT_ATTR const *from;
435 for (check_item = paircursor(&cursor, &check);
437 check_item = pairnext(&cursor)) {
439 * If the user is setting a configuration value,
440 * then don't bother comparing it to any attributes
441 * sent to us by the user. It ALWAYS matches.
443 if ((check_item->op == T_OP_SET) ||
444 (check_item->op == T_OP_ADD)) {
448 if (!check_item->da->vendor) switch (check_item->da->attr) {
450 * Attributes we skip during comparison.
451 * These are "server" check items.
453 case PW_CRYPT_PASSWORD:
457 case PW_SESSION_TYPE:
458 case PW_STRIP_USER_NAME:
463 * IF the password attribute exists, THEN
464 * we can do comparisons against it. If not,
465 * then the request did NOT contain a
466 * User-Password attribute, so we CANNOT do
467 * comparisons against it.
469 * This hack makes CHAP-Password work..
471 case PW_USER_PASSWORD:
472 if (check_item->op == T_OP_CMP_EQ) {
473 WDEBUG("Found User-Password == \"...\".");
474 WDEBUG("Are you sure you don't mean Cleartext-Password?");
475 WDEBUG("See \"man rlm_pap\" for more information.");
477 if (pairfind(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
484 * See if this item is present in the request.
486 first_only = otherattr(check_item->da, &from);
488 auth_item = req_list;
491 while (auth_item != NULL) {
492 if ((auth_item->da == from) || (!from)) {
495 auth_item = auth_item->next;
500 * Not found, it's not a match.
502 if (auth_item == NULL) {
504 * Didn't find it. If we were *trying*
505 * to not find it, then we succeeded.
507 if (check_item->op == T_OP_CMP_FALSE) {
515 * Else we found it, but we were trying to not
516 * find it, so we failed.
518 if (check_item->op == T_OP_CMP_FALSE) {
524 * We've got to xlat the string before doing
527 radius_xlat_do(request, check_item);
530 * OK it is present now compare them.
532 compare = radius_callback_compare(request, auth_item,
533 check_item, check, rep_list);
535 switch (check_item->op) {
538 INFO("Invalid operator for item %s: "
539 "reverting to '=='", check_item->da->name);
544 if (compare != 0) result = -1;
548 if (compare == 0) result = -1;
552 if (compare >= 0) result = -1;
556 if (compare <= 0) result = -1;
560 if (compare > 0) result = -1;
564 if (compare < 0) result = -1;
570 if (compare != 0) result = -1;
573 } /* switch over the operator of the check item */
576 * This attribute didn't match, but maybe there's
577 * another of the same attribute, which DOES match.
579 if ((result != 0) && (!first_only)) {
580 auth_item = auth_item->next;
585 } /* for every entry in the check item list */
590 /** Expands an attribute marked with pairmark_xlat
592 * Writes the new value to the vp.
594 * @param request Current request.
595 * @param vp to expand.
596 * @return 0 if successful else -1 (on xlat failure) or -2 (on parse failure).
597 * On failure pair will still no longer be marked for xlat expansion.
599 int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
605 if (vp->type != VT_XLAT) return 0;
609 len = radius_xlat(buffer, sizeof(buffer), request, vp->value.xlat, NULL, NULL);
612 rad_const_free(vp->value.xlat);
613 vp->value.xlat = NULL;
619 * Parse the string into a new value.
621 if (!pairparsevalue(vp, buffer)){
628 /** Move pairs, replacing/over-writing them, and doing xlat.
630 * Move attributes from one list to the other if not already present.
632 void radius_xlat_move(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR **from)
634 VALUE_PAIR **tailto, *i, *j, *next;
635 VALUE_PAIR *tailfrom = NULL;
639 * Point "tailto" to the end of the "to" list.
642 for (i = *to; i; i = i->next) {
647 * Loop over the "from" list.
649 for (i = *from; i; i = next) {
653 * Don't move 'fallthrough' over.
655 if (!i->da->vendor && i->da->attr == PW_FALL_THROUGH) {
661 * We've got to xlat the string before moving
664 radius_xlat_do(request, i);
666 found = pairfind(*to, i->da->attr, i->da->vendor, TAG_ANY);
670 * If a similar attribute is found,
673 case T_OP_SUB: /* -= */
675 if (!i->vp_strvalue[0] ||
676 (strcmp(found->vp_strvalue,
677 i->vp_strvalue) == 0)) {
678 pairdelete(to, found->da->attr,
683 * 'tailto' may have been
687 for (j = *to; j; j = j->next) {
697 * Add it, if it's not already there.
699 case T_OP_EQ: /* = */
702 continue; /* with the loop */
707 * If a similar attribute is found,
708 * replace it with the new one. Otherwise,
709 * add the new one to the list.
711 case T_OP_SET: /* := */
716 memcpy(found, i, sizeof(*found));
724 * FIXME: Add support for <=, >=, <, >
726 * which will mean (for integers)
727 * 'make the attribute the smaller, etc'
731 * Add the new element to the list, even
732 * if similar ones already exist.
735 case T_OP_ADD: /* += */
740 tailfrom->next = next;
746 * If ALL of the 'to' attributes have been deleted,
747 * then ensure that the 'tail' is updated to point
758 } /* loop over the 'from' list */
761 /** Create a VALUE_PAIR and add it to a list of VALUE_PAIR s
763 * @note This function ALWAYS returns. If we're OOM, then it causes the
764 * @note server to exit, so you don't need to check the return value.
766 * @param[in] request Current request.
767 * @param[out] vps List to add new VALUE_PAIR to, if NULL will just
769 * @param[in] attribute number.
770 * @param[in] vendor number.
771 * @return a new VLAUE_PAIR or causes server to exit on error.
773 VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
774 unsigned int attribute, unsigned int vendor)
779 * FIXME: the context should ideally be the packet...
781 vp = paircreate(request, attribute, vendor);
784 rad_assert("No memory" == NULL);
788 if (vps) pairadd(vps, vp);
793 /** Print a single valuepair to stderr or error log.
795 * @param[in] vp list to print.
797 void debug_pair(VALUE_PAIR *vp)
799 if (!vp || !debug_flag || !fr_log_fp) return;
801 vp_print(fr_log_fp, vp);
804 /** Print a list of valuepairs to stderr or error log.
806 * @param[in] vp to print.
808 void debug_pair_list(VALUE_PAIR *vp)
811 if (!vp || !debug_flag || !fr_log_fp) return;
813 for (vp = paircursor(&cursor, &vp);
815 vp = pairnext(&cursor)) {
817 * Take this opportunity to verify all the VALUE_PAIRs are still valid.
819 if (!talloc_get_type(vp, VALUE_PAIR)) {
820 ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
822 log_talloc_report(vp);
826 vp_print(fr_log_fp, vp);
831 /** Print a list of valuepairs to the request list.
833 * @param[in] level Debug level (1-4).
834 * @param[in] request to read logging params from.
835 * @param[in] vp to print.
837 void rdebug_pair_list(int level, REQUEST *request, VALUE_PAIR *vp)
841 if (!vp || !request || !request->radlog) return;
843 for (vp = paircursor(&cursor, &vp);
845 vp = pairnext(&cursor)) {
847 * Take this opportunity to verify all the VALUE_PAIRs are still valid.
849 if (!talloc_get_type(vp, VALUE_PAIR)) {
850 REDEBUG("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
852 log_talloc_report(vp);
856 vp_prints(buffer, sizeof(buffer), vp);
858 request->radlog(L_DBG, level, request, "\t%s", buffer);
862 /** Resolve attribute pair_lists_t value to an attribute list.
864 * The value returned is a pointer to the pointer of the HEAD of the list
865 * in the REQUEST. If the head of the list changes, the pointer will still
868 * @param[in] request containing the target lists.
869 * @param[in] list pair_list_t value to resolve to VALUE_PAIR list.
870 * Will be NULL if list name couldn't be resolved.
872 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
874 if (!request) return NULL;
877 case PAIR_LIST_UNKNOWN:
881 case PAIR_LIST_REQUEST:
882 return &request->packet->vps;
884 case PAIR_LIST_REPLY:
885 return &request->reply->vps;
887 case PAIR_LIST_CONTROL:
888 return &request->config_items;
891 case PAIR_LIST_PROXY_REQUEST:
892 if (!request->proxy) break;
893 return &request->proxy->vps;
895 case PAIR_LIST_PROXY_REPLY:
896 if (!request->proxy) break;
897 return &request->proxy_reply->vps;
902 (request->coa->proxy->code == PW_COA_REQUEST)) {
903 return &request->coa->proxy->vps;
907 case PAIR_LIST_COA_REPLY:
908 if (request->coa && /* match reply with request */
909 (request->coa->proxy->code == PW_COA_REQUEST) &&
910 request->coa->proxy_reply) {
911 return &request->coa->proxy_reply->vps;
917 (request->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
918 return &request->coa->proxy->vps;
922 case PAIR_LIST_DM_REPLY:
923 if (request->coa && /* match reply with request */
924 (request->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
925 request->coa->proxy_reply) {
926 return &request->coa->proxy->vps;
932 RWDEBUG2("List \"%s\" is not available",
933 fr_int2str(pair_lists, list, "<INVALID>"));
938 /** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
940 * Takes a single value_pair_map_t, resolves request and list identifiers
941 * to pointers in the current request, then attempts to retrieve module
942 * specific value(s) using callback, and adds the resulting values to the
943 * correct request/list.
945 * @param request The current request.
946 * @param map specifying destination attribute and location and src identifier.
947 * @param func to retrieve module specific values and convert them to
949 * @param ctx to be passed to func.
950 * @param src name to be used in debugging if different from map value.
951 * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
953 int radius_map2request(REQUEST *request, value_pair_map_t const *map,
954 UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx)
958 VALUE_PAIR **list, *vp, *head = NULL;
961 if (radius_request(&request, map->dst->request) < 0) {
962 REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
967 list = radius_list(request, map->dst->list);
969 REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
976 * The callback should either return -1 to signify operations error, -2 when it can't find the
977 * attribute or list being referenced, or 0 to signify success.
978 * It may return "sucess", but still have no VPs to work with.
979 * Only if it returned an error code should it not write anything to the head pointer.
981 rcode = func(&head, request, map, ctx);
992 if (debug_flag) for (vp = paircursor(&cursor, &head); vp; vp = pairnext(&cursor)) {
995 switch (map->src->type) {
997 * Just print the value being assigned
1001 case VPT_TYPE_LITERAL:
1002 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
1006 vp_prints_value(buffer, sizeof(buffer), vp, '"');
1010 vp_prints_value(buffer, sizeof(buffer), vp, 0);
1014 * Just printing the value doesn't make sense, but we still
1015 * want to know what it was...
1018 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
1019 value = talloc_asprintf(request, "&%s%s -> %s", map->src->name, vp->da->name, buffer);
1022 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
1023 value = talloc_asprintf(request, "&%s -> %s", map->src->name, buffer);
1028 RDEBUG("\t\t%s %s %s", map->dst->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value);
1030 if (value != buffer) talloc_free(value);
1034 * Use pairmove so the operator is respected
1036 radius_pairmove(request, list, head);
1040 /** Process map which has exec as a src
1042 * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so
1043 * has been broken out into it's own function.
1045 * @param[out] out Where to write the VALUE_PAIR(s).
1046 * @param[in] request structure (used only for talloc).
1047 * @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.
1048 * @return -1 on failure, 0 on success.
1050 int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map)
1053 char *expanded = NULL;
1055 VALUE_PAIR **input_pairs = NULL;
1056 VALUE_PAIR **output_pairs = NULL;
1060 rad_assert(map->src->type == VPT_TYPE_EXEC);
1061 rad_assert((map->dst->type == VPT_TYPE_ATTR) || (map->dst->type == VPT_TYPE_LIST));
1064 * We always put the request pairs into the environment
1066 input_pairs = radius_list(request, PAIR_LIST_REQUEST);
1069 * Automagically switch output type depending on our destination
1070 * If dst is a list, then we create attributes from the output of the program
1071 * if dst is an attribute, then we create an attribute of that type and then
1072 * call pairparsevalue on the output of the script.
1075 result = radius_exec_program(request, map->src->name, true, true,
1076 answer, sizeof(answer), EXEC_TIMEOUT,
1077 input_pairs ? *input_pairs : NULL,
1078 (map->dst->type == VPT_TYPE_LIST) ? output_pairs : NULL);
1079 talloc_free(expanded);
1081 REDEBUG("%s", answer);
1082 talloc_free(output_pairs);
1086 switch (map->dst->type) {
1088 if (!output_pairs) {
1091 *out = *output_pairs;
1098 vp = pairalloc(request, map->dst->da);
1101 if (!pairparsevalue(vp, answer)) {
1116 /** Convert a map to a VALUE_PAIR.
1118 * @param[out] out Where to write the VALUE_PAIR(s).
1119 * @param[in] request structure (used only for talloc)
1120 * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST.
1121 * @param[in] ctx unused
1122 * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent
1124 int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx)
1127 VALUE_PAIR *vp = NULL, *found, **from = NULL;
1128 DICT_ATTR const *da;
1132 rad_assert(request != NULL);
1133 rad_assert(map != NULL);
1138 * Special case for !*, we don't need to parse the value, just allocate an attribute with
1139 * the right operator.
1141 if (map->op == T_OP_CMP_FALSE) {
1142 vp = pairalloc(request, map->dst->da);
1151 * List to list found, this is a special case because we don't need
1152 * to allocate any attributes, just found the current list, and change
1155 if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) {
1156 from = radius_list(request, map->src->list);
1157 if (!from) return -2;
1159 found = paircopy(request, *from);
1161 * List to list copy is invalid if the src list has no attributes.
1163 if (!found) return -2;
1165 for (vp = paircursor(&cursor, &found);
1167 vp = pairnext(&cursor)) {
1177 * Deal with all non-list founding operations.
1179 da = map->dst->da ? map->dst->da : map->src->da;
1181 switch (map->src->type) {
1183 case VPT_TYPE_LITERAL:
1185 vp = pairalloc(request, da);
1197 switch (map->src->type) {
1199 rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */
1201 * Don't call unnecessary expansions
1203 if (strchr(map->src->name, '%') != NULL) {
1207 slen = radius_axlat(&str, request, map->src->name, NULL, NULL);
1212 rcode = pairparsevalue(vp, str);
1224 case VPT_TYPE_LITERAL:
1225 if (!pairparsevalue(vp, map->src->name)) {
1232 rad_assert(!map->dst->da ||
1233 (map->src->da->type == map->dst->da->type) ||
1234 (map->src->da->type == PW_TYPE_OCTETS) ||
1235 (map->dst->da->type == PW_TYPE_OCTETS));
1238 if (radius_request(&context, map->src->request) == 0) {
1239 from = radius_list(context, map->src->list);
1243 * Can't add the attribute if the list isn't
1252 * Special case, destination is a list, found all instance of an attribute.
1254 if (map->dst->type == VPT_TYPE_LIST) {
1255 found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
1257 REDEBUG("Attribute \"%s\" not found in request", map->src->name);
1262 for (vp = paircursor(&cursor, &found);
1264 vp = pairnext(&cursor)) {
1273 * FIXME: allow tag references?
1275 found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
1277 REDEBUG("Attribute \"%s\" not found in request", map->src->name);
1283 * Copy the data over verbatim, assuming it's
1286 // rad_assert(found->type == VT_DATA);
1287 vp = paircopyvpdata(request, da, found);
1296 rad_assert(map->src->da->type == map->dst->da->type);
1297 memcpy(&vp->data, map->src->vpd, sizeof(vp->data));
1298 vp->length = map->src->length;
1302 * This essentially does the same as rlm_exec xlat, except it's non-configurable.
1303 * It's only really here as a convenience for people who expect the contents of
1304 * backticks to be executed in a shell.
1306 * exec string is xlat expanded and arguments are shell escaped.
1309 return radius_mapexec(out, request, map);
1311 rad_assert(0); /* Should of been caught at parse time */
1322 /** Convert a valuepair string to VALUE_PAIR and insert it into a list
1324 * Takes a valuepair string with list and request qualifiers, converts it into a VALUE_PAIR
1325 * and inserts it into the appropriate list.
1327 * @param request Current request.
1328 * @param raw string to parse.
1329 * @param request_def to use if attribute isn't qualified.
1330 * @param list_def to use if attribute isn't qualified.
1331 * @return 0 on success, -1 on error.
1333 int radius_str2vp(REQUEST *request, char const *raw, request_refs_t request_def, pair_lists_t list_def)
1340 VALUE_PAIR *vp = NULL;
1345 req = radius_request_name(&p, request_def);
1347 if (req == REQUEST_UNKNOWN) {
1348 REDEBUG("Invalid request qualifier \"%.*s\"", (int) len, raw);
1354 list = radius_list_name(&p, list_def);
1355 if (list == PAIR_LIST_UNKNOWN) {
1358 REDEBUG("Invalid list qualifier \"%.*s\"", (int) len, raw);
1364 if (radius_request(&request, req) < 0) {
1368 vps = radius_list(request, list);
1373 if (userparse(request, raw, &vp) == T_OP_INVALID) {
1377 pairmove(request, vps, &vp);
1383 /** Return a VP from a value_pair_tmpl_t
1385 * @param request current request.
1386 * @param vpt the value pair template
1387 * @return NULL if not found, or the VPs.
1389 VALUE_PAIR *radius_vpt_get_vp(REQUEST *request, value_pair_tmpl_t const *vpt)
1393 if (radius_request(&request, vpt->request) < 0) {
1397 vps = radius_list(request, vpt->list);
1402 switch (vpt->type) {
1404 * May not may not be found, but it *is* a known name.
1407 return pairfind(*vps, vpt->da->attr, vpt->da->vendor, TAG_ANY);
1421 /** Return a VP from the specified request.
1423 * @param request current request.
1424 * @param name attribute name including qualifiers.
1425 * @param vp_p where to write the pointer to the resolved VP.
1426 * Will be NULL if the attribute couldn't be resolved.
1427 * @return -1 if either the attribute or qualifier were invalid, else 0
1429 int radius_get_vp(REQUEST *request, char const *name, VALUE_PAIR **vp_p)
1431 value_pair_tmpl_t vpt;
1435 if (radius_parse_attr(name, &vpt, REQUEST_CURRENT,
1436 PAIR_LIST_REQUEST) < 0) {
1440 *vp_p = radius_vpt_get_vp(request, &vpt);
1444 /** Add a module failure message VALUE_PAIR to the request
1446 void module_failure_msg(REQUEST *request, char const *fmt, ...)
1452 if (!fmt || !request->packet) {
1459 vp = paircreate(request->packet, PW_MODULE_FAILURE_MESSAGE, 0);
1465 p = talloc_vasprintf(vp, fmt, ap);
1467 if (request->module && *request->module) {
1468 pairsprintf(vp, "%s: %s", request->module, p);
1470 pairsprintf(vp, "%s", p);
1473 pairadd(&request->packet->vps, vp);