const char *vp_print_name(char *buffer, size_t bufsize, int attr) { int vendor; size_t len = 0; if (!buffer) return NULL; vendor = VENDOR(attr); if (vendor) { DICT_VENDOR *v; v = dict_vendorbyvalue(vendor); if (v) { snprintf(buffer, bufsize, "%s-", v->name); } else { snprintf(buffer, bufsize, "Vendor-%u-", vendor); } len = strlen(buffer); if (len == bufsize) { return NULL; } } snprintf(buffer + len, bufsize - len, "Attr-%u", attr & 0xffff); len += strlen(buffer + len); if (len == bufsize) { return NULL; } return buffer; }
/* * Create a VALUE_PAIR from an ASCII attribute and value, * where the attribute name is in the form: * * Attr-%d * Vendor-%d-Attr-%d * VendorName-Attr-%d */ static VALUE_PAIR *pairmake_any(const char *attribute, const char *value, int operator) { int attr, vendor; size_t size; const char *p = attribute; char *q; VALUE_PAIR *vp; /* * Unknown attributes MUST be of type 'octets' */ if (value && (strncasecmp(value, "0x", 2) != 0)) { fr_strerror_printf("Unknown attribute \"%s\" requires a hex string, not \"%s\"", attribute, value); return NULL; } vendor = 0; /* * Pull off vendor prefix first. */ if (strncasecmp(p, "Attr-", 5) != 0) { if (strncasecmp(p, "Vendor-", 7) == 0) { vendor = (int) strtol(p + 7, &q, 10); if ((vendor == 0) || (vendor > 65535)) { fr_strerror_printf("Invalid vendor value in attribute name \"%s\"", attribute); return NULL; } p = q; } else { /* must be vendor name */ char buffer[256]; q = strchr(p, '-'); if (!q) { fr_strerror_printf("Invalid vendor name in attribute name \"%s\"", attribute); return NULL; } if ((size_t) (q - p) >= sizeof(buffer)) { fr_strerror_printf("Vendor name too long in attribute name \"%s\"", attribute); return NULL; } memcpy(buffer, p, (q - p)); buffer[q - p] = '\0'; vendor = dict_vendorbyname(buffer); if (!vendor) { fr_strerror_printf("Unknown vendor name in attribute name \"%s\"", attribute); return NULL; } p = q; } if (*p != '-') { fr_strerror_printf("Invalid text following vendor definition in attribute name \"%s\"", attribute); return NULL; } p++; } /* * Attr-%d */ if (strncasecmp(p, "Attr-", 5) != 0) { fr_strerror_printf("Invalid format in attribute name \"%s\"", attribute); return NULL; } attr = strtol(p + 5, &q, 10); /* * Invalid, or trailing text after number. */ if ((attr == 0) || *q) { fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute); return NULL; } /* * Double-check the size of attr. */ if (vendor) { DICT_VENDOR *dv = dict_vendorbyvalue(vendor); if (!dv) { if (attr > 255) { attr_error: fr_strerror_printf("Invalid attribute number in attribute name \"%s\"", attribute); return NULL; } } else switch (dv->type) { case 1: if (attr > 255) goto attr_error; break; case 2: if (attr > 65535) goto attr_error; break; case 4: /* Internal limitations! */ if (attr > 65535) goto attr_error; break; default: fr_strerror_printf("Internal sanity check failed"); return NULL; } } attr |= vendor << 16; /* * We've now parsed the attribute properly, Let's create * it. This next stop also looks the attribute up in the * dictionary, and creates the appropriate type for it. */ if ((vp = paircreate(attr, PW_TYPE_OCTETS)) == NULL) { fr_strerror_printf("out of memory"); return NULL; } vp->operator = (operator == 0) ? T_OP_EQ : operator; if (!value) return vp; size = strlen(value + 2); /* * We may be reading something like Attr-5. i.e. * who-ever wrote the text didn't understand it, but we * do. */ switch (vp->type) { default: if (size == (vp->length * 2)) break; vp->type = PW_TYPE_OCTETS; /* FALL-THROUGH */ case PW_TYPE_OCTETS: case PW_TYPE_ABINARY: vp->length = size >> 1; if (vp->length > sizeof(vp->vp_octets)) { vp->length = sizeof(vp->vp_octets); } break; case PW_TYPE_STRING: vp->length = size >> 1; memset(&vp->vp_strvalue, 0, sizeof(vp->vp_strvalue)); if (vp->length >= sizeof(vp->vp_strvalue)) { vp->length = sizeof(vp->vp_strvalue) - 1; } break; } if (fr_hex2bin(value + 2, vp->vp_octets, size) != vp->length) { fr_strerror_printf("Invalid hex string"); free(vp); return NULL; } /* * Move contents around based on type. This is * to work around the historical use of "lvalue". */ switch (vp->type) { case PW_TYPE_DATE: case PW_TYPE_IPADDR: case PW_TYPE_INTEGER: memcpy(&vp->lvalue, vp->vp_octets, sizeof(vp->lvalue)); vp->vp_strvalue[0] = '\0'; break; default: break; } return vp; }
/** Print out attribute info * * Prints out all instances of a current attribute, or all attributes in a list. * * At higher debugging levels, also prints out alternative decodings of the same * value. This is helpful to determine types for unknown attributes of long * passed vendors, or just crazy/broken NAS. * * It's also useful for exposing issues in the packet decoding functions, as in * some cases they get fed random garbage data. * * This expands to a zero length string. */ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, UNUSED size_t outlen) { VALUE_PAIR *vp, **vps; REQUEST *current; value_pair_tmpl_t vpt; vp_cursor_t cursor; char buffer[1024]; if (!RDEBUG_ENABLED2) { *out = '\0'; return -1; } while (isspace((int) *fmt)) fmt++; if (*fmt == '&') fmt++; if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { return -1; } current = request; if (radius_request(¤t, vpt.request) < 0) return -2; vps = radius_list(current, vpt.list); if (!vps) { return -2; } RIDEBUG("Attributes matching \"%s\"", fmt); vp = fr_cursor_init(&cursor, vps); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } while (vp) { DICT_ATTR *dac = NULL; DICT_VENDOR *dv; VALUE_PAIR *vpc = NULL; FR_NAME_NUMBER const *type; vp_prints_value(buffer, sizeof(buffer), vp, '\''); if (vp->da->flags.has_tag) { RIDEBUG2("\t%s:%s:%i %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, vp->tag, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } else { RIDEBUG2("\t%s:%s %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } if (!RDEBUG_ENABLED3) { goto next_vp; } if (vp->da->vendor) { dv = dict_vendorbyvalue(vp->da->vendor); RDEBUG3("\t\tvendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown"); } RDEBUG3("\t\ttype : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>")); RDEBUG3("\t\tlength : %zu", vp->length); dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR)); if (!dac) { return -1; } dac->flags.vp_free = 0; if (!RDEBUG_ENABLED4) { goto next_vp; } type = dict_attr_types; while (type->name) { int pad; ssize_t len; uint8_t const *data = NULL; vpc = NULL; if ((PW_TYPE) type->number == vp->da->type) { goto next_type; } switch (type->number) { case PW_TYPE_INVALID: /* Not real type */ case PW_TYPE_MAX: /* Not real type */ case PW_TYPE_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_LONG_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_TLV: /* Not safe/appropriate */ case PW_TYPE_VSA: /* @fixme We need special behaviour for these */ goto next_type; default: break; } dac->type = type->number; len = rad_vp2data(&data, vp); if (len < 0) { goto next_type; } if (data2vp(NULL, NULL, NULL, dac, data, len, len, &vpc) < 0) { goto next_type; } /* * data2vp has knowledge of expected format lengths, if the length * from rad_vp2data doesn't match, it encodes the attribute * as raw octets. This results in many useless debug lines with * the same hex string. */ if ((type->number != PW_TYPE_OCTETS) && (vpc->da->type == PW_TYPE_OCTETS)) { goto next_type; } if (!vp_prints_value(buffer, sizeof(buffer), vpc, '\'')) { goto next_type; } if ((pad = (11 - strlen(type->name))) < 0) { pad = 0; } /* * @fixme: if the value happens to decode as a VSA * (someone put a VSA into a VSA?), we probably to print * extended info for that/reparse */ RDEBUG4("\t\tas %s%*s: %s", type->name, pad, " ", buffer); next_type: talloc_free(vpc); type++; } next_vp: talloc_free(dac); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } else { vp = fr_cursor_next(&cursor); } } *out = '\0'; return 0; }
/* * Add an attribute to the dictionary. */ int dict_addattr(const char *name, int vendor, int type, int value, ATTR_FLAGS flags) { size_t namelen; static int max_attr = 0; DICT_ATTR *attr; namelen = strlen(name); if (namelen >= DICT_ATTR_MAX_NAME_LEN) { fr_strerror_printf("dict_addattr: attribute name too long"); return -1; } /* * If the value is '-1', that means use a pre-existing * one (if it already exists). If one does NOT already exist, * then create a new attribute, with a non-conflicting value, * and use that. */ if (value == -1) { if (dict_attrbyname(name)) { return 0; /* exists, don't add it again */ } value = ++max_attr; } else if (vendor == 0) { /* * Update 'max_attr' */ if (value > max_attr) { max_attr = value; } } if (value < 0) { fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (less than zero)"); return -1; } if (value >= 65536) { fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (larger than 65535)."); return -1; } if (vendor) { DICT_VENDOR *dv; static DICT_VENDOR *last_vendor = NULL; if (flags.is_tlv && (flags.encrypt != FLAG_ENCRYPT_NONE)) { fr_strerror_printf("Sub-TLV's cannot be encrypted"); return -1; } if (flags.has_tlv && (flags.encrypt != FLAG_ENCRYPT_NONE)) { fr_strerror_printf("TLV's cannot be encrypted"); return -1; } if (flags.is_tlv && flags.has_tag) { fr_strerror_printf("Sub-TLV's cannot have a tag"); return -1; } if (flags.has_tlv && flags.has_tag) { fr_strerror_printf("TLV's cannot have a tag"); return -1; } /* * Most ATTRIBUTEs are bunched together by * VENDOR. We can save a lot of lookups on * dictionary initialization by caching the last * vendor. */ if (last_vendor && (vendor == last_vendor->vendorpec)) { dv = last_vendor; } else { dv = dict_vendorbyvalue(vendor); last_vendor = dv; } /* * If the vendor isn't defined, die. */ if (!dv) { fr_strerror_printf("dict_addattr: Unknown vendor"); return -1; } /* * FIXME: Switch over dv->type, and limit things * properly. */ if ((dv->type == 1) && (value >= 256) && !flags.is_tlv) { fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (larger than 255)."); return -1; } /* else 256..65535 are allowed */ } /* * Create a new attribute for the list */ if ((attr = fr_pool_alloc(sizeof(*attr) + namelen)) == NULL) { fr_strerror_printf("dict_addattr: out of memory"); return -1; } memcpy(attr->name, name, namelen); attr->name[namelen] = '\0'; attr->attr = value; attr->attr |= (vendor << 16); /* FIXME: hack */ attr->vendor = vendor; attr->type = type; attr->flags = flags; attr->vendor = vendor; /* * Insert the attribute, only if it's not a duplicate. */ if (!fr_hash_table_insert(attributes_byname, attr)) { DICT_ATTR *a; /* * If the attribute has identical number, then * ignore the duplicate. */ a = fr_hash_table_finddata(attributes_byname, attr); if (a && (strcasecmp(a->name, attr->name) == 0)) { if (a->attr != attr->attr) { fr_strerror_printf("dict_addattr: Duplicate attribute name %s", name); fr_pool_free(attr); return -1; } /* * Same name, same vendor, same attr, * maybe the flags and/or type is * different. Let the new value * over-ride the old one. */ } fr_hash_table_delete(attributes_byvalue, a); if (!fr_hash_table_replace(attributes_byname, attr)) { fr_strerror_printf("dict_addattr: Internal error storing attribute %s", name); fr_pool_free(attr); return -1; } } /* * Insert the SAME pointer (not free'd when this entry is * deleted), into another table. * * We want this behaviour because we want OLD names for * the attributes to be read from the configuration * files, but when we're printing them, (and looking up * by value) we want to use the NEW name. */ if (!fr_hash_table_replace(attributes_byvalue, attr)) { fr_strerror_printf("dict_addattr: Failed inserting attribute name %s", name); return -1; } if (!vendor && (value > 0) && (value < 256)) { dict_base_attrs[value] = attr; } return 0; }
/* * Process the VENDOR command */ static int process_vendor(const char* fn, const int line, char **argv, int argc) { int value; int continuation = 0; const char *format = NULL; if ((argc < 2) || (argc > 3)) { fr_strerror_printf( "dict_init: %s[%d] invalid VENDOR entry", fn, line); return -1; } /* * Validate all entries */ if (!isdigit((int) argv[1][0])) { fr_strerror_printf("dict_init: %s[%d]: invalid value", fn, line); return -1; } value = atoi(argv[1]); /* Create a new VENDOR entry for the list */ if (dict_addvendor(argv[0], value) < 0) { char buffer[256]; strlcpy(buffer, fr_strerror(), sizeof(buffer)); fr_strerror_printf("dict_init: %s[%d]: %s", fn, line, buffer); return -1; } /* * Look for a format statement */ if (argc == 3) { format = argv[2]; } else if (value == VENDORPEC_USR) { /* catch dictionary screw-ups */ format = "format=4,0"; } else if (value == VENDORPEC_LUCENT) { format = "format=2,1"; } else if (value == VENDORPEC_STARENT) { format = "format=2,2"; } /* else no fixups to do */ if (format) { int type, length; const char *p; DICT_VENDOR *dv; if (strncasecmp(format, "format=", 7) != 0) { fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR. Expected \"format=\", got \"%s\"", fn, line, format); return -1; } p = format + 7; if ((strlen(p) < 3) || !isdigit((int) p[0]) || (p[1] != ',') || !isdigit((int) p[2]) || (p[3] && (p[3] != ','))) { fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR. Expected text like \"1,1\", got \"%s\"", fn, line, p); return -1; } type = (int) (p[0] - '0'); length = (int) (p[2] - '0'); if (p[3] == ',') { if ((p[4] != 'c') || (p[5] != '\0')) { fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR. Expected text like \"1,1\", got \"%s\"", fn, line, p); return -1; } continuation = 1; } dv = dict_vendorbyvalue(value); if (!dv) { fr_strerror_printf("dict_init: %s[%d]: Failed adding format for VENDOR", fn, line); return -1; } if ((type != 1) && (type != 2) && (type != 4)) { fr_strerror_printf("dict_init: %s[%d]: invalid type value %d for VENDOR", fn, line, type); return -1; } if ((length != 0) && (length != 1) && (length != 2)) { fr_strerror_printf("dict_init: %s[%d]: invalid length value %d for VENDOR", fn, line, length); return -1; } dv->type = type; dv->length = length; dv->flags = continuation; } return 0; }