7. Rationale for This Design
The path to extending the RADIUS protocol has been long and arduous. A number of proposals have been made and discarded by the RADEXT working group. These proposals have been judged to be either too bulky, too complex, too simple, or unworkable in practice. We do not otherwise explain here why earlier proposals did not obtain working group consensus.
The changes outlined here have the benefit of being simple, as the "Extended Type" format requires only a one-octet change to the Attribute format. The downside is that the "Long Extended Type" format is awkward, and the 7 Reserved bits will likely never be used for anything.7.1. Attribute Audit
An audit of almost five thousand publicly available attributes [ATTR] (2010) shows the statistics summarized below. The Attributes include over 100 Vendor dictionaries, along with the IANA-assigned attributes: Count Data Type ----- --------- 2257 integer 1762 text 273 IPv4 Address 225 string 96 other data types 35 IPv6 Address 18 date 10 integer64 4 Interface Id 3 IPv6 Prefix 4683 Total The entries in the "Data Type" column are data types recommended by [RFC6158], along with "integer64". The "other data types" row encompasses all other data types, including complex data types and data types transporting opaque data. We see that over half of the Attributes encode less than 16 octets of data. It is therefore important to have an extension mechanism that adds as little as possible to the size of these attributes. Another result is that the overwhelming majority of attributes use simple data types. Of the Attributes defined above, 177 were declared as being inside of a TLV. This is approximately 4% of the total. We did not investigate whether additional attributes were defined in a flat namespace but could have been defined as being inside of a TLV. We expect that the number could be as high as 10% of attributes.
Manual inspection of the dictionaries shows that approximately 20 (or 0.5%) attributes have the ability to transport more than 253 octets of data. These attributes are divided between VSAs and a small number of standard Attributes such as EAP-Message. The results of this audit and analysis are reflected in the design of the extended attributes. The extended format has minimal overhead, permits TLVs, and has support for "long" attributes.8. Diameter Considerations
The Attribute formats defined in this specification need to be transported in Diameter. While Diameter supports attributes longer than 253 octets and grouped attributes, we do not use that functionality here. Instead, we define the simplest possible encapsulation method. The new formats MUST be treated the same as traditional RADIUS attributes when converting from RADIUS to Diameter, or vice versa. That is, the new attribute space is not converted to any "extended" Diameter attribute space. Fragmented attributes are not converted to a single long Diameter attribute. The new EVS data types are not converted to Diameter attributes with the "V" bit set. In short, this document mandates no changes for existing RADIUS-to- Diameter or Diameter-to-RADIUS gateways.9. Examples
A few examples are presented here in order to illustrate the encoding of the new Attribute formats. These examples are not intended to be exhaustive, as many others are possible. For simplicity, we do not show complete packets, but only attributes.
The examples are given using a domain-specific language implemented by the program given in Appendix A of this document. The language is line oriented and composed of a sequence of lines matching the ABNF grammar ([RFC5234]) given below: Identifier = 1*DIGIT *( "." 1*DIGIT ) HEXCHAR = HEXDIG HEXDIG STRING = DQUOTE 1*CHAR DQUOTE TLV = "{" SP 1*DIGIT SP DATA SP "}" DATA = (HEXCHAR *(SP HEXCHAR)) / (TLV *(SP TLV)) / STRING LINE = Identifier SP DATA The program has additional restrictions on its input that are not reflected in the above grammar. For example, the portions of the identifier that refer to Type and Extended-Type are limited to values between 1 and 255. We trust that the source code in Appendix A is clear and that these restrictions do not negatively affect the comprehensibility of the examples. The program reads the input text and interprets it as a set of instructions to create RADIUS attributes. It then prints the hex encoding of those attributes. It implements the minimum set of functionality that achieves that goal. This minimalism means that it does not use attribute dictionaries; it does not implement support for RADIUS data types; it can be used to encode attributes with invalid data fields; and there is no requirement for consistency from one example to the next. For example, it can be used to encode a User-Name attribute that contains non-UTF8 data or a Framed-IP-Address that contains 253 octets of ASCII data. As a result, it MUST NOT be used to create RADIUS attributes for transport in a RADIUS message. However, the program correctly encodes the RADIUS attribute fields of "Type", "Length", "Extended-Type", "More", "Reserved", "Vendor-Id", "Vendor-Type", and "Vendor-Length". It encodes RADIUS attribute data types "evs" and "tlv". It can therefore be used to encode example attributes from inputs that are human readable. We do not give examples of "invalid attributes". We also note that the examples show format, rather than consistent meaning. A particular Attribute Type code may be used to demonstrate two different formats. In real specifications, attributes have a static definitions based on their type code.
The examples given below are strictly for demonstration purposes only and do not provide a standard of any kind.9.1. Extended Type
The following is a series of examples of the "Extended Type" format. Attribute encapsulating textual data: 241.1 "bob" -> f1 06 01 62 6f 62 Attribute encapsulating a TLV with TLV-Type of one (1): 241.2 { 1 23 45 } -> f1 07 02 01 04 23 45 Attribute encapsulating two TLVs, one after the other: 241.2 { 1 23 45 } { 2 67 89 } -> f1 0b 02 01 04 23 45 02 04 67 89 Attribute encapsulating two TLVs, where the second TLV is itself encapsulating a TLV: 241.2 { 1 23 45 } { 3 { 1 ab cd } } -> f1 0d 02 01 04 23 45 03 06 01 04 ab cd Attribute encapsulating two TLVs, where the second TLV is itself encapsulating two TLVs: 241.2 { 1 23 45 } { 3 { 1 ab cd } { 2 "foo" } } -> f1 12 02 01 04 23 45 03 0b 01 04 ab cd 02 05 66 6f 6f Attribute encapsulating a TLV, which in turn encapsulates a TLV, to a depth of 5 nestings: 241.1 { 1 { 2 { 3 { 4 { 5 cd ef } } } } } -> f1 0f 01 01 0c 02 0a 03 08 04 06 05 04 cd ef Attribute encapsulating an Extended-Vendor-Specific Attribute, with Vendor-Id of 1 and Vendor-Type of 4, which in turn encapsulates textual data: 241.26.1.4 "test" -> f1 0c 1a 00 00 00 01 04 74 65 73 74
Attribute encapsulating an Extended-Vendor-Specific Attribute, with Vendor-Id of 1 and Vendor-Type of 5, which in turn encapsulates a TLV with TLV-Type of 3, which encapsulates textual data: 241.26.1.5 { 3 "test" } -> f1 0e 1a 00 00 00 01 05 03 06 74 65 73 749.2. Long Extended Type
The following is a series of examples of the "Long Extended Type" format. Attribute encapsulating textual data: 245.1 "bob" -> f5 07 01 00 62 6f 62 Attribute encapsulating a TLV with TLV-Type of one (1): 245.2 { 1 23 45 } -> f5 08 02 00 01 04 23 45 Attribute encapsulating two TLVs, one after the other: 245.2 { 1 23 45 } { 2 67 89 } -> f5 0c 02 00 01 04 23 45 02 04 67 89 Attribute encapsulating two TLVs, where the second TLV is itself encapsulating a TLV: 245.2 { 1 23 45 } { 3 { 1 ab cd } } -> f5 0e 02 00 01 04 23 45 03 06 01 04 ab cd Attribute encapsulating two TLVs, where the second TLV is itself encapsulating two TLVs: 245.2 { 1 23 45 } { 3 { 1 ab cd } { 2 "foo" } } -> f5 13 02 00 01 04 23 45 03 0b 01 04 ab cd 02 05 66 6f 6f Attribute encapsulating a TLV, which in turn encapsulates a TLV, to a depth of 5 nestings: 245.1 { 1 { 2 { 3 { 4 { 5 cd ef } } } } } -> f5 10 01 00 01 0c 02 0a 03 08 04 06 05 04 cd ef
Attribute encapsulating an Extended-Vendor-Specific Attribute, with Vendor-Id of 1 and Vendor-Type of 4, which in turn encapsulates textual data: 245.26.1.4 "test" -> f5 0d 1a 00 00 00 00 01 04 74 65 73 74 Attribute encapsulating an Extended-Vendor-Specific Attribute, with Vendor-Id of 1 and Vendor-Type of 5, which in turn encapsulates a TLV with TLV-Type of 3, which encapsulates textual data: 245.26.1.5 { 3 "test" } -> f5 0f 1a 00 00 00 00 01 05 03 06 74 65 73 74 Attribute encapsulating more than 251 octets of data. The "Data" portions are indented for readability: 245.4 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccc ccccccccccc" -> f5 ff 04 80 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb f5 13 04 00 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
Below is an example of an attribute encapsulating an Extended-Vendor- Specific Attribute, with Vendor-Id of 1 and Vendor-Type of 6, which in turn encapsulates more than 251 octets of data. As the VSA encapsulates more than 251 octets of data, it is split into two RADIUS attributes. The first attribute has the More field set, and it carries the Vendor-Id and Vendor-Type. The second attribute has the More field clear and carries the rest of the data portion of the VSA. Note that the second attribute does not include the Vendor-Id ad Vendor-Type fields. The "Data" portions are indented for readability: 245.26.1.6 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccc ccccccccccccccccc" -> f5 ff 1a 80 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb f5 18 1a 00 bb bb bb bb bb cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
10. IANA Considerations
This document updates [RFC3575] in that it adds new IANA considerations for RADIUS attributes. These considerations modify and extend the IANA considerations for RADIUS, rather than replacing them. The IANA considerations of this document are limited to the "RADIUS Attribute Types" registry. Some Attribute Type values that were previously marked "Reserved" are now allocated, and the registry is extended from a simple 8-bit array to a tree-like structure, up to a maximum depth of 125 nodes. Detailed instructions are given below.10.1. Attribute Allocations
IANA has moved the following Attribute Type values from "Reserved" to "Allocated" with the corresponding names: * 241 Extended-Type-1 * 242 Extended-Type-2 * 243 Extended-Type-3 * 244 Extended-Type-4 * 245 Long-Extended-Type-1 * 246 Long-Extended-Type-2 These values serve as an encapsulation layer for the new RADIUS Attribute Type tree.10.2. RADIUS Attribute Type Tree
Each of the Attribute Type values allocated above extends the "RADIUS Attribute Types" to an N-ary tree, via a "dotted number" notation. Allocation of an Attribute Type value "TYPE" using the new "Extended Type" format results in allocation of 255 new Attribute Type values of format "TYPE.1" through "TYPE.255". Value twenty-six (26) is assigned as "Extended-Vendor-Specific-*". Values "TYPE.241" through "TYPE.255" are marked "Reserved". All other values are "Unassigned".
The initial set of Attribute Type values and names assigned by this document is given below. * 241 Extended-Attribute-1 * 241.{1-25} Unassigned * 241.26 Extended-Vendor-Specific-1 * 241.{27-240} Unassigned * 241.{241-255} Reserved * 242 Extended-Attribute-2 * 242.{1-25} Unassigned * 242.26 Extended-Vendor-Specific-2 * 242.{27-240} Unassigned * 242.{241-255} Reserved * 243 Extended-Attribute-3 * 243.{1-25} Unassigned * 243.26 Extended-Vendor-Specific-3 * 243.{27-240} Unassigned * 243.{241-255} Reserved * 244 Extended-Attribute-4 * 244.{1-25} Unassigned * 244.26 Extended-Vendor-Specific-4 * 244.{27-240} Unassigned * 244.{241-255} Reserved * 245 Extended-Attribute-5 * 245.{1-25} Unassigned * 245.26 Extended-Vendor-Specific-5 * 245.{27-240} Unassigned * 245.{241-255} Reserved * 246 Extended-Attribute-6 * 246.{1-25} Unassigned * 246.26 Extended-Vendor-Specific-6 * 246.{27-240} Unassigned * 246.{241-255} Reserved As per [RFC5226], the values marked "Unassigned" above are available for assignment by IANA in future RADIUS specifications. The values marked "Reserved" are reserved for future use. The Extended-Vendor-Specific spaces (TYPE.26) are for Private Use, and allocations are not managed by IANA. Allocation of Reserved entries in the extended space requires Standards Action. All other allocations in the extended space require IETF Review.
10.3. Allocation Instructions
This section defines what actions IANA needs to take when allocating new attributes. Different actions are required when allocating attributes from the standard space, attributes of the "Extended Type" format, attributes of the "Long Extended Type" format, preferential allocations, attributes of data type TLV, attributes within a TLV, and attributes of other data types.10.3.1. Requested Allocation from the Standard Space
Specifications can request allocation of an Attribute from within the standard space (e.g., Attribute Type Codes 1 through 255), subject to the considerations of [RFC3575] and this document.10.3.2. Requested Allocation from the Short Extended Space
Specifications can request allocation of an Attribute that requires the format "Extended Type", by specifying the short extended space. In that case, IANA should assign the lowest Unassigned number from the Attribute Type space with the relevant format.10.3.3. Requested Allocation from the Long Extended Space
Specifications can request allocation of an Attribute that requires the format "Long Extended Type", by specifying the extended space (long). In that case, IANA should assign the lowest Unassigned number from the Attribute Type space with the relevant format.10.3.4. Allocation Preferences
Specifications that make no request for allocation from a specific type space should have Attributes allocated using the following criteria: * When the standard space has no more Unassigned attributes, all allocations should be performed from the extended space. * Specifications that allocate a small number of attributes (i.e., less than ten) should have all allocations made from the standard space. * Specifications that would allocate more than twenty percent of the remaining standard space attributes should have all allocations made from the extended space. * Specifications that request allocation of an attribute of data type TLV should have that attribute allocated from the extended space.
* Specifications that request allocation of an attribute that can transport 253 or more octets of data should have that attribute allocated from within the long extended space. We note that Section 6.5 above makes recommendations related to this allocation. There is otherwise no requirement that all attributes within a specification be allocated from one type space or another. Specifications can simultaneously allocate attributes from both the standard space and the extended space.10.3.5. Extending the Type Space via the TLV Data Type
When specifications request allocation of an attribute of data type TLV, that allocation extends the Attribute Type tree by one more level. Allocation of an Attribute Type value "TYPE.TLV", with data type TLV, results in allocation of 255 new Attribute Type values, of format "TYPE.TLV.1" through "TYPE.TLV.255". Values 254-255 are marked "Reserved". All other values are "Unassigned". Value 26 has no special meaning. For example, if a new attribute "Example-TLV" of data type TLV is assigned the identifier "245.1", then the extended tree will be allocated as below: * 245.1 Example-TLV * 245.1.{1-253} Unassigned * 245.1.{254-255} Reserved Note that this example does not define an "Example-TLV" attribute. The Attribute Type tree can be extended multiple levels in one specification when the specification requests allocation of nested TLVs, as discussed below.10.3.6. Allocation within a TLV
Specifications can request allocation of Attribute Type values within an Attribute of data type TLV. The encapsulating TLV can be allocated in the same specification, or it can have been previously allocated. Specifications need to request allocation within a specific Attribute Type value (e.g., "TYPE.TLV.*"). Allocations are performed from the smallest Unassigned value, proceeding to the largest Unassigned value.
Where the Attribute being allocated is of data type TLV, the Attribute Type tree is extended by one level, as given in the previous section. Allocations can then be made within that level.10.3.7. Allocation of Other Data Types
Attribute Type value allocations are otherwise allocated from the smallest Unassigned value, proceeding to the largest Unassigned value, e.g., starting from 241.1, proceeding through 241.255, then to 242.1, through 242.255, etc.11. Security Considerations
This document defines new formats for data carried inside of RADIUS but otherwise makes no changes to the security of the RADIUS protocol. Attacks on cryptographic hashes are well known and are getting better with time, as discussed in [RFC4270]. The security of the RADIUS protocol is dependent on MD5 [RFC1321], which has security issues as discussed in [RFC6151]. It is not known if the issues described in [RFC6151] apply to RADIUS. For other issues, we incorporate by reference the security considerations of [RFC6158] Section 5. As with any protocol change, code changes are required in order to implement the new features. These code changes have the potential to introduce new vulnerabilities in the software. Since the RADIUS server performs network authentication, it is an inviting target for attackers. We RECOMMEND that access to RADIUS servers be kept to a minimum.12. References
12.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997. [RFC2865] Rigney, C., Willens, S., Rubens, A., and W. Simpson, "Remote Authentication Dial In User Service (RADIUS)", RFC 2865, June 2000. [RFC2866] Rigney, C., "RADIUS Accounting", RFC 2866, June 2000. [RFC3575] Aboba, B., "IANA Considerations for RADIUS (Remote Authentication Dial In User Service)", RFC 3575, July 2003.
[RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 5226, May 2008. [RFC6158] DeKok, A., Ed., and G. Weber, "RADIUS Design Guidelines", BCP 158, RFC 6158, March 2011. [PEN] IANA, "PRIVATE ENTERPRISE NUMBERS", <http://www.iana.org/assignments/enterprise-numbers>.12.2. Informative References
[RFC1321] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April 1992. [RFC2868] Zorn, G., Leifer, D., Rubens, A., Shriver, J., Holdrege, M., and I. Goyret, "RADIUS Attributes for Tunnel Protocol Support", RFC 2868, June 2000. [RFC4270] Hoffman, P. and B. Schneier, "Attacks on Cryptographic Hashes in Internet Protocols", RFC 4270, November 2005. [RFC5234] Crocker, D., Ed., and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, January 2008. [RFC6151] Turner, S. and L. Chen, "Updated Security Considerations for the MD5 Message-Digest and the HMAC-MD5 Algorithms", RFC 6151, March 2011. [ATTR] "alandekok/freeradius-server", available from GitHub, data retrieved September 2010, <http://github.com/alandekok/ freeradius-server/tree/master/share/>.13. Acknowledgments
This document is the result of long discussions in the IETF RADEXT working group. The authors would like to thank all of the participants who contributed various ideas over the years. Their feedback has been invaluable and has helped to make this specification better.
Appendix A. Extended Attribute Generator Program
This section contains "C" program source code that can be used for testing. It reads a line-oriented text file, parses it to create RADIUS formatted attributes, and prints the hex version of those attributes to standard output. The input accepts grammar similar to that given in Section 9, with some modifications for usability. For example, blank lines are allowed, lines beginning with a '#' character are interpreted as comments, numbers (RADIUS Types, etc.) are checked for minimum/ maximum values, and RADIUS attribute lengths are enforced. The program is included here for demonstration purposes only, and does not define a standard of any kind. ------------------------------------------------------------ /* * Copyright (c) 2013 IETF Trust and the persons identified as * authors of the code. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * - Neither the name of Internet Society, IETF or IETF Trust, nor * the names of specific contributors, may be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Alan DeKok <aland@networkradius.com> */ #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <ctype.h> static int encode_tlv(char *buffer, uint8_t *output, size_t outlen); static const char *hextab = "0123456789abcdef"; static int encode_data_string(char *buffer, uint8_t *output, size_t outlen) { int length = 0; char *p; p = buffer + 1; while (*p && (outlen > 0)) { if (*p == '"') { return length; } if (*p != '\\') { *(output++) = *(p++); outlen--; length++; continue; } switch (p[1]) { default: *(output++) = p[1]; break; case 'n': *(output++) = '\n'; break;
case 'r': *(output++) = '\r'; break; case 't': *(output++) = '\t'; break; } outlen--; length++; } fprintf(stderr, "String is not terminated\n"); return 0; } static int encode_data_tlv(char *buffer, char **endptr, uint8_t *output, size_t outlen) { int depth = 0; int length; char *p; for (p = buffer; *p != '\0'; p++) { if (*p == '{') depth++; if (*p == '}') { depth--; if (depth == 0) break; } } if (*p != '}') { fprintf(stderr, "No trailing '}' in string starting " "with \"%s\"\n", buffer); return 0; } *endptr = p + 1; *p = '\0'; p = buffer + 1; while (isspace((int) *p)) p++;
length = encode_tlv(p, output, outlen); if (length == 0) return 0; return length; } static int encode_data(char *p, uint8_t *output, size_t outlen) { int length; if (!isspace((int) *p)) { fprintf(stderr, "Invalid character following attribute " "definition\n"); return 0; } while (isspace((int) *p)) p++; if (*p == '{') { int sublen; char *q; length = 0; do { while (isspace((int) *p)) p++; if (!*p) { if (length == 0) { fprintf(stderr, "No data\n"); return 0; } break; } sublen = encode_data_tlv(p, &q, output, outlen); if (sublen == 0) return 0; length += sublen; output += sublen; outlen -= sublen; p = q; } while (*q); return length; }
if (*p == '"') { length = encode_data_string(p, output, outlen); return length; } length = 0; while (*p) { char *c1, *c2; while (isspace((int) *p)) p++; if (!*p) break; if(!(c1 = memchr(hextab, tolower((int) p[0]), 16)) || !(c2 = memchr(hextab, tolower((int) p[1]), 16))) { fprintf(stderr, "Invalid data starting at " "\"%s\"\n", p); return 0; } *output = ((c1 - hextab) << 4) + (c2 - hextab); output++; length++; p += 2; outlen--; if (outlen == 0) { fprintf(stderr, "Too much data\n"); return 0; } } if (length == 0) { fprintf(stderr, "Empty string\n"); return 0; } return length; }
static int decode_attr(char *buffer, char **endptr) { long attr; attr = strtol(buffer, endptr, 10); if (*endptr == buffer) { fprintf(stderr, "No valid number found in string " "starting with \"%s\"\n", buffer); return 0; } if (!**endptr) { fprintf(stderr, "Nothing follows attribute number\n"); return 0; } if ((attr <= 0) || (attr > 256)) { fprintf(stderr, "Attribute number is out of valid " "range\n"); return 0; } return (int) attr; } static int decode_vendor(char *buffer, char **endptr) { long vendor; if (*buffer != '.') { fprintf(stderr, "Invalid separator before vendor id\n"); return 0; } vendor = strtol(buffer + 1, endptr, 10); if (*endptr == (buffer + 1)) { fprintf(stderr, "No valid vendor number found\n"); return 0; } if (!**endptr) { fprintf(stderr, "Nothing follows vendor number\n"); return 0; }
if ((vendor <= 0) || (vendor > (1 << 24))) { fprintf(stderr, "Vendor number is out of valid range\n"); return 0; } if (**endptr != '.') { fprintf(stderr, "Invalid data following vendor number\n"); return 0; } (*endptr)++; return (int) vendor; } static int encode_tlv(char *buffer, uint8_t *output, size_t outlen) { int attr; int length; char *p; attr = decode_attr(buffer, &p); if (attr == 0) return 0; output[0] = attr; output[1] = 2; if (*p == '.') { p++; length = encode_tlv(p, output + 2, outlen - 2); } else { length = encode_data(p, output + 2, outlen - 2); } if (length == 0) return 0; if (length > (255 - 2)) { fprintf(stderr, "TLV data is too long\n"); return 0; } output[1] += length; return length + 2; }
static int encode_vsa(char *buffer, uint8_t *output, size_t outlen) { int vendor; int attr; int length; char *p; vendor = decode_vendor(buffer, &p); if (vendor == 0) return 0; output[0] = 0; output[1] = (vendor >> 16) & 0xff; output[2] = (vendor >> 8) & 0xff; output[3] = vendor & 0xff; length = encode_tlv(p, output + 4, outlen - 4); if (length == 0) return 0; if (length > (255 - 6)) { fprintf(stderr, "VSA data is too long\n"); return 0; } return length + 4; } static int encode_evs(char *buffer, uint8_t *output, size_t outlen) { int vendor; int attr; int length; char *p; vendor = decode_vendor(buffer, &p); if (vendor == 0) return 0; attr = decode_attr(p, &p); if (attr == 0) return 0; output[0] = 0; output[1] = (vendor >> 16) & 0xff; output[2] = (vendor >> 8) & 0xff; output[3] = vendor & 0xff; output[4] = attr; length = encode_data(p, output + 5, outlen - 5); if (length == 0) return 0;
return length + 5; } static int encode_extended(char *buffer, uint8_t *output, size_t outlen) { int attr; int length; char *p; attr = decode_attr(buffer, &p); if (attr == 0) return 0; output[0] = attr; if (attr == 26) { length = encode_evs(p, output + 1, outlen - 1); } else { length = encode_data(p, output + 1, outlen - 1); } if (length == 0) return 0; if (length > (255 - 3)) { fprintf(stderr, "Extended Attr data is too long\n"); return 0; } return length + 1; } static int encode_extended_flags(char *buffer, uint8_t *output, size_t outlen) { int attr; int length, total; char *p; attr = decode_attr(buffer, &p); if (attr == 0) return 0; /* output[0] is the extended attribute */ output[1] = 4; output[2] = attr; output[3] = 0;
if (attr == 26) { length = encode_evs(p, output + 4, outlen - 4); if (length == 0) return 0; output[1] += 5; length -= 5; } else { length = encode_data(p, output + 4, outlen - 4); } if (length == 0) return 0; total = 0; while (1) { int sublen = 255 - output[1]; if (length <= sublen) { output[1] += length; total += output[1]; break; } length -= sublen; memmove(output + 255 + 4, output + 255, length); memcpy(output + 255, output, 4); output[1] = 255; output[3] |= 0x80; output += 255; output[1] = 4; total += 255; } return total; } static int encode_rfc(char *buffer, uint8_t *output, size_t outlen) { int attr; int length, sublen; char *p; attr = decode_attr(buffer, &p); if (attr == 0) return 0;
length = 2; output[0] = attr; output[1] = 2; if (attr == 26) { sublen = encode_vsa(p, output + 2, outlen - 2); } else if ((*p == ' ') || ((attr < 241) || (attr > 246))) { sublen = encode_data(p, output + 2, outlen - 2); } else { if (*p != '.') { fprintf(stderr, "Invalid data following " "attribute number\n"); return 0; } if (attr < 245) { sublen = encode_extended(p + 1, output + 2, outlen - 2); } else { /* * Not like the others! */ return encode_extended_flags(p + 1, output, outlen); } } if (sublen == 0) return 0; if (sublen > (255 -2)) { fprintf(stderr, "RFC Data is too long\n"); return 0; } output[1] += sublen; return length + sublen; } int main(int argc, char *argv[]) { int lineno; size_t i, outlen; FILE *fp; char input[8192], buffer[8192]; uint8_t output[4096];
if ((argc < 2) || (strcmp(argv[1], "-") == 0)) { fp = stdin; } else { fp = fopen(argv[1], "r"); if (!fp) { fprintf(stderr, "Error opening %s: %s\n", argv[1], strerror(errno)); exit(1); } } lineno = 0; while (fgets(buffer, sizeof(buffer), fp) != NULL) { char *p = strchr(buffer, '\n'); lineno++; if (!p) { if (!feof(fp)) { fprintf(stderr, "Line %d too long in %s\n", lineno, argv[1]); exit(1); } } else { *p = '\0'; } p = strchr(buffer, '#'); if (p) *p = '\0'; p = buffer; while (isspace((int) *p)) p++; if (!*p) continue; strcpy(input, p); outlen = encode_rfc(input, output, sizeof(output)); if (outlen == 0) { fprintf(stderr, "Parse error in line %d of %s\n", lineno, input); exit(1); } printf("%s -> ", buffer); for (i = 0; i < outlen; i++) { printf("%02x ", output[i]); }
printf("\n"); } if (fp != stdin) fclose(fp); return 0; } ------------------------------------------------------------Authors' Addresses
Alan DeKok Network RADIUS SARL 57bis blvd des Alpes 38240 Meylan France EMail: aland@networkradius.com URI: http://networkradius.com Avi Lior EMail: avi.ietf@lior.org