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 #ifdef HAVE_PCREPOSIX_H
38 #include <pcreposix.h>
44 * For POSIX Regular expressions.
45 * (0) Means no extended regular expressions.
46 * REG_EXTENDED means use extended regular expressions.
49 #define REG_EXTENDED (0)
58 const FR_NAME_NUMBER vpt_types[] = {
59 {"unknown", VPT_TYPE_UNKNOWN },
60 {"literal", VPT_TYPE_LITERAL },
61 {"expanded", VPT_TYPE_XLAT },
62 {"attribute ref", VPT_TYPE_ATTR },
63 {"list", VPT_TYPE_LIST },
64 {"exec", VPT_TYPE_EXEC }
68 unsigned int attribute;
69 unsigned int otherattr;
70 void *instance; /* module instance */
71 RAD_COMPARE_FUNC compare;
74 static struct cmp *cmp;
76 /** Compares check and vp by value.
78 * Does not call any per-attribute comparison function, but does honour
79 * check.operator. Basically does "vp.value check.op check.value".
81 * @param request Current request
82 * @param check rvalue, and operator
85 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
90 * Check for =* and !* and return appropriately
92 if (check->op == T_OP_CMP_TRUE) return 0;
93 if (check->op == T_OP_CMP_FALSE) return 1;
96 if (check->op == T_OP_REG_EQ) {
100 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
102 vp_prints_value(value, sizeof(value), vp, -1);
105 * Include substring matches.
107 compare = regcomp(®, check->vp_strvalue, REG_EXTENDED);
110 regerror(compare, ®, buffer, sizeof(buffer));
112 RDEBUG("Invalid regular expression %s: %s",
113 check->vp_strvalue, buffer);
116 compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
121 * Add %{0}, %{1}, etc.
123 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
125 char buffer[sizeof(check->vp_strvalue)];
128 * Didn't match: delete old
129 * match, if it existed.
131 if ((compare != 0) ||
132 (rxmatch[i].rm_so == -1)) {
133 p = request_data_get(request, request,
134 REQUEST_DATA_REGEX | i);
148 * Copy substring into buffer.
150 memcpy(buffer, value + rxmatch[i].rm_so,
151 rxmatch[i].rm_eo - rxmatch[i].rm_so);
152 buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
155 * Copy substring, and add it to
158 * Note that we don't check
159 * for out of memory, which is
160 * the only error we can get...
163 request_data_add(request, request,
164 REQUEST_DATA_REGEX | i,
167 if (compare == 0) return 0;
171 if (check->op == T_OP_REG_NE) {
175 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
177 vp_prints_value(value, sizeof(value), vp, -1);
180 * Include substring matches.
182 compare = regcomp(®, (char *)check->vp_strvalue,
186 regerror(compare, ®, buffer, sizeof(buffer));
188 RDEBUG("Invalid regular expression %s: %s",
189 check->vp_strvalue, buffer);
192 compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
196 if (compare != 0) return 0;
203 * Tagged attributes are equal if and only if both the
204 * tag AND value match.
206 if (check->da->flags.has_tag) {
207 ret = ((int) vp->tag) - ((int) check->tag);
208 if (ret != 0) return ret;
212 * Not a regular expression, compare the types.
214 switch(check->da->type) {
215 #ifdef WITH_ASCEND_BINARY
217 * Ascend binary attributes can be treated
218 * as opaque objects, I guess...
220 case PW_TYPE_ABINARY:
223 if (vp->length != check->length) {
224 ret = 1; /* NOT equal */
227 ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
232 ret = strcmp((char *)vp->vp_strvalue,
233 (char *)check->vp_strvalue);
238 case PW_TYPE_INTEGER:
239 ret = vp->vp_integer - check->vp_integer;
242 case PW_TYPE_INTEGER64:
244 * Don't want integer overflow!
246 if (vp->vp_integer64 < check->vp_integer64) {
248 } else if (vp->vp_integer64 > check->vp_integer64) {
256 if (vp->vp_signed < check->vp_signed) {
258 } else if (vp->vp_signed > check->vp_signed) {
266 ret = vp->vp_date - check->vp_date;
270 ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
273 case PW_TYPE_IPV6ADDR:
274 ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
275 sizeof(vp->vp_ipv6addr));
278 case PW_TYPE_IPV6PREFIX:
279 ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
280 sizeof(vp->vp_ipv6prefix));
284 ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
285 sizeof(vp->vp_ifid));
296 /** Compare check and vp. May call the attribute compare function.
298 * Unlike radius_compare_vps() this function will call any attribute-specific
299 * comparison function.
301 * @param req Current request
302 * @param request value pairs in the reqiest
305 * @param reply_pairs value pairs in the reply
308 int radius_callback_compare(REQUEST *req, VALUE_PAIR *request,
309 VALUE_PAIR *check, VALUE_PAIR *check_pairs,
310 VALUE_PAIR **reply_pairs)
315 * Check for =* and !* and return appropriately
317 if (check->op == T_OP_CMP_TRUE) return 0;
318 if (check->op == T_OP_CMP_FALSE) return 1;
321 * See if there is a special compare function.
323 * FIXME: use new RB-Tree code.
325 for (c = cmp; c; c = c->next) {
326 if (!check->da->vendor && (c->attribute == check->da->attr)) {
327 return (c->compare)(c->instance, req, request, check,
328 check_pairs, reply_pairs);
332 if (!request) return -1; /* doesn't exist, don't compare it */
334 return radius_compare_vps(req, check, request);
338 /** Find a comparison function for two attributes.
342 int radius_find_compare(unsigned int attribute)
346 for (c = cmp; c; c = c->next) {
347 if (c->attribute == attribute) {
356 /** See what attribute we want to compare with.
360 static int otherattr(unsigned int attribute)
364 for (c = cmp; c; c = c->next) {
365 if (c->attribute == attribute) {
373 /** Register a function as compare function.
376 * @param other_attr we want to compare with. Normally this is the
378 * You can set this to:
379 * - -1 The same as attribute.
380 * - 0 Always call compare function, not tied to request attribute.
381 * - >0 Attribute to compare with. For example, PW_GROUP in a check
382 * item needs to be compared with PW_USER_NAME in the incoming request.
383 * @param func comparison function
384 * @param instance argument to comparison function
387 int paircompare_register(unsigned int attribute, int other_attr,
388 RAD_COMPARE_FUNC func, void *instance)
392 paircompare_unregister(attribute, func);
394 c = rad_malloc(sizeof(struct cmp));
397 c->attribute = attribute;
398 c->otherattr = other_attr;
399 c->instance = instance;
406 /** Unregister comparison function for an attribute
408 * @param attribute attribute to unregister for.
409 * @param func comparison function to remove.
412 void paircompare_unregister(unsigned int attribute, RAD_COMPARE_FUNC func)
414 struct cmp *c, *last;
417 for (c = cmp; c; c = c->next) {
418 if (c->attribute == attribute && c->compare == func) {
424 if (c == NULL) return;
427 last->next = c->next;
435 /** Unregister comparison function for a module
437 * All paircompare() functions for this module will be unregistered.
439 * @param instance the module instance
442 void paircompare_unregister_instance(void *instance)
444 struct cmp *c, **tail;
447 while ((c = *tail) != NULL) {
448 if (c->instance == instance) {
458 /** Compare two pair lists except for the password information.
460 * For every element in "check" at least one matching copy must be present
463 * @param[in] request Current request.
464 * @param[in] req_list request valuepairs.
465 * @param[in] check Check/control valuepairs.
466 * @param[in,out] rep_list Reply value pairs.
468 * @return 0 on match.
470 int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
471 VALUE_PAIR **rep_list)
473 VALUE_PAIR *check_item;
474 VALUE_PAIR *auth_item;
480 for (check_item = check;
482 check_item = check_item->next) {
484 * If the user is setting a configuration value,
485 * then don't bother comparing it to any attributes
486 * sent to us by the user. It ALWAYS matches.
488 if ((check_item->op == T_OP_SET) ||
489 (check_item->op == T_OP_ADD)) {
493 if (!check_item->da->vendor) switch (check_item->da->attr) {
495 * Attributes we skip during comparison.
496 * These are "server" check items.
498 case PW_CRYPT_PASSWORD:
502 case PW_SESSION_TYPE:
503 case PW_STRIP_USER_NAME:
508 * IF the password attribute exists, THEN
509 * we can do comparisons against it. If not,
510 * then the request did NOT contain a
511 * User-Password attribute, so we CANNOT do
512 * comparisons against it.
514 * This hack makes CHAP-Password work..
516 case PW_USER_PASSWORD:
517 if (check_item->op == T_OP_CMP_EQ) {
518 DEBUGW("Found User-Password == \"...\".");
519 DEBUGW("Are you sure you don't mean Cleartext-Password?");
520 DEBUGW("See \"man rlm_pap\" for more information.");
522 if (pairfind(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
529 * See if this item is present in the request.
531 other = otherattr(check_item->da->attr);
533 auth_item = req_list;
536 while (auth_item != NULL) {
537 if ((auth_item->da->attr ==
538 (unsigned int) other) ||
542 auth_item = auth_item->next;
547 * Not found, it's not a match.
549 if (auth_item == NULL) {
551 * Didn't find it. If we were *trying*
552 * to not find it, then we succeeded.
554 if (check_item->op == T_OP_CMP_FALSE) {
562 * Else we found it, but we were trying to not
563 * find it, so we failed.
565 if (check_item->op == T_OP_CMP_FALSE) {
571 * We've got to xlat the string before doing
574 radius_xlat_do(request, check_item);
577 * OK it is present now compare them.
579 compare = radius_callback_compare(request, auth_item,
580 check_item, check, rep_list);
582 switch (check_item->op) {
585 radlog(L_INFO, "Invalid operator for item %s: "
586 "reverting to '=='", check_item->da->name);
591 if (compare != 0) result = -1;
595 if (compare == 0) result = -1;
599 if (compare >= 0) result = -1;
603 if (compare <= 0) result = -1;
607 if (compare > 0) result = -1;
611 if (compare < 0) result = -1;
617 if (compare != 0) result = -1;
620 } /* switch over the operator of the check item */
623 * This attribute didn't match, but maybe there's
624 * another of the same attribute, which DOES match.
626 if ((result != 0) && (other >= 0)) {
627 auth_item = auth_item->next;
632 } /* for every entry in the check item list */
637 /** Expands an attribute marked with pairmark_xlat
639 * Writes the new value to the vp.
641 * @param request Current request.
642 * @param vp to expand.
643 * @return 0 if successful else -1 (on xlat failure) or -2 (on parse failure).
644 * On failure pair will still no longer be marked for xlat expansion.
646 int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
652 if (vp->type != VT_XLAT) return 0;
656 len = radius_xlat(buffer, sizeof(buffer), request, vp->value.xlat, NULL, NULL);
659 rad_const_free(vp->value.xlat);
660 vp->value.xlat = NULL;
666 * Parse the string into a new value.
668 if (!pairparsevalue(vp, buffer)){
675 /** Move pairs, replacing/over-writing them, and doing xlat.
677 * Move attributes from one list to the other if not already present.
679 void radius_xlat_move(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR **from)
681 VALUE_PAIR **tailto, *i, *j, *next;
682 VALUE_PAIR *tailfrom = NULL;
686 * Point "tailto" to the end of the "to" list.
689 for (i = *to; i; i = i->next) {
694 * Loop over the "from" list.
696 for (i = *from; i; i = next) {
700 * Don't move 'fallthrough' over.
702 if (!i->da->vendor && i->da->attr == PW_FALL_THROUGH) {
708 * We've got to xlat the string before moving
711 radius_xlat_do(request, i);
713 found = pairfind(*to, i->da->attr, i->da->vendor, TAG_ANY);
717 * If a similar attribute is found,
720 case T_OP_SUB: /* -= */
722 if (!i->vp_strvalue[0] ||
723 (strcmp((char *)found->vp_strvalue,
724 (char *)i->vp_strvalue) == 0)) {
725 pairdelete(to, found->da->attr,
730 * 'tailto' may have been
734 for (j = *to; j; j = j->next) {
744 * Add it, if it's not already there.
746 case T_OP_EQ: /* = */
749 continue; /* with the loop */
754 * If a similar attribute is found,
755 * replace it with the new one. Otherwise,
756 * add the new one to the list.
758 case T_OP_SET: /* := */
763 memcpy(found, i, sizeof(*found));
771 * FIXME: Add support for <=, >=, <, >
773 * which will mean (for integers)
774 * 'make the attribute the smaller, etc'
778 * Add the new element to the list, even
779 * if similar ones already exist.
782 case T_OP_ADD: /* += */
787 tailfrom->next = next;
793 * If ALL of the 'to' attributes have been deleted,
794 * then ensure that the 'tail' is updated to point
805 } /* loop over the 'from' list */
808 /** Create a VALUE_PAIR and add it to a list of VALUE_PAIR s
810 * @note This function ALWAYS returns. If we're OOM, then it causes the
811 * @note server to exit, so you don't need to check the return value.
813 * @param[in] request Current request.
814 * @param[out] vps List to add new VALUE_PAIR to, if NULL will just
816 * @param[in] attribute number.
817 * @param[in] vendor number.
818 * @return a new VLAUE_PAIR or causes server to exit on error.
820 VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
821 unsigned int attribute, unsigned int vendor)
826 * FIXME: the context should ideally be the packet...
828 vp = paircreate(request, attribute, vendor);
831 rad_assert("No memory" == NULL);
835 if (vps) pairadd(vps, vp);
840 /** Print a single valuepair to stderr or error log.
842 * @param[in] vp list to print.
844 void debug_pair(VALUE_PAIR *vp)
846 if (!vp || !debug_flag || !fr_log_fp) return;
848 vp_print(fr_log_fp, vp);
851 /** Print a list of valuepairs to stderr or error log.
853 * @param[in] vp to print.
855 void debug_pair_list(VALUE_PAIR *vp)
857 if (!vp || !debug_flag || !fr_log_fp) return;
861 * Take this opportunity to verify all the VALUE_PAIRs are still valid.
863 if (!talloc_get_type(vp, VALUE_PAIR)) {
864 ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
866 log_talloc_report(vp);
870 vp_print(fr_log_fp, vp);
876 /** Print a list of valuepairs to the request list.
878 * @param[in] level Debug level (1-4).
879 * @param[in] request to read logging params from.
880 * @param[in] vp to print.
882 void rdebug_pair_list(int level, REQUEST *request, VALUE_PAIR *vp)
885 if (!vp || !request || !request->radlog) return;
889 * Take this opportunity to verify all the VALUE_PAIRs are still valid.
891 if (!talloc_get_type(vp, VALUE_PAIR)) {
892 RDEBUGE("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
894 log_talloc_report(vp);
898 vp_prints(buffer, sizeof(buffer), vp);
900 request->radlog(L_DBG, level, request, "\t%s", buffer);
905 /** Resolve attribute pair_lists_t value to an attribute list.
907 * The value returned is a pointer to the pointer of the HEAD of the list
908 * in the REQUEST. If the head of the list changes, the pointer will still
911 * @param[in] request containing the target lists.
912 * @param[in] list pair_list_t value to resolve to VALUE_PAIR list.
913 * Will be NULL if list name couldn't be resolved.
915 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
917 if (!request) return NULL;
920 case PAIR_LIST_UNKNOWN:
924 case PAIR_LIST_REQUEST:
925 return &request->packet->vps;
927 case PAIR_LIST_REPLY:
928 return &request->reply->vps;
930 case PAIR_LIST_CONTROL:
931 return &request->config_items;
934 case PAIR_LIST_PROXY_REQUEST:
935 if (!request->proxy) break;
936 return &request->proxy->vps;
938 case PAIR_LIST_PROXY_REPLY:
939 if (!request->proxy) break;
940 return &request->proxy_reply->vps;
945 (request->coa->proxy->code == PW_COA_REQUEST)) {
946 return &request->coa->proxy->vps;
950 case PAIR_LIST_COA_REPLY:
951 if (request->coa && /* match reply with request */
952 (request->coa->proxy->code == PW_COA_REQUEST) &&
953 request->coa->proxy_reply) {
954 return &request->coa->proxy_reply->vps;
960 (request->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
961 return &request->coa->proxy->vps;
965 case PAIR_LIST_DM_REPLY:
966 if (request->coa && /* match reply with request */
967 (request->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
968 request->coa->proxy_reply) {
969 return &request->coa->proxy->vps;
975 RDEBUG2W("List \"%s\" is not available",
976 fr_int2str(pair_lists, list, "<INVALID>"));
982 /** Release memory allocated to value pair template.
984 * @param[in,out] tmpl to free.
986 void radius_tmplfree(value_pair_tmpl_t **tmpl)
988 if (*tmpl == NULL) return;
990 dict_attr_free(&((*tmpl)->da));
997 /** Parse qualifiers to convert attrname into a value_pair_tmpl_t.
999 * VPTs are used in various places where we need to pre-parse configuration
1000 * sections into attribute mappings.
1002 * Note: name field is just a copy of the input pointer, if you know that
1003 * string might be freed before you're done with the vpt use radius_attr2tmpl
1006 * @param[in] name attribute name including qualifiers.
1007 * @param[out] vpt to modify.
1008 * @param[in] request_def The default request to insert unqualified
1010 * @param[in] list_def The default list to insert unqualified attributes into.
1011 * @return -1 on error or 0 on success.
1013 int radius_parse_attr(char const *name, value_pair_tmpl_t *vpt,
1014 request_refs_t request_def,
1015 pair_lists_t list_def)
1017 const DICT_ATTR *da;
1021 memset(vpt, 0, sizeof(*vpt));
1025 vpt->request = radius_request_name(&p, request_def);
1027 if (vpt->request == REQUEST_UNKNOWN) {
1028 ERROR("Invalid request qualifier \"%.*s\"", (int) len, name);
1034 vpt->list = radius_list_name(&p, list_def);
1035 if (vpt->list == PAIR_LIST_UNKNOWN) {
1037 ERROR("Invalid list qualifier \"%.*s\"", (int) len, name);
1043 vpt->type = VPT_TYPE_LIST;
1048 da = dict_attrbyname(p);
1050 da = dict_attrunknownbyname(p, FALSE);
1055 vpt->type = VPT_TYPE_ATTR;
1060 /** Parse qualifiers to convert attrname into a value_pair_tmpl_t.
1062 * VPTs are used in various places where we need to pre-parse configuration
1063 * sections into attribute mappings.
1065 * @param[in] ctx for talloc
1066 * @param[in] name attribute name including qualifiers.
1067 * @param[in] request_def The default request to insert unqualified
1069 * @param[in] list_def The default list to insert unqualified attributes into.
1070 * @return pointer to a value_pair_tmpl_t struct (must be freed with
1071 * radius_tmplfree) or NULL on error.
1073 value_pair_tmpl_t *radius_attr2tmpl(TALLOC_CTX *ctx, char const *name,
1074 request_refs_t request_def,
1075 pair_lists_t list_def)
1077 value_pair_tmpl_t *vpt;
1080 vpt = talloc(ctx, value_pair_tmpl_t); /* parse_attr zeroes it */
1081 copy = talloc_strdup(vpt, name);
1083 if (radius_parse_attr(copy, vpt, request_def, list_def) < 0) {
1084 radius_tmplfree(&vpt);
1091 /** Convert module specific attribute id to value_pair_tmpl_t.
1093 * @param[in] ctx for talloc
1094 * @param[in] name string to convert.
1095 * @param[in] type Type of quoting around value.
1096 * @return pointer to new VPT.
1098 value_pair_tmpl_t *radius_str2tmpl(TALLOC_CTX *ctx, char const *name, FR_TOKEN type)
1100 value_pair_tmpl_t *vpt;
1102 vpt = talloc_zero(ctx, value_pair_tmpl_t);
1103 vpt->name = talloc_strdup(vpt, name);
1108 case T_SINGLE_QUOTED_STRING:
1109 vpt->type = VPT_TYPE_LITERAL;
1111 case T_DOUBLE_QUOTED_STRING:
1112 vpt->type = VPT_TYPE_XLAT;
1114 case T_BACK_QUOTED_STRING:
1115 vpt->type = VPT_TYPE_EXEC;
1125 /** Convert CONFIG_PAIR (which may contain refs) to value_pair_map_t.
1127 * Treats the left operand as an attribute reference
1128 * @verbatim<request>.<list>.<attribute>@endverbatim
1130 * Treatment of left operand depends on quotation, barewords are treated as
1131 * attribute references, double quoted values are treated as expandable strings,
1132 * single quoted values are treated as literal strings.
1134 * Return must be freed with talloc_free
1136 * @param[in] ctx for talloc
1137 * @param[in] cp to convert to map.
1138 * @param[in] dst_request_def The default request to insert unqualified
1140 * @param[in] dst_list_def The default list to insert unqualified attributes
1142 * @param[in] src_request_def The default request to resolve attribute
1144 * @param[in] src_list_def The default list to resolve unqualified attributes
1146 * @return value_pair_map_t if successful or NULL on error.
1148 value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp,
1149 request_refs_t dst_request_def,
1150 pair_lists_t dst_list_def,
1151 request_refs_t src_request_def,
1152 pair_lists_t src_list_def)
1154 value_pair_map_t *map;
1158 CONF_ITEM *ci = cf_pairtoitem(cp);
1160 if (!cp) return NULL;
1162 map = talloc_zero(ctx, value_pair_map_t);
1164 attr = cf_pair_attr(cp);
1165 value = cf_pair_value(cp);
1167 cf_log_err(ci, "Missing attribute value");
1172 map->dst = radius_attr2tmpl(map, attr, dst_request_def, dst_list_def);
1178 * Bare words always mean attribute references.
1180 type = cf_pair_value_type(cp);
1181 if (type == T_BARE_WORD) {
1182 if (*value == '&') {
1183 map->src = radius_attr2tmpl(map, value + 1, src_request_def, src_list_def);
1186 if (!isdigit((int) *value)) {
1187 map->src = radius_attr2tmpl(map, value, src_request_def, src_list_def);
1190 DEBUGW("%s[%d]: Please add '&' for attribute reference '%s = &%s'",
1191 cf_pair_filename(cp), cf_pair_lineno(cp),
1194 map->src = radius_str2tmpl(map, value, type);
1198 map->src = radius_str2tmpl(map, value, type);
1205 map->op = cf_pair_operator(cp);
1209 * Lots of sanity checks for insane people...
1213 * We don't support implicit type conversion
1215 if (map->dst->da && map->src->da &&
1216 (map->src->da->type != map->dst->da->type)) {
1217 cf_log_err(ci, "Attribute type mismatch");
1223 * What exactly where you expecting to happen here?
1225 if ((map->dst->type == VPT_TYPE_ATTR) &&
1226 (map->src->type == VPT_TYPE_LIST)) {
1227 cf_log_err(ci, "Can't copy list into an attribute");
1233 * Depending on the attribute type, some operators are
1236 if (map->dst->type == VPT_TYPE_ATTR) {
1237 if ((map->op != T_OP_EQ) &&
1238 (map->op != T_OP_CMP_EQ) &&
1239 (map->op != T_OP_ADD) &&
1240 (map->op != T_OP_SUB) &&
1241 (map->op != T_OP_LE) &&
1242 (map->op != T_OP_GE) &&
1243 (map->op != T_OP_CMP_FALSE) &&
1244 (map->op != T_OP_SET)) {
1245 cf_log_err(ci, "Invalid operator for attribute");
1250 switch (map->src->type)
1254 * Only += and -= operators are supported for list copy.
1263 cf_log_err(ci, "Operator \"%s\" not allowed "
1265 fr_int2str(fr_tokens, map->op,
1272 * @todo add support for exec expansion.
1275 cf_log_err(ci, "Exec values are not allowed");
1289 /** Convert an 'update' config section into an attribute map.
1291 * Uses 'name2' of section to set default request and lists.
1293 * @param[in] cs the update section
1294 * @param[out] head Where to store the head of the map.
1295 * @param[in] dst_list_def The default destination list, usually dictated by
1296 * the section the module is being called in.
1297 * @param[in] src_list_def The default source list, usually dictated by the
1298 * section the module is being called in.
1299 * @param[in] max number of mappings to process.
1300 * @return -1 on error, else 0.
1302 int radius_attrmap(CONF_SECTION *cs, value_pair_map_t **head,
1303 pair_lists_t dst_list_def, pair_lists_t src_list_def,
1306 char const *cs_list, *p;
1308 request_refs_t request_def = REQUEST_CURRENT;
1313 unsigned int total = 0;
1314 value_pair_map_t **tail, *map;
1323 * The first map has cs as the parent.
1324 * The rest have the previous map as the parent.
1328 ci = cf_sectiontoitem(cs);
1330 cs_list = p = cf_section_name2(cs);
1332 request_def = radius_request_name(&p, REQUEST_UNKNOWN);
1333 if (request_def == REQUEST_UNKNOWN) {
1334 cf_log_err(ci, "Default request specified "
1335 "in mapping section is invalid");
1339 dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN);
1340 if (dst_list_def == PAIR_LIST_UNKNOWN) {
1341 cf_log_err(ci, "Default list \"%s\" specified "
1342 "in mapping section is invalid", p);
1347 for (ci = cf_item_find_next(cs, NULL);
1349 ci = cf_item_find_next(cs, ci)) {
1350 if (total++ == max) {
1351 cf_log_err(ci, "Map size exceeded");
1355 if (!cf_item_is_pair(ci)) {
1356 cf_log_err(ci, "Entry is not in \"attribute = "
1361 cp = cf_itemtopair(ci);
1362 map = radius_cp2map(ctx, cp, request_def, dst_list_def,
1363 REQUEST_CURRENT, src_list_def);
1369 tail = &(map->next);
1380 /** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
1382 * Takes a single value_pair_map_t, resolves request and list identifiers
1383 * to pointers in the current request, then attempts to retrieve module
1384 * specific value(s) using callback, and adds the resulting values to the
1385 * correct request/list.
1387 * @param request The current request.
1388 * @param map specifying destination attribute and location and src identifier.
1389 * @param func to retrieve module specific values and convert them to
1391 * @param ctx to be passed to func.
1392 * @param src name to be used in debugging if different from map value.
1393 * @return -1 if either attribute or qualifier weren't valid in this context
1394 * or callback returned NULL pointer, else 0.
1396 int radius_map2request(REQUEST *request, value_pair_map_t const *map,
1397 UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx)
1399 VALUE_PAIR **list, *vp, *head;
1400 char buffer[MAX_STRING_LEN];
1402 if (radius_request(&request, map->dst->request) < 0) {
1403 RDEBUGW("Mapping \"%s\" -> \"%s\" "
1404 "invalid in this context, skipping!",
1405 map->src->name, map->dst->name);
1410 list = radius_list(request, map->dst->list);
1412 RDEBUGW("Mapping \"%s\" -> \"%s\" "
1413 "invalid in this context, skipping!",
1414 map->src->name, map->dst->name);
1419 head = func(request, map, ctx);
1424 if (debug_flag) for (vp = head; vp != NULL; vp = vp->next) {
1425 rad_assert(vp->op == map->op);
1427 vp_prints_value(buffer, sizeof(buffer), vp, 1);
1429 RDEBUG("\t\t%s %s %s", map->dst->name,
1430 fr_int2str(fr_tokens, vp->op, "?unknown?"),
1435 * Use pairmove so the operator is respected
1437 radius_pairmove(request, list, head);
1441 /** Convert a map to a VALUE_PAIR.
1443 * @param[in] request structure (used only for talloc)
1444 * @param[in] map the map. The LHS has to be VPT_TYPE_ATTR.
1445 * @param[in] ctx unused
1446 * @return the newly allocated VALUE_PAIR
1448 VALUE_PAIR *radius_map2vp(REQUEST *request, value_pair_map_t const *map,
1451 VALUE_PAIR *vp, *found, **from = NULL;
1454 rad_assert(request != NULL);
1455 rad_assert(map != NULL);
1456 rad_assert(map->dst->type == VPT_TYPE_ATTR);
1457 rad_assert(map->dst->da != NULL);
1459 vp = pairalloc(request, map->dst->da);
1460 if (!vp) return NULL;
1467 switch (map->src->type) {
1470 * Don't call unnecessary expansions
1472 if (strchr(map->src->name, '%') != NULL) {
1476 slen = radius_axlat(&str, request, map->src->name, NULL, NULL);
1477 if (slen < 0) goto error;
1479 if (!pairparsevalue(vp, str)) {
1487 case VPT_TYPE_LITERAL:
1488 if (!pairparsevalue(vp, map->src->name)) goto error;
1492 rad_assert(map->src->da->type == map->dst->da->type);
1495 if (radius_request(&context, map->src->request) == 0) {
1496 from = radius_list(context, map->src->list);
1500 * Can't add the attribute if the list isn't
1503 if (!from) goto error;
1506 * FIXME: allow tag references?
1508 found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
1510 RDEBUGW("\"%s\" not found, skipping",
1516 * Copy the data over verbatim, assuming it's
1519 rad_assert(found->type == VT_DATA);
1520 memcpy(&vp->data, &found->data, found->length);
1521 vp->length = found->length;
1535 /** Convert a valuepair string to VALUE_PAIR and insert it into a list
1537 * Takes a valuepair string with list and request qualifiers, converts it into a VALUE_PAIR
1538 * and inserts it into the appropriate list.
1540 * @param request Current request.
1541 * @param raw string to parse.
1542 * @param request_def to use if attribute isn't qualified.
1543 * @param list_def to use if attribute isn't qualified.
1544 * @return 0 on success, -1 on error.
1546 int radius_str2vp(REQUEST *request, char const *raw, request_refs_t request_def, pair_lists_t list_def)
1553 VALUE_PAIR *vp = NULL;
1558 req = radius_request_name(&p, request_def);
1560 if (req == REQUEST_UNKNOWN) {
1561 RDEBUGE("Invalid request qualifier \"%.*s\"", (int) len, raw);
1567 list = radius_list_name(&p, list_def);
1568 if (list == PAIR_LIST_UNKNOWN) {
1571 RDEBUGE("Invalid list qualifier \"%.*s\"", (int) len, raw);
1577 if (radius_request(&request, req) < 0) {
1581 vps = radius_list(request, list);
1586 if (userparse(request, raw, &vp) == T_OP_INVALID) {
1590 pairmove(request, vps, &vp);
1595 /** Return a VP from the specified request.
1597 * @param request current request.
1598 * @param name attribute name including qualifiers.
1599 * @param vp_p where to write the pointer to the resolved VP.
1600 * Will be NULL if the attribute couldn't be resolved.
1601 * @return -1 if either the attribute or qualifier were invalid, else 0
1603 int radius_get_vp(REQUEST *request, char const *name, VALUE_PAIR **vp_p)
1605 value_pair_tmpl_t vpt;
1610 if (radius_parse_attr(name, &vpt, REQUEST_CURRENT,
1611 PAIR_LIST_REQUEST) < 0) {
1615 if (radius_request(&request, vpt.request) < 0) {
1619 vps = radius_list(request, vpt.list);
1627 * May not may not be found, but it *is* a known name.
1630 *vp_p = pairfind(*vps, vpt.da->attr, vpt.da->vendor, TAG_ANY);
1646 /** Add a module failure message VALUE_PAIR to the request
1648 void module_failure_msg(REQUEST *request, char const *fmt, ...)
1655 vp = paircreate(request->packet, PW_MODULE_FAILURE_MESSAGE, 0);
1661 len = snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s: ", request->module);
1663 DIAG_OFF(format-nonliteral)
1664 vsnprintf(vp->vp_strvalue + len, sizeof(vp->vp_strvalue) - len, fmt, ap);
1665 DIAG_ON(format-nonliteral)
1666 pairadd(&request->packet->vps, vp);