From: Alan T. DeKok Date: Thu, 9 Sep 2010 13:29:29 +0000 (+0200) Subject: Add support for extended attributes: draft-dekok-radext-radius-extensions X-Git-Tag: release_3_0_0_beta0~1258 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=freeradius.git;a=commitdiff_plain;h=3fe7a409cbbc70bf0bee41c46a38d6fdfa77c697 Add support for extended attributes: draft-dekok-radext-radius-extensions 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. --- diff --git a/share/dictionary b/share/dictionary index dad1f42..5cb0e2f 100644 --- a/share/dictionary +++ b/share/dictionary @@ -82,6 +82,7 @@ $INCLUDE dictionary.rfc5176 $INCLUDE dictionary.rfc5580 $INCLUDE dictionary.rfc5607 $INCLUDE dictionary.rfc5904 +$INCLUDE dictionary.extended # # Include vendor dictionaries after the standard ones. diff --git a/share/dictionary.extended b/share/dictionary.extended new file mode 100644 index 0000000..dee13a3 --- /dev/null +++ b/share/dictionary.extended @@ -0,0 +1,23 @@ +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 diff --git a/src/include/libradius.h b/src/include/libradius.h index 1d12fd7..e7cdbc6 100644 --- a/src/include/libradius.h +++ b/src/include/libradius.h @@ -104,6 +104,8 @@ typedef struct attr_flags { 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 */ diff --git a/src/include/radius.h b/src/include/radius.h index 7d784c3..c6eaad2 100644 --- a/src/include/radius.h +++ b/src/include/radius.h @@ -305,6 +305,7 @@ #define VENDORPEC_MICROSOFT 311 #define VENDORPEC_FREERADIUS 11344 #define VENDORPEC_WIMAX 24757 +#define VENDORPEC_EXTENDED (1 << 25) /* * Vendor specific attributes diff --git a/src/lib/dict.c b/src/lib/dict.c index 6c14414..ff77e21 100644 --- a/src/lib/dict.c +++ b/src/lib/dict.c @@ -546,12 +546,33 @@ int dict_addattr(const char *name, int attr, int vendor, int type, } } + /* + * 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; @@ -616,7 +637,6 @@ int dict_addattr(const char *name, int attr, int vendor, int type, da->vendor = vendor; da->type = type; da->flags = flags; - da->vendor = vendor; /* * Insert the attribute, only if it's not a duplicate. @@ -833,7 +853,7 @@ static int sscanf_i(const char *str, int *pvalue) { 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'))) { @@ -871,6 +891,7 @@ static int process_attribute(const char* fn, const int line, 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", @@ -878,6 +899,14 @@ static int process_attribute(const char* fn, const int 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 */ @@ -886,6 +915,101 @@ static int process_attribute(const char* fn, const int line, 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. @@ -897,7 +1021,6 @@ static int process_attribute(const char* fn, const int line, return -1; } } else { - char *p; type = PW_TYPE_OCTETS; p = strchr(argv[2] + 7, ']'); @@ -923,7 +1046,6 @@ static int process_attribute(const char* fn, const int line, * 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; @@ -993,7 +1115,7 @@ static int process_attribute(const char* fn, const int line, return -1; } - } else if (strncmp(key, "array", 8) == 0) { + } else if (strncmp(key, "array", 6) == 0) { flags.array = 1; switch (type) { @@ -1018,6 +1140,23 @@ static int process_attribute(const char* fn, const int line, ((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); @@ -1074,7 +1213,6 @@ static int process_attribute(const char* fn, const int line, flags.is_tlv = 1; } - /* * Add it in. */ diff --git a/src/lib/radius.c b/src/lib/radius.c index be4124f..62751df 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -816,7 +816,8 @@ static int rad_vp2rfc(const RADIUS_PACKET *packet, 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; @@ -921,6 +922,52 @@ static int wimax2data(const RADIUS_PACKET *packet, } +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. */ @@ -941,6 +988,11 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, 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 @@ -1218,10 +1270,12 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, */ 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 @@ -1263,7 +1317,7 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, */ 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); @@ -2786,6 +2840,31 @@ static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet, /* + * 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, @@ -2795,6 +2874,50 @@ 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;