We can encode / decode all non-TLV types without a problem.
TLVs are currently limited to one level (241.1.2), and to the
length of the encapsulating RADIUS attribute.
The "M" flag for extended attributes with flags is not supported.
$INCLUDE dictionary.rfc5580
$INCLUDE dictionary.rfc5607
$INCLUDE dictionary.rfc5904
+$INCLUDE dictionary.extended
#
# Include vendor dictionaries after the standard ones.
--- /dev/null
+ATTRIBUTE Extended-Attribute-1 241 octets extended
+ATTRIBUTE Extended-Attribute-2 242 octets extended
+ATTRIBUTE Extended-Attribute-3 243 octets extended
+ATTRIBUTE Extended-Attribute-4 244 octets extended
+ATTRIBUTE Extended-Attribute-5 245 octets extended-flags
+ATTRIBUTE Extended-Attribute-6 246 octets extended-flags
+
+ATTRIBUTE Extended-Vendor-Specific-1 241.26 octets
+ATTRIBUTE Extended-Vendor-Specific-2 242.26 octets
+ATTRIBUTE Extended-Vendor-Specific-3 243.26 octets
+ATTRIBUTE Extended-Vendor-Specific-4 244.26 octets
+ATTRIBUTE Extended-Vendor-Specific-5 245.26 octets
+ATTRIBUTE Extended-Vendor-Specific-6 246.26 octets
+
+ATTRIBUTE Extended-Example-1 241.1 integer
+ATTRIBUTE Extended-Example-2 241.2 ipaddr
+ATTRIBUTE Extended-Example-3 241.3 tlv
+ATTRIBUTE Extended-Example-TLV-3-1 241.3.1 integer
+ATTRIBUTE Extended-Example-TLV-3-2 241.3.2 string
+ATTRIBUTE Extended-Example-4 241.4 string
+
+ATTRIBUTE Extended-Example-Flags-1 245.1 integer
+ATTRIBUTE Extended-Example-Flags-2 245.2 ipaddr
unsigned int has_tlv : 1; /* has sub attributes */
unsigned int is_tlv : 1; /* is a sub attribute */
unsigned int encoded : 1; /* has been put into packet */
+ unsigned int extended : 1; /* extended attribute */
+ unsigned int extended_flags : 1; /* with flag */
int8_t tag; /* tag for tunneled attributes */
uint8_t encrypt; /* encryption method */
#define VENDORPEC_MICROSOFT 311
#define VENDORPEC_FREERADIUS 11344
#define VENDORPEC_WIMAX 24757
+#define VENDORPEC_EXTENDED (1 << 25)
/*
* Vendor specific attributes
}
}
+ /*
+ * Additional checks for extended attributes.
+ */
+ if (flags.extended || flags.extended_flags) {
+ if (vendor != 0) {
+ fr_strerror_printf("dict_addattr: VSAs cannot use the \"extended\" attribute format.");
+ return -1;
+ }
+ vendor = VENDORPEC_EXTENDED;
+
+ if ((attr < 256) && (type != PW_TYPE_OCTETS)) {
+ fr_strerror_printf("dict_addattr: The base \"extended\" attribute definition MUST be of type \"octets\".");
+ return -1;
+ }
+
+ if (flags.has_tag || flags.array || (flags.encrypt != FLAG_ENCRYPT_NONE)) {
+ fr_strerror_printf("dict_addattr: The \"extended\" attributes MUST NOT have any flags set.");
+ return -1;
+ }
+ }
+
if (attr < 0) {
fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (less than zero)");
return -1;
}
- if (vendor) {
+ if (vendor && (vendor != VENDORPEC_EXTENDED)) {
DICT_VENDOR *dv;
static DICT_VENDOR *last_vendor = NULL;
da->vendor = vendor;
da->type = type;
da->flags = flags;
- da->vendor = vendor;
/*
* Insert the attribute, only if it's not a duplicate.
{
int rcode = 0;
int base = 10;
- const char *tab = "0123456789";
+ static const char *tab = "0123456789";
if ((str[0] == '0') &&
((str[1] == 'x') || (str[1] == 'X'))) {
int type;
int length = 0;
ATTR_FLAGS flags;
+ char *p;
if ((argc < 3) || (argc > 4)) {
fr_strerror_printf("dict_init: %s[%d]: invalid ATTRIBUTE line",
return -1;
}
+ memset(&flags, 0, sizeof(flags));
+
+ /*
+ * Look for extended attributes before doing anything else.
+ */
+ p = strchr(argv[1], '.');
+ if (p) *p = '\0';
+
/*
* Validate all entries
*/
return -1;
}
+ /*
+ * Parse extended attributes.
+ */
+ if (p) {
+ int sub;
+ char *q;
+ DICT_ATTR *da;
+
+ *p = '.'; /* reset forlater printing */
+
+ /*
+ * Does the parent attribute exist?
+ */
+ da = dict_attrbyvalue(value, VENDORPEC_EXTENDED);
+ if (!da) {
+ fr_strerror_printf("dict_init: %s[%d]: Entry refers to unknown attribute %d", fn, line, value);
+ return -1;
+ }
+
+ /*
+ * 241.1 means 241 is of type "extended".
+ * Otherwise, die.
+ */
+ if (!da->flags.extended && !da->flags.extended_flags) {
+ fr_strerror_printf("dict_init: %s[%d]: Entry refers to a non-extended attribute %d", fn, line, value);
+ return -1;
+ }
+
+ /*
+ * Look for sub-TLVs
+ */
+ q = strchr(p + 1, '.');
+ if (q) *q = '\0';
+
+ /*
+ * Parse error.
+ */
+ if (!sscanf_i(p + 1, &sub)) {
+ fr_strerror_printf("dict_init: %s[%d]: Parse error in value \"%s\"", fn, line, argv[1]);
+ return -1;
+ }
+
+ /*
+ * Value is out of bounds.
+ */
+ if ((sub == 0) || (sub > 255)) {
+ fr_strerror_printf("dict_init: %s[%d]: Entry has value out of range 0..255: %d", fn, line, sub);
+ return -1;
+ }
+
+ value |= (sub << fr_wimax_shift[1]);
+
+ /*
+ * If this is defining the contents of a TLV,
+ * look for the parent, and check it.
+ */
+ if (q) {
+ DICT_ATTR *tlv;
+
+ tlv = dict_attrbyvalue(value, VENDORPEC_EXTENDED);
+ if (!tlv || !tlv->flags.has_tlv ||
+ (!tlv->flags.extended && !tlv->flags.extended_flags)) {
+ fr_strerror_printf("dict_init: %s[%d]: Entry refers to Attribute \"%s\", which is not an extended attribute TLV", fn, line, argv[1]);
+ return -1;
+
+ }
+
+ flags.is_tlv = 1;
+
+ /*
+ * Parse error.
+ */
+ if (!sscanf_i(q + 1, &sub)) {
+ fr_strerror_printf("dict_init: %s[%d]: Parse error in value \"%s\"", fn, line, argv[1]);
+ return -1;
+ }
+
+ /*
+ * Value is out of bounds.
+ */
+ if ((sub == 0) || (sub > 255)) {
+ fr_strerror_printf("dict_init: %s[%d]: Entry has value out of range 0..255: %d", fn, line, sub);
+ return -1;
+ }
+
+ value |= (sub << fr_wimax_shift[2]);
+ }
+
+ /*
+ * Set which type of attribute this is.
+ */
+ flags.extended = da->flags.extended;
+ flags.extended_flags = da->flags.extended_flags;
+ }
+
if (strncmp(argv[2], "octets[", 7) != 0) {
/*
* find the type of the attribute.
return -1;
}
} else {
- char *p;
type = PW_TYPE_OCTETS;
p = strchr(argv[2] + 7, ']');
* Only look up the vendor if the string
* is non-empty.
*/
- memset(&flags, 0, sizeof(flags));
if (argc < 4) {
/*
* Force "length" for data types of fixed length;
return -1;
}
- } else if (strncmp(key, "array", 8) == 0) {
+ } else if (strncmp(key, "array", 6) == 0) {
flags.array = 1;
switch (type) {
((vendor = dict_vendorbyname(key)) !=0)) {
break;
+ } else if (strncmp(key, "extended-flags", 15) == 0) {
+ if (flags.extended) {
+ fr_strerror_printf( "dict_init: %s[%d] You cannot set two \"extended\" flags.",
+ fn, line);
+ return -1;
+ }
+
+ flags.extended_flags = 1;
+
+ } else if (strncmp(key, "extended", 9) == 0) {
+ if (flags.extended_flags) {
+ fr_strerror_printf( "dict_init: %s[%d] You cannot set two \"extended\" flags.",
+ fn, line);
+ return -1;
+ }
+ flags.extended = 1;
+
} else {
fr_strerror_printf( "dict_init: %s[%d]: unknown option \"%s\"",
fn, line, key);
flags.is_tlv = 1;
}
-
/*
* Add it in.
*/
ptr[0] = attribute & 0xff; /* NOT vp->attribute */
ptr[1] = 2;
- len = vp2data(packet, original, secret, vp, ptr + 2, room - 2);
+ if (room > (255 - ptr[1])) room = 255 - ptr[1];
+ len = vp2data(packet, original, secret, vp, ptr + 2, room);
if (len < 0) return len;
ptr[1] += len;
}
+static int rad_vp2extended(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR *vp,
+ unsigned int attribute, uint8_t *ptr, size_t room)
+{
+ int len = 2;
+
+ if (room < 3) return 0;
+
+ ptr[0] = attribute & 0xff; /* NOT vp->attribute */
+ ptr[1] = 3;
+
+ if (vp->flags.extended) {
+ ptr[2] = (attribute & 0xff00) >> 8;
+ len++;
+
+ } else if (vp->flags.extended_flags) {
+ if (room < 4) return 0;
+
+ ptr[1] = 4;
+ ptr[2] = (attribute & 0xff00) >> 8;
+ ptr[3] = 0;
+
+ len += 2;
+ }
+
+ /*
+ * For now, no extended attribute can be longer than the
+ * encapsulating attribute. Once we add support for the
+ * "M" bit, this restriction will be relaxed.
+ */
+ if (room > (255 - ptr[1])) room = 255 - ptr[1];
+
+ if (!vp->flags.is_tlv) {
+ len = vp2data(packet, original, secret, vp, ptr + ptr[1], room);
+ } else {
+ len = tlv2data(packet, original, secret, vp, ptr + ptr[1], room, 2);
+ }
+
+ if (len < 0) return len;
+
+ ptr[1] += len;
+
+ return ptr[1];
+}
+
/*
* Parse a data structure into a RADIUS attribute.
*/
vp->attribute, start, room);
}
+ if (vp->vendor == VENDORPEC_EXTENDED) {
+ return rad_vp2extended(packet, original, secret, vp,
+ vp->attribute, start, room);
+ }
+
/*
* Not enough room for:
* attr, len, vendor-id, vsa, vsalen
*/
for (reply = packet->vps; reply; reply = reply->next) {
/*
- * Ignore non-wire attributes
+ * Ignore non-wire attributes, but allow extended
+ * attributes.
*/
if ((reply->vendor == 0) &&
- ((reply->attribute & 0xFFFF) > 0xff)) {
+ ((reply->attribute & 0xFFFF) >= 256) &&
+ !reply->flags.extended && !reply->flags.extended_flags) {
#ifndef NDEBUG
/*
* Permit the admin to send BADLY formatted
*/
if (reply->flags.encoded) continue;
- if (reply->flags.is_tlv) {
+ if ((reply->vendor == VENDORPEC_WIMAX) && reply->flags.is_tlv) {
len = rad_encode_wimax(packet, original, secret,
reply, ptr,
((uint8_t *) data) + sizeof(data) - ptr);
/*
+ * Extended attribute TLV to VP.
+ */
+static VALUE_PAIR *tlv2vp(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int attribute,
+ int length, const uint8_t *data)
+{
+ VALUE_PAIR *vp;
+
+ if ((length < 2) || (data[1] < 2)) return NULL;
+
+ /*
+ * For now, only one TLV is allowed.
+ */
+ if (data[1] != length) return NULL;
+
+ attribute |= (data[0] << fr_wimax_shift[2]);
+
+ vp = paircreate(attribute, VENDORPEC_EXTENDED, PW_TYPE_OCTETS);
+ if (!vp) return NULL;
+
+ return data2vp(packet, original, secret, length - 2, data + 2, vp);
+}
+
+/*
* Parse a RADIUS attribute into a data structure.
*/
VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet,
{
VALUE_PAIR *vp;
+ /*
+ * Hard-coded values are bad...
+ */
+ if ((vendor == 0) && (attribute >= 241) && (attribute <= 246)) {
+ DICT_ATTR *da;
+
+ da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+ if (da && (da->flags.extended || da->flags.extended_flags)) {
+
+ if (length == 0) return NULL;
+
+ attribute |= (data[0] << fr_wimax_shift[1]);
+ vendor = VENDORPEC_EXTENDED;
+
+ data++;
+ length--;
+
+ /*
+ * There may be a flag octet.
+ */
+ if (da->flags.extended_flags) {
+ if (length == 0) return NULL;
+
+ /*
+ * If there's a flag, we can't
+ * handle it.
+ */
+ if (data[0] != 0) return NULL;
+ data++;
+ length--;
+ }
+
+ /*
+ * Now look up the extended attribute, to
+ * see if it's a TLV carrying more data.
+ *
+ */
+ da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+ if (da && da->flags.has_tlv) {
+ return tlv2vp(packet, original, secret,
+ attribute, length, data);
+ }
+ }
+ }
vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
if (!vp) return NULL;