2 * valuepair.c Functions to handle VALUE_PAIRs
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
27 #include <sys/types.h>
34 #include "libradius.h"
46 static const char *months[] = {
47 "jan", "feb", "mar", "apr", "may", "jun",
48 "jul", "aug", "sep", "oct", "nov", "dec" };
52 * Create a new valuepair.
54 VALUE_PAIR *paircreate(int attr, int type)
59 if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) {
60 librad_log("out of memory");
63 memset(vp, 0, sizeof(VALUE_PAIR));
65 vp->operator = T_OP_EQ;
69 * Dictionary type over-rides what the caller says.
71 if ((da = dict_attrbyvalue(attr)) != NULL) {
72 strcpy(vp->name, da->name);
74 vp->flags = da->flags;
75 } else if (VENDOR(attr) == 0) {
76 sprintf(vp->name, "Attr-%u", attr);
80 v = dict_vendorbyvalue(VENDOR(attr));
82 sprintf(vp->name, "%s-Attr-%u",
83 v->name, attr & 0xffff);
85 sprintf(vp->name, "Vendor-%u-Attr-%u",
86 VENDOR(attr), attr & 0xffff);
104 * release the memory used by a single attribute-value pair
105 * just a wrapper around free() for now.
107 void pairbasicfree(VALUE_PAIR *pair)
109 /* clear the memory here */
110 memset(pair, 0, sizeof(*pair));
115 * Release the memory used by a list of attribute-value
116 * pairs, and sets the pair pointer to NULL.
118 void pairfree(VALUE_PAIR **pair_ptr)
120 VALUE_PAIR *next, *pair;
122 if (!pair_ptr) return;
125 while (pair != NULL) {
136 * Find the pair with the matching attribute
138 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
140 while(first && first->attribute != attr)
147 * Delete the pair(s) with the matching attribute
149 void pairdelete(VALUE_PAIR **first, int attr)
151 VALUE_PAIR *i, *next;
152 VALUE_PAIR **last = first;
154 for(i = *first; i; i = next) {
156 if (i->attribute == attr) {
166 * Add a pair at the end of a VALUE_PAIR list.
168 void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
172 if (*first == NULL) {
176 for(i = *first; i->next; i = i->next)
182 * Add or replace a pair at the end of a VALUE_PAIR list.
184 void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
186 VALUE_PAIR *i, *next;
187 VALUE_PAIR **prev = first;
189 if (*first == NULL) {
195 * Not an empty list, so find item if it is there, and
196 * replace it. Note, we always replace the first one, and
197 * we ignore any others that might exist.
199 for(i = *first; i; i = next) {
203 * Found the first attribute, replace it,
206 if (i->attribute == replace->attribute) {
210 * Should really assert that replace->next == NULL
212 replace->next = next;
218 * Point to where the attribute should go.
224 * If we got here, we didn't find anything to replace, so
225 * stopped at the last item, which we just append to.
231 * Copy just a certain type of pairs.
233 VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
235 VALUE_PAIR *first, *n, **last;
241 if (attr >= 0 && vp->attribute != attr) {
245 if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
246 librad_log("out of memory");
249 memcpy(n, vp, sizeof(VALUE_PAIR));
262 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
264 return paircopy2(vp, -1);
269 * Move attributes from one list to the other
270 * if not already present.
272 void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
274 VALUE_PAIR **tailto, *i, *j, *next;
275 VALUE_PAIR *tailfrom = NULL;
277 int has_password = 0;
286 * First, see if there are any passwords here, and
287 * point "tailto" to the end of the "to" list.
290 for(i = *to; i; i = i->next) {
291 if (i->attribute == PW_PASSWORD ||
292 i->attribute == PW_CRYPT_PASSWORD)
298 * Loop over the "from" list.
300 for(i = *from; i; i = next) {
303 * If there was a password in the "to" list,
304 * do not move any other password from the
305 * "from" to the "to" list.
308 (i->attribute == PW_PASSWORD ||
309 i->attribute == PW_CRYPT_PASSWORD)) {
314 * If the attribute is already present in "to",
315 * do not move it from "from" to "to". We make
316 * an exception for "Hint" which can appear multiple
317 * times, and we never move "Fall-Through".
319 if (i->attribute == PW_FALL_THROUGH ||
320 (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
322 found = pairfind(*to, i->attribute);
323 switch (i->operator) {
326 * If a similar attribute is found,
329 case T_OP_SUB: /* -= */
331 if (!i->strvalue[0] ||
332 (strcmp((char *)found->strvalue,
333 (char *)i->strvalue) == 0)){
334 pairdelete(to, found->attribute);
337 * 'tailto' may have been
341 for(j = *to; j; j = j->next) {
350 /* really HAVE_REGEX_H */
353 * Attr-Name =~ "s/find/replace/"
355 * Very bad code. Barely working,
361 (i->strvalue[0] == 's')) {
369 q = strchr(p + 1, *p);
370 if (!q || (q[strlen(q) - 1] != *p)) {
374 str = strdup(i->strvalue + 2);
377 q[strlen(q) - 1] = '\0';
379 regcomp(®, str, 0);
380 if (regexec(®, found->strvalue,
382 fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
383 found->strvalue, match[0].rm_so,
390 tailfrom = i; /* don't copy it over */
394 case T_OP_EQ: /* = */
396 * FIXME: Tunnel attributes with
397 * different tags are different
402 continue; /* with the loop */
407 * If a similar attribute is found,
408 * replace it with the new one. Otherwise,
409 * add the new one to the list.
411 case T_OP_SET: /* := */
413 pairdelete(to, found->attribute);
415 * 'tailto' may have been
419 for(j = *to; j; j = j->next) {
426 * Add the new element to the list, even
427 * if similar ones already exist.
430 case T_OP_ADD: /* += */
435 tailfrom->next = next;
440 * If ALL of the 'to' attributes have been deleted,
441 * then ensure that the 'tail' is updated to point
456 * Move one kind of attributes from one list to the other
458 void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
460 VALUE_PAIR *to_tail, *i, *next;
461 VALUE_PAIR *iprev = NULL;
464 * Find the last pair in the "to" list and put it in "to_tail".
468 for(i = *to; i; i = i->next)
473 for(i = *from; i; i = next) {
478 * If the attribute to move is NOT a VSA, then it
479 * ignores any attributes which do not match exactly.
481 if ((attr != PW_VENDOR_SPECIFIC) &&
482 (i->attribute != attr)) {
488 * If the attribute to move IS a VSA, then it ignores
489 * any non-VSA attribute.
491 if ((attr == PW_VENDOR_SPECIFIC) &&
492 (VENDOR(i->attribute) == 0)) {
498 * Remove the attribute from the "from" list.
506 * Add the attribute to the "to" list.
519 * Sort of strtok/strsep function.
521 static char *mystrtok(char **ptr, const char *sep)
527 while (**ptr && strchr(sep, **ptr))
532 while (**ptr && strchr(sep, **ptr) == NULL)
540 * Turn printable string into time_t
541 * Returns -1 on error, 0 on OK.
543 static int gettime(const char *valstr, time_t *lvalue)
554 * Test for unix timestamp date
556 *lvalue = strtoul(valstr, &tail, 10);
562 memset(tm, 0, sizeof(*tm));
563 tm->tm_isdst = -1; /* don't know, and don't care about DST */
565 strNcpy(buf, valstr, sizeof(buf));
568 f[0] = mystrtok(&p, " \t");
569 f[1] = mystrtok(&p, " \t");
570 f[2] = mystrtok(&p, " \t");
571 f[3] = mystrtok(&p, " \t"); /* may, or may not, be present */
572 if (!f[0] || !f[1] || !f[2]) return -1;
575 * The month is text, which allows us to find it easily.
578 for (i = 0; i < 3; i++) {
579 if (isalpha( (int) *f[i])) {
581 * Bubble the month to the front of the list
587 for (i = 0; i < 12; i++) {
588 if (strncasecmp(months[i], f[0], 3) == 0) {
596 /* month not found? */
597 if (tm->tm_mon == 12) return -1;
600 * The year may be in f[1], or in f[2]
602 tm->tm_year = atoi(f[1]);
603 tm->tm_mday = atoi(f[2]);
605 if (tm->tm_year >= 1900) {
610 * We can't use 2-digit years any more, they make it
611 * impossible to tell what's the day, and what's the year.
613 if (tm->tm_mday < 1900) return -1;
616 * Swap the year and the day.
619 tm->tm_year = tm->tm_mday - 1900;
624 * If the day is out of range, die.
626 if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
631 * There may be %H:%M:%S. Parse it in a hacky way.
634 f[0] = f[3]; /* HH */
635 f[1] = strchr(f[0], ':'); /* find : separator */
636 if (!f[1]) return -1;
638 *(f[1]++) = '\0'; /* nuke it, and point to MM:SS */
640 f[2] = strchr(f[1], ':'); /* find : separator */
641 if (!f[2]) return -1;
642 *(f[2]++) = '\0'; /* nuke it, and point to SS */
644 tm->tm_hour = atoi(f[0]);
645 tm->tm_min = atoi(f[1]);
646 tm->tm_sec = atoi(f[2]);
650 * Returns -1 on error.
653 if (t == (time_t) -1) return -1;
661 * Parse a string value into a given VALUE_PAIR
663 VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
670 * Even for integers, dates and ip addresses we
671 * keep the original string in vp->strvalue.
673 strNcpy((char *)vp->strvalue, value, sizeof(vp->strvalue));
674 vp->length = strlen(vp->strvalue);
679 * Already handled above.
685 * FIXME: complain if hostname
686 * cannot be resolved, or resolve later!
688 if ((p = strrchr(value, '+')) != NULL && !p[1]) {
689 cs = s = strdup(value);
692 vp->flags.addport = 1;
697 vp->lvalue = librad_dodns ? ip_getaddr(cs) :
702 case PW_TYPE_INTEGER:
704 * If it starts with a digit, it must
705 * be a number (or a range).
707 * Note that ALL integers are unsigned!
709 if (strspn(value, "0123456789") == strlen(value)) {
710 vp->lvalue = (uint32_t) strtoul(value, NULL, 10);
714 * Look for the named value for the given
717 else if ((dval = dict_valbyname(vp->attribute, value)) == NULL) {
718 librad_log("Unknown value %s for attribute %s",
722 vp->lvalue = dval->value;
728 if (gettime(value, (time_t *)&vp->lvalue) < 0) {
729 librad_log("failed to parse time string "
735 case PW_TYPE_ABINARY:
737 if (strncasecmp(value, "0x", 2) == 0) {
738 vp->type = PW_TYPE_OCTETS;
743 * Special case to convert filter to binary
745 strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
746 if (ascend_parse_filter(vp) < 0 ) {
747 fprintf(stderr, "FUCK %s\n", value);
748 librad_log("failed to parse Ascend binary attribute: %s",
755 * If Ascend binary is NOT defined,
756 * then fall through to raw octets, so that
757 * the user can at least make them by hand...
761 /* raw octets: 0x01020304... */
763 if (strncasecmp(value, "0x", 2) == 0) {
771 * There is only one character,
774 if ((strlen(cp) & 0x01) != 0) {
775 librad_log("Hex string is not an even length string.");
780 while (*cp && vp->length < MAX_STRING_LEN) {
783 if (sscanf(cp, "%02x", &tmp) != 1) {
784 librad_log("Non-hex characters at %c%c", cp[0], cp[1]);
797 if (ifid_aton(value, vp->strvalue) == NULL) {
798 librad_log("failed to parse interface-id "
799 "string \"%s\"", value);
803 vp->strvalue[vp->length] = '\0';
806 case PW_TYPE_IPV6ADDR:
807 if (ipv6_addr(value, vp->strvalue) < 0) {
808 librad_log("failed to parse IPv6 address "
809 "string \"%s\"", value);
812 vp->length = 16; /* length of IPv6 address */
813 vp->strvalue[vp->length] = '\0';
819 librad_log("unknown attribute type %d", vp->type);
827 * Create a VALUE_PAIR from an ASCII attribute and value,
828 * where the attribute name is in the form:
833 static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
842 * Unknown attributes MUST be of type 'octets'
844 if (value && (strncasecmp(value, "0x", 2) != 0)) {
851 if (strncasecmp(attribute, "Attr-", 5) == 0) {
852 attr = atoi(attribute + 5);
854 p += strspn(p, "0123456789");
855 if (*p != 0) goto error;
861 } else if (strncasecmp(attribute, "Vendor-", 7) == 0) {
864 vendor = atoi(attribute + 7);
865 if ((vendor == 0) || (vendor > 65535)) goto error;
868 p += strspn(p, "0123456789");
871 * Not Vendor-%d-Attr-%d
873 if (strncasecmp(p, "-Attr-", 6) != 0) goto error;
878 p += strspn(p, "0123456789");
879 if (*p != 0) goto error;
881 if ((attr == 0) || (attr > 65535)) goto error;
883 attr |= (vendor << 16);
888 } else if (((p = strchr(attribute, '-')) != NULL) &&
889 (strncasecmp(p, "-Attr-", 6) == 0)) {
893 if ((p - attribute) >= sizeof(buffer)) goto error;
895 memcpy(buffer, attribute, p - attribute);
896 buffer[p - attribute] = '\0';
898 vendor = dict_vendorbyname(buffer);
899 if (vendor == 0) goto error;
904 p += strspn(p, "0123456789");
905 if (*p != 0) goto error;
907 if ((attr == 0) || (attr > 65535)) goto error;
909 attr |= (vendor << 16);
911 } else { /* very much unknown: die */
913 librad_log("Unknown attribute \"%s\"", attribute);
918 * We've now parsed the attribute properly, and verified
919 * it to have value 'octets'. Let's create it.
921 if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
922 librad_log("out of memory");
925 memset(vp, 0, sizeof(VALUE_PAIR));
926 vp->type = PW_TYPE_OCTETS;
929 * It may not be valid hex characters. If not, die.
931 if (pairparsevalue(vp, value) == NULL) {
937 * Dictionary type over-rides what the caller says.
938 * This "converts" the parsed value into the appropriate
941 * Also, normalize the name of the attribute...
943 * Much of this code is copied from paircreate()
945 if ((da = dict_attrbyvalue(attr)) != NULL) {
946 strcpy(vp->name, da->name);
948 vp->flags = da->flags;
951 * Sanity check the type for length. We don't
952 * want to look at attributes which are of the
957 case PW_TYPE_INTEGER:
958 case PW_TYPE_IPADDR: /* always kept in network byte order */
959 if (vp->length != 4) {
962 librad_log("Attribute has invalid length");
965 memcpy(&vp->lvalue, vp->strvalue, sizeof(vp->lvalue));
969 if (vp->length != 8) goto length_error;
972 case PW_TYPE_IPV6ADDR:
973 if (vp->length != 16) goto length_error;
977 case PW_TYPE_ABINARY:
978 if (vp->length != 32) goto length_error;
981 default: /* string, octets, etc. */
985 } else if (VENDOR(attr) == 0) {
986 sprintf(vp->name, "Attr-%u", attr);
988 sprintf(vp->name, "Vendor-%u-Attr-%u",
989 VENDOR(attr), attr & 0xffff);
992 vp->attribute = attr;
993 vp->operator = (operator == 0) ? T_OP_EQ : operator;
1001 * Create a VALUE_PAIR from an ASCII attribute and value.
1003 VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
1016 * Check for tags in 'Attribute:Tag' format.
1021 ts = strrchr( attribute, ':' );
1023 /* Colon found with something behind it */
1024 if (ts[1] == '*' && ts[2] == 0) {
1025 /* Wildcard tag for check items */
1028 } else if ((ts[1] >= '0') && (ts[1] <= '9')) {
1029 /* It's not a wild card tag */
1030 tag = strtol(ts + 1, &tc, 0);
1031 if (tc && !*tc && TAG_VALID_ZERO(tag))
1035 librad_log("Invalid tag for attribute %s", attribute);
1042 * It's not found in the dictionary, so we use
1043 * another method to create the attribute.
1045 if ((da = dict_attrbyname(attribute)) == NULL) {
1046 return pairmake_any(attribute, value, operator);
1049 if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
1050 librad_log("out of memory");
1054 memset(vp, 0, sizeof(VALUE_PAIR));
1055 vp->attribute = da->attr;
1056 vp->type = da->type;
1057 vp->operator = (operator == 0) ? T_OP_EQ : operator;
1058 strcpy(vp->name, da->name);
1059 vp->flags = da->flags;
1062 /* Check for a tag in the 'Merit' format of:
1063 * :Tag:Value. Print an error if we already found
1064 * a tag in the Attribute.
1067 if (value && (*value == ':' && da->flags.has_tag)) {
1068 /* If we already found a tag, this is invalid */
1071 librad_log("Duplicate tag %s for attribute %s",
1073 DEBUG("Duplicate tag %s for attribute %s\n",
1078 /* Colon found and attribute allows a tag */
1079 if (value[1] == '*' && value[2] == ':') {
1080 /* Wildcard tag for check items */
1085 tag = strtol(value + 1, &tc, 0);
1086 if (tc && *tc==':' && TAG_VALID_ZERO(tag))
1094 vp->flags.tag = tag;
1097 switch (vp->operator) {
1102 * For =* and !* operators, the value is irrelevant
1106 case T_OP_CMP_FALSE:
1107 vp->strvalue[0] = '\0';
1113 * Regular expression comparison of integer attributes
1114 * does a STRING comparison of the names of their
1115 * integer attributes.
1117 case T_OP_REG_EQ: /* =~ */
1118 case T_OP_REG_NE: /* !~ */
1119 if (vp->type == PW_TYPE_INTEGER) {
1124 * Regular expression match with no regular
1125 * expression is wrong.
1132 res = regcomp(&cre, value, REG_EXTENDED|REG_NOSUB);
1136 regerror(res, &cre, msg, sizeof(msg));
1137 librad_log("Illegal regular expression in attribute: %s: %s",
1144 librad_log("Regelar expressions not enabled in this build, error in attribute %s",
1151 if (value && (pairparsevalue(vp, value) == NULL)) {
1160 * Read a valuepair from a buffer, and advance pointer.
1161 * Sets *eol to T_EOL if end of line was encountered.
1163 VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
1169 LRAD_TOKEN token, t, xlat;
1174 /* Get attribute. */
1175 token = gettoken(ptr, attr, sizeof(attr));
1177 /* If it's a comment, then exit, as we haven't read a pair */
1178 if (token == T_HASH) {
1180 librad_log("Read a comment instead of a token");
1184 /* It's not a comment, so it MUST be an attribute */
1185 if ((token == T_EOL) ||
1187 librad_log("No token read where we expected an attribute name");
1191 /* Now we should have an '=' here. */
1192 token = gettoken(ptr, buf, sizeof(buf));
1193 if (token < T_EQSTART || token > T_EQEND) {
1194 librad_log("expecting '='");
1198 /* Read value. Note that empty string values are allowed */
1199 xlat = gettoken(ptr, value, sizeof(value));
1200 if (xlat == T_EOL) {
1201 librad_log("failed to get value");
1206 * Peek at the next token. Must be T_EOL, T_COMMA, or T_HASH
1209 t = gettoken(&p, buf, sizeof(buf));
1210 if (t != T_EOL && t != T_COMMA && t != T_HASH) {
1211 librad_log("Expected end of line or comma");
1222 * Make the full pair now.
1225 vp = pairmake(attr, value, token);
1231 case T_DOUBLE_QUOTED_STRING:
1232 p = strchr(value, '%');
1233 if (p && (p[1] == '{')) {
1234 vp = pairmake(attr, NULL, token);
1240 strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
1241 vp->flags.do_xlat = 1;
1244 vp = pairmake(attr, value, token);
1250 * Mark the pair to be allocated later.
1252 case T_BACK_QUOTED_STRING:
1253 vp = pairmake(attr, NULL, token);
1259 vp->flags.do_xlat = 1;
1260 strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
1266 * If we didn't make a pair, return an error.
1277 * Read one line of attribute/value pairs. This might contain
1278 * multiple pairs seperated by comma's.
1280 LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
1284 LRAD_TOKEN last_token = T_INVALID;
1285 LRAD_TOKEN previous_token;
1288 * We allow an empty line.
1295 previous_token = last_token;
1296 if ((vp = pairread(&p, &last_token)) == NULL) {
1299 pairadd(first_pair, vp);
1300 } while (*p && (last_token == T_COMMA));
1303 * Don't tell the caller that there was a comment.
1305 if (last_token == T_HASH) {
1306 return previous_token;
1310 * And return the last token which we read.
1316 * Read valuepairs from the fp up to End-Of-File.
1318 * Hmm... this function is only used by radclient..
1320 VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix)
1323 LRAD_TOKEN last_token = T_EOL;
1330 while (!error && fgets(buf, sizeof(buf), fp) != NULL) {
1332 * If we get a '\n' by itself, we assume that's
1333 * the end of that VP
1335 if ((buf[0] == '\n') && (list)) {
1338 if ((buf[0] == '\n') && (!list)) {
1343 * Comments get ignored
1345 if (buf[0] == '#') continue;
1348 * Read all of the attributes on the current line.
1351 last_token = userparse(buf, &vp);
1353 if (last_token != T_EOL) {
1354 librad_perror(errprefix);
1365 if (error) pairfree(&list);
1369 return error ? NULL: list;