X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fradius.c;h=62751dfa469c4bc3c8074da8b9f9e8bedd358bd7;hb=3fe7a409cbbc70bf0bee41c46a38d6fdfa77c697;hp=a59ef18e91a0ee0f582782cbb2b11a1238b6ad89;hpb=493cac1399fc817a3e626d94ca1f339ebde26353;p=freeradius.git diff --git a/src/lib/radius.c b/src/lib/radius.c index a59ef18..62751df 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -626,6 +626,7 @@ static void make_tunnel_passwd(uint8_t *output, size_t *outlen, } fr_MD5Final(digest, &context); + for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + 2 + n] ^= digest[i]; } @@ -636,14 +637,14 @@ static void make_tunnel_passwd(uint8_t *output, size_t *outlen, /* * Returns the end of the data. */ -static uint8_t *vp2data(const RADIUS_PACKET *packet, - const RADIUS_PACKET *original, - const char *secret, const VALUE_PAIR *vp, uint8_t *ptr, - size_t room) +static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, uint8_t *start, + size_t room) { uint32_t lvalue; size_t len; const uint8_t *data; + uint8_t *ptr = start; uint8_t array[4]; /* @@ -710,13 +711,14 @@ static uint8_t *vp2data(const RADIUS_PACKET *packet, data = vp->vp_tlv; if (!data) { fr_strerror_printf("ERROR: Cannot encode NULL TLV"); - return NULL; + return -1; } + if (vp->length > room) return 0; /* can't chop TLVs to fit */ break; default: /* unknown type: ignore it */ fr_strerror_printf("ERROR: Unknown attribute type %d", vp->type); - return NULL; + return -1; } /* @@ -737,6 +739,9 @@ static uint8_t *vp2data(const RADIUS_PACKET *packet, break; case FLAG_ENCRYPT_TUNNEL_PASSWORD: + lvalue = 0; + if (vp->flags.has_tag) lvalue = 1; + /* * Check if there's enough room. If there isn't, * we discard the attribute. @@ -744,7 +749,7 @@ static uint8_t *vp2data(const RADIUS_PACKET *packet, * This is ONLY a problem if we have multiple VSA's * in one Vendor-Specific, though. */ - if (room < 18) return ptr; + if (room < (18 + lvalue)) return 0; switch (packet->code) { case PW_AUTHENTICATION_ACK: @@ -753,15 +758,19 @@ static uint8_t *vp2data(const RADIUS_PACKET *packet, default: if (!original) { fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name); - return NULL; + return -1; } - make_tunnel_passwd(ptr, &len, data, len, room, + + if (lvalue) ptr[0] = vp->flags.tag; + make_tunnel_passwd(ptr + lvalue, &len, data, len, + room - lvalue, secret, original->vector); break; case PW_ACCOUNTING_REQUEST: case PW_DISCONNECT_REQUEST: case PW_COA_REQUEST: - make_tunnel_passwd(ptr, &len, data, len, room, + ptr[0] = vp->flags.tag; + make_tunnel_passwd(ptr + 1, &len, data, len - 1, room, secret, packet->vector); break; } @@ -778,228 +787,249 @@ static uint8_t *vp2data(const RADIUS_PACKET *packet, default: - /* - * Just copy the data over - */ + if (vp->flags.has_tag && TAG_VALID(vp->flags.tag)) { + if (vp->type == PW_TYPE_STRING) { + if (len > (room - 1)) len = room - 1; + ptr[0] = vp->flags.tag; + ptr++; + } else if (vp->type == PW_TYPE_INTEGER) { + array[0] = vp->flags.tag; + } /* else it can't be any other type */ + } memcpy(ptr, data, len); break; } /* switch over encryption flags */ - return ptr + len; + return len + (ptr - start);; } -static VALUE_PAIR *rad_vp2tlv(VALUE_PAIR *vps) +static int rad_vp2rfc(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 maxattr = 0; - int length; - unsigned int attribute; - uint8_t *ptr, *end; - VALUE_PAIR *vp, *tlv; + int len; - attribute = vps->attribute & 0xffff00ff; - maxattr = vps->attribute & 0x0ff; + if (room < 2) return 0; - tlv = paircreate(attribute, vps->vendor, PW_TYPE_TLV); - if (!tlv) return NULL; + ptr[0] = attribute & 0xff; /* NOT vp->attribute */ + ptr[1] = 2; - tlv->length = 0; - for (vp = vps; vp != NULL; vp = vp->next) { - /* - * Group the attributes ONLY until we see a - * non-TLV attribute. - */ - if (!vp->flags.is_tlv || - vp->flags.encoded || - (vp->flags.encrypt != FLAG_ENCRYPT_NONE) || - (vp->vendor != vps->vendor) || - ((vp->attribute & 0xffff00ff) != attribute) || - ((vp->attribute & 0x0000ff00) <= maxattr)) { - break; - } + if (room > (255 - ptr[1])) room = 255 - ptr[1]; + len = vp2data(packet, original, secret, vp, ptr + 2, room); + if (len < 0) return len; - maxattr = vp->attribute & 0xff00; - tlv->length += vp->length + 2; - } + ptr[1] += len; - if (!tlv->length) { - pairfree(&tlv); - return NULL; - } + return ptr[1]; +} - tlv->vp_tlv = malloc(tlv->length); - if (!tlv->vp_tlv) { - pairfree(&tlv); - return NULL; - } +extern int fr_wimax_max_tlv; +extern int fr_wimax_shift[]; +extern int fr_wimax_mask[]; - ptr = tlv->vp_tlv; - maxattr = vps->attribute & 0x0ff; - for (vp = vps; vp != NULL; vp = vp->next) { - if (!vp->flags.is_tlv || - vp->flags.encoded || - (vp->flags.encrypt != FLAG_ENCRYPT_NONE) || - ((vp->attribute & 0xffff00ff) != attribute) || - ((vp->attribute & 0x0000ff00) <= maxattr)) { - break; - } +static int tlv2data(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, + uint8_t *ptr, size_t room, int nest) +{ + int len; - maxattr = vp->attribute & 0xff00; - end = vp2data(NULL, NULL, NULL, vp, ptr + 2, - tlv->vp_tlv + tlv->length - ptr); - if (!end) { - vp->length = ptr - vp->vp_tlv; - return tlv; /* should be a more serious error... */ - } + if (nest > fr_wimax_max_tlv) return -1; - length = (end - ptr); - if (length > 255) return NULL; + if (room < 2) return 0; + room -= 2; - /* - * Pack the attribute. - */ - ptr[0] = (vp->attribute & 0xff00) >> 8; - ptr[1] = (length & 0xff); + ptr[0] = (vp->attribute >> fr_wimax_shift[nest]) & fr_wimax_mask[nest]; + ptr[1] = 2; - ptr += ptr[1]; - vp->flags.encoded = 1; + /* + * No more nested TLVs: pack the data. + */ + if ((nest == fr_wimax_max_tlv) || + ((vp->attribute >> fr_wimax_shift[nest + 1]) == 0)) { + len = vp2data(packet, original, secret, vp, ptr + 2, room); + } else { + len = tlv2data(packet, original, secret, vp, ptr + 2, room, + nest + 1); } + if (len <= 0) return len; - return tlv; + ptr[1] += len; + + return ptr[1]; } -/* - * Pack data without any encryption. - * start == start of RADIUS attribute - * ptr == continuation byte (i.e. one after length) - */ -static int rad_vp2continuation(const VALUE_PAIR *vp, uint8_t *start, - uint8_t *ptr) +static int wimax2data(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, + uint8_t *start, size_t room, uint8_t *ptr) { - size_t left, piece; - size_t hsize = (ptr - start); - uint8_t *this = start; - const uint8_t *data; - uint8_t header[16]; - + int len; + /* - * If it's too long and marked as encrypted, ignore it. + * Offsets to Vendor-Specific length, and to length of + * WiMAX attribute. */ - if (vp->flags.encrypt != FLAG_ENCRYPT_NONE) { - return 0; - } - - memcpy(header, start, hsize); +#define VS_OFF (1) +#define WM_OFF (7) - left = vp->length; - - switch (vp->type) { - case PW_TYPE_TLV: - data = vp->vp_tlv; - break; + if (room < 1) return 0; + room--; - case PW_TYPE_OCTETS: - case PW_TYPE_STRING: - data = vp->vp_octets; - break; + /* + * Account for continuation bytes. The caller has + * already accounting for the continuation byte in the + * Vendor-Specific "length" field. + */ + start[WM_OFF]++; + *(ptr++) = 0; - /* - * This is invalid. - */ - default: + /* + * Chop everything to fit in one attribute. + */ + if (room > (255 - 9)) room = (255 - 9); + + /* + * The attribute contains TLVs that we have NOT decoded + * properly, OR it contains TLV that the user has encoded + * manually. If it has no data, OR it's too long, + * discard it. We're not going to walk through its + * contents trying to figure out how to chop it across + * multiple continuations. + */ + if (vp->flags.has_tlv && (!vp->vp_tlv || (vp->length > room))) { return 0; } - - while (left > 0) { - memcpy(this, header, hsize); - ptr = this + hsize; - - /* - * 254 to account for - * continuation flag. - */ - if (left > (254 - hsize)) { - piece = 254 - hsize; - *(ptr++) = 0x80; - } else { - piece = left; - *(ptr++) = 0x00; - } - - memcpy(ptr, data, piece); - this[1] = hsize + piece + 1; - /* - * - */ - this[hsize - 1] = hsize - 6 + 1 + piece; - data += piece; - ptr += piece; - left -= piece; - this = ptr; - } - - return (ptr - start); + /* + * The attribute is a top-level integer, ipaddr, etc. + * Encode it. + */ + if (!vp->flags.is_tlv) { + len = vp2data(packet, original, secret, vp, ptr, room); + } else { + len = tlv2data(packet, original, secret, vp, ptr, room, 1); + } + + if (len <= 0) return len; + + start[VS_OFF] += len; + start[WM_OFF] += len; + + return start[VS_OFF]; } +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. */ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, - const char *secret, const VALUE_PAIR *vp, uint8_t *start) + const char *secret, const VALUE_PAIR *vp, uint8_t *start, + size_t room) { - int vendorcode; - int len, total_length; - uint32_t lvalue; - uint8_t *ptr, *length_ptr, *vsa_length_ptr, *tlv_length_ptr; - uint8_t *end; + int len; + uint32_t lvalue; + uint8_t *ptr; + DICT_VENDOR *dv; - ptr = start; - end = ptr + 255; - vendorcode = total_length = 0; - length_ptr = vsa_length_ptr = tlv_length_ptr = NULL; + /* + * RFC format attributes take the fast path. + */ + if (vp->vendor == 0) { + return rad_vp2rfc(packet, original, secret, vp, + vp->attribute, start, room); + } + + if (vp->vendor == VENDORPEC_EXTENDED) { + return rad_vp2extended(packet, original, secret, vp, + vp->attribute, start, room); + } /* - * For interoperability, always put vendor attributes - * into their own VSA. + * Not enough room for: + * attr, len, vendor-id, vsa, vsalen */ - if ((vendorcode = vp->vendor) == 0) { - *(ptr++) = vp->attribute & 0xff; - length_ptr = ptr; - *(ptr++) = 2; - total_length += 2; + if (room < 8) return 0; - } else { - int vsa_tlen = 1; - int vsa_llen = 1; - int vsa_offset = 0; - DICT_VENDOR *dv = dict_vendorbyvalue(vendorcode); + /* + * Build the Vendor-Specific header + */ + ptr = start; + *ptr++ = PW_VENDOR_SPECIFIC; + *ptr++ = 6; + room -= 6; + lvalue = htonl(vp->vendor); + memcpy(ptr, &lvalue, 4); + ptr += 4; - /* - * This must be an RFC-format attribute. If it - * wasn't, then the "decode" function would have - * made a Vendor-Specific attribute (i.e. type - * 26), and we would have "vendorcode == 0" here. - */ - if (dv) { - vsa_tlen = dv->type; - vsa_llen = dv->length; - if (dv->flags) vsa_offset = 1; - } + /* + * Unknown vendors, and type=1,length=1,no-continuation + * are RFC format attributes. + */ + dv = dict_vendorbyvalue(vp->vendor); + if (!dv || + ((dv->type == 1) && (dv->length = 1) && !dv->flags)) { + len = rad_vp2rfc(packet, original, secret, vp, + vp->attribute, ptr, room); + if (len <= 0) return len; - /* - * Build a VSA header. - */ - *ptr++ = PW_VENDOR_SPECIFIC; - vsa_length_ptr = ptr; - *ptr++ = 6; - lvalue = htonl(vendorcode); - memcpy(ptr, &lvalue, 4); - ptr += 4; - total_length += 6; + start[1] += len; + return start[1]; + } - switch (vsa_tlen) { + if (room < (dv->type + dv->length + dv->flags)) return 0; + room -= (dv->type + dv->length + dv->flags); + start[1] += (dv->type + dv->length + dv->flags); + + switch (dv->type) { case 1: ptr[0] = (vp->attribute & 0xFF); break; @@ -1011,116 +1041,138 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, case 4: ptr[0] = 0; - ptr[1] = 0; + ptr[1] = ((vp->attribute >> 16) & 0xFF); ptr[2] = ((vp->attribute >> 8) & 0xFF); ptr[3] = (vp->attribute & 0xFF); break; default: return 0; /* silently discard it */ - } - ptr += vsa_tlen; + } + ptr += dv->type; - switch (vsa_llen) { - case 0: - length_ptr = vsa_length_ptr; - vsa_length_ptr = NULL; - break; - case 1: - ptr[0] = 0; - length_ptr = ptr; - break; - case 2: - ptr[0] = 0; - ptr[1] = 0; - length_ptr = ptr + 1; - break; + switch (dv->length) { + case 0: + break; + case 1: + ptr[0] = dv->type + 1; + break; + case 2: + ptr[0] = 0; + ptr[1] = dv->type + 2; + break; - default: - return 0; /* silently discard it */ - } - ptr += vsa_llen; + default: + return 0; /* silently discard it */ + } + ptr += dv->length; - /* - * Allow for some continuation. - */ - if (vsa_offset) { - /* - * Allow TLV's to be encoded, if someone - * manages to somehow encode the sub-tlv's. - * - * FIXME: Keep track of room in the packet! - */ - if (vp->length > (((size_t) 254) - (ptr - start))) { - return rad_vp2continuation(vp, start, ptr); - } + /* + * WiMAX attributes take their own path through the + * system. + */ + if (dv->flags) return wimax2data(packet, original, secret, vp, + start, room, ptr); - ptr[0] = 0x00; - ptr++; + len = vp2data(packet, original, secret, vp, ptr, room); + if (len <= 0) return len; - /* - * sub-TLV's can only be in one format. - */ - if (vp->flags.is_tlv) { - *(ptr++) = (vp->attribute & 0xff00) >> 8; - tlv_length_ptr = ptr; - *(ptr++) = 2; - vsa_offset += 2; - } - } + if (dv->length != 0) ptr[-1] += len; - total_length += vsa_tlen + vsa_llen + vsa_offset; - if (vsa_length_ptr) *vsa_length_ptr += vsa_tlen + vsa_llen + vsa_offset; - *length_ptr += vsa_tlen + vsa_llen + vsa_offset; - } + start[1] += len; + + return start[1]; +} + +/* + * Swap 123a -> 0321 + */ +#define REORDER(x) ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000 >> 24)) + + +/* + * Encode a WiMAX sub-TLV. It must NOT be called for WiMAX + * attributes that are of type integer, string, etc. + */ +static int rad_encode_wimax(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, VALUE_PAIR *reply, + uint8_t *start, size_t room) +{ + int len, redo; + uint32_t lvalue; + uint8_t *ptr = start, *vsa = start; + uint32_t maxattr; + VALUE_PAIR *vp = reply; /* - * Insert tags for string attributes. They go BEFORE - * the string. + * Swap the order of the WiMAX hacks, to make later + * comparisons easier. */ - if (vp->flags.has_tag && (vp->type == PW_TYPE_STRING) && - (TAG_VALID(vp->flags.tag) || - (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD))) { - ptr[0] = vp->flags.tag; - end = vp2data(packet, original, secret, vp, ptr + 1, - (end - ptr) - 1); - } else { - end = vp2data(packet, original, secret, vp, ptr, - (end - ptr)); - } - if (!end) return -1; + maxattr = REORDER(vp->attribute); /* - * Insert tags for integer attributes. They go at the START - * of the integer, and over-write the first byte. + * Build the Vendor-Specific header */ - if (vp->flags.has_tag && (vp->type == PW_TYPE_INTEGER)) { - ptr[0] = vp->flags.tag; - } + ptr = start; + redo = 0; + +redo_vsa: + vsa = ptr; + + if (room < 9) return 0; + *ptr++ = PW_VENDOR_SPECIFIC; + *ptr++ = 9; + room -= 9; + lvalue = htonl(vp->vendor); + memcpy(ptr, &lvalue, 4); + ptr += 4; + *(ptr++) = vp->attribute & 0xff; + *(ptr++) = 3; + *(ptr++) = 0; /* continuation */ + room -= 9; + +redo_tlv: + len = tlv2data(packet, original, secret, vp, ptr, room, 1); + if (len < 0) return len; /* - * RFC 2865 section 5 says that zero-length attributes - * MUST NOT be sent. - * - * ... and the WiMAX forum ignores this... because of - * one vendor. Don't they have anything better to do - * with their time? + * Not enough room. Do a continuation. */ - if ((end == ptr) && - (vp->attribute != PW_CHARGEABLE_USER_IDENTITY)) return 0; + if ((len == 0) || ((vsa[VS_OFF] + len) > 255)) { + if (redo) return (start - vsa); - len = (end - ptr); + vsa[8] = 0x80; + redo = 1; + goto redo_vsa; + } + redo = 0; + + ptr += len; + vsa[VS_OFF] += len; + vsa[WM_OFF] += len; + + vp->flags.encoded = 1; + vp = vp->next; /* - * Update the various lengths. + * Look at the NEXT tlv. Ensure that we encode + * attributes into a common VSA *only* if they are for + * the same WiMAX VSA, AND if the TLVs are in numerically + * increasing order. */ - *length_ptr += len; - if (vsa_length_ptr) *vsa_length_ptr += len; - if (tlv_length_ptr) *tlv_length_ptr += len; - ptr += len; - total_length += len; + if (vp && vp->flags.is_tlv && (reply->vendor == vp->vendor) && + ((reply->attribute & 0xff) == (vp->attribute & 0xff))) { + uint32_t attr; - return total_length; /* of attribute */ + attr = REORDER(vp->attribute); + if (attr >= maxattr) { + maxattr = attr; + goto redo_tlv; + } + } + + return ptr - start; } @@ -1139,13 +1191,9 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, char ip_buffer[128]; /* - * For simplicity in the following logic, we allow - * the attributes to "overflow" the 4k maximum - * RADIUS packet size, by one attribute. - * - * It's uint32_t, for alignment purposes. + * A 4K packet, aligned on 64-bits. */ - uint32_t data[(MAX_PACKET_LEN + 256) / 4]; + uint64_t data[MAX_PACKET_LEN / sizeof(uint64_t)]; if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) { what = fr_packet_codes[packet->code]; @@ -1222,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,38 +1313,29 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, debug_pair(reply); /* - * Print them in order, even if they were encoded - * already. + * Skip attributes that are encoded. */ - len = 0; - if (reply->flags.encoded) goto next; - - if (reply->flags.is_tlv) { - VALUE_PAIR *tlv = rad_vp2tlv(reply); - if (tlv) { - tlv->next = reply->next; - reply->next = tlv; - } + if (reply->flags.encoded) continue; - /* - * The encoded flag MUST be set in reply! - */ - reply = reply->next; - } + if ((reply->vendor == VENDORPEC_WIMAX) && reply->flags.is_tlv) { + len = rad_encode_wimax(packet, original, secret, + reply, ptr, + ((uint8_t *) data) + sizeof(data) - ptr); + } else { - len = rad_vp2attr(packet, original, secret, reply, ptr); + len = rad_vp2attr(packet, original, secret, reply, ptr, + ((uint8_t *) data) + sizeof(data) - ptr); + } if (len < 0) return -1; /* - * Check that the packet is no more than 4k in - * size, AFTER writing the attribute past the 4k - * boundary, but BEFORE deciding to increase the - * size of the packet. Note that the 'data' - * buffer, above, is one attribute longer than - * necessary, in order to permit this overflow. + * Failed to encode the attribute, likely because + * the packet is full. */ - if ((total_length + len) > MAX_PACKET_LEN) { + if ((len == 0) && + (total_length > (sizeof(data) - 2 - reply->length))) { + DEBUG("WARNING: Attributes are too long for packet. Discarding data past %d bytes", total_length); break; } @@ -1773,6 +1814,18 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) while (count > 0) { /* + * We need at least 2 bytes to check the + * attribute header. + */ + if (count < 2) { + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute header overflows the packet", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr))); + return 0; + } + + /* * Attribute number zero is NOT defined. */ if (attr[0] == 0) { @@ -1788,7 +1841,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * fields. Anything shorter is an invalid attribute. */ if (attr[1] < 2) { - fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %d too short", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u too short", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), @@ -1797,6 +1850,19 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) } /* + * If there are fewer bytes in the packet than in the + * attribute, it's a bad packet. + */ + if (count < attr[1]) { + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u data overflows the packet", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr)), + attr[0]); + return 0; + } + + /* * Sanity check the attributes for length. */ switch (attr[0]) { @@ -2128,7 +2194,7 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, case PW_ACCOUNTING_REQUEST: if (calc_acctdigest(packet, secret) > 1) { fr_strerror_printf("Received %s packet " - "from %s with invalid signature! (Shared secret is incorrect.)", + "from client %s with invalid signature! (Shared secret is incorrect.)", fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, @@ -2149,13 +2215,12 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, rcode = calc_replydigest(packet, original, secret); if (rcode > 1) { fr_strerror_printf("Received %s packet " - "from client %s port %d with invalid signature (err=%d)! (Shared secret is incorrect.)", + "from home server %s port %d with invalid signature! (Shared secret is incorrect.)", fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, buffer, sizeof(buffer)), - packet->src_port, - rcode); + packet->src_port); return -1; } break; @@ -2193,6 +2258,15 @@ static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet, vp->next = NULL; /* + * It's supposed to be a fixed length, but we found + * a different length instead. Make it type "octets", + * and do no more processing on it. + */ + if ((vp->flags.length > 0) && (vp->flags.length != length)) { + goto raw; + } + + /* * Handle tags. */ if (vp->flags.has_tag) { @@ -2205,7 +2279,10 @@ static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet, vp->flags.tag = data[0]; if ((vp->type == PW_TYPE_STRING) || - (vp->type == PW_TYPE_OCTETS)) offset = 1; + (vp->type == PW_TYPE_OCTETS)) { + if (length == 0) goto raw; + offset = 1; + } } } @@ -2409,16 +2486,35 @@ static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet, default: raw: - vp->type = PW_TYPE_OCTETS; - vp->length = length; - memcpy(vp->vp_octets, data, length); - - /* - * Ensure there's no encryption or tag stuff, - * we just pass the attribute as-is. + * Change the name to show the user that the + * attribute is not of the correct format. */ - memset(&vp->flags, 0, sizeof(vp->flags)); + { + int attr = vp->attribute; + int vendor = vp->vendor; + VALUE_PAIR *vp2; + + vp2 = pairalloc(NULL); + if (!vp2) { + pairfree(&vp); + return NULL; + } + pairfree(&vp); + vp = vp2; + + /* + * This sets "vp->flags" appropriately, + * and vp->type. + */ + if (!paircreate_raw(attr, vendor, PW_TYPE_OCTETS, vp)) { + return NULL; + } + + vp->length = length; + memcpy(vp->vp_octets, data, length); + } + break; } return vp; @@ -2547,6 +2643,74 @@ static uint8_t *rad_coalesce(unsigned int attribute, int vendor, return tlv_data; } + +/* + * Walk over Evil WIMAX TLVs, creating attributes. + */ +static VALUE_PAIR *tlv2wimax(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, + int attribute, int vendor, + uint8_t *ptr, size_t len, int nest) +{ + VALUE_PAIR *head = NULL; + VALUE_PAIR **tail = &head; + VALUE_PAIR *vp; + uint8_t *y; /* why do I need to do this? */ + + if (nest > fr_wimax_max_tlv) return NULL; + + /* + * Sanity check the attribute. + */ + for (y = ptr; y < (ptr + len); y += y[1]) { + if ((y[0] == 0) || ((y + 2) > (ptr + len)) || + (y[1] < 2) || ((y + y[1]) > (ptr + len))) { + return NULL; + } + + /* + * Attribute number is too large for us to + * represent it in our horrible internal + * representation. + */ + if ((ptr[0] & ~fr_wimax_mask[nest]) != 0) { + return NULL; + } + } + + for (y = ptr; y < (ptr + len); y += y[1]) { + DICT_ATTR *da; + + da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[nest]), vendor); + if (da && (da->type == PW_TYPE_TLV)) { + vp = tlv2wimax(packet, original, secret, + attribute | (ptr[0] << fr_wimax_shift[nest]), + vendor, ptr + 2, ptr[1] - 2, + nest + 1); + if (!vp) goto error; + } else { + vp = paircreate(attribute | (ptr[0] << fr_wimax_shift[nest]), vendor, + PW_TYPE_OCTETS); + if (!vp) { + error: + pairfree(&head); + return NULL; + } + + if (!data2vp(packet, original, secret, + y[1] - 2, y + 2, vp)) { + goto error; + } + } + + *tail = vp; + while (*tail) tail = &((*tail)->next); + } + + return head; +} + /* * Start at the *data* portion of a continued attribute. search * through the rest of the attributes to find a matching one, and @@ -2564,6 +2728,7 @@ static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet, uint8_t *ptr; uint8_t *tlv_data; VALUE_PAIR *vp, *head, **tail; + DICT_ATTR *tlv_da; /* * Ensure we have data that hasn't been split across @@ -2622,6 +2787,7 @@ static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet, (ptr[1] > left)) { goto not_well_formed; } + left -= ptr[1]; } @@ -2634,20 +2800,32 @@ static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet, for (ptr = tlv_data; ptr != (tlv_data + tlv_length); ptr += ptr[1]) { - vp = paircreate(attribute | (ptr[0] << 8), vendor, PW_TYPE_OCTETS); - if (!vp) { - pairfree(&head); - goto not_well_formed; - } - if (!data2vp(packet, original, secret, - ptr[1] - 2, ptr + 2, vp)) { - pairfree(&head); - goto not_well_formed; + tlv_da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[1]), vendor); + if (tlv_da && (tlv_da->type == PW_TYPE_TLV)) { + vp = tlv2wimax(packet, original, secret, + attribute | (ptr[0] << 8), + vendor, ptr + 2, ptr[1] - 2, 2); + + if (!vp) goto error; + } else { + vp = paircreate(attribute | (ptr[0] << fr_wimax_shift[1]), vendor, + PW_TYPE_OCTETS); + if (!vp) { + error: + pairfree(&head); + goto not_well_formed; + } + + if (!data2vp(packet, original, secret, + ptr[1] - 2, ptr + 2, vp)) { + goto error; + } } *tail = vp; - tail = &(vp->next); + + while (*tail) tail = &((*tail)->next); } /* @@ -2662,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, @@ -2671,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; @@ -2780,13 +3027,10 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, if (myvendor == 0) goto create_pair; /* - * This is an implementation issue. - * We currently pack vendor into the upper - * 16 bits of a 32-bit attribute number, - * so we can't handle vendor numbers larger - * than 16 bits. + * Allow vendors up to 2^24. Past that, + * get confused. */ - if (myvendor > 65535) goto create_pair; + if (myvendor > FR_MAX_VENDOR) goto create_pair; vsa_tlen = vsa_llen = 1; vsa_offset = 0; @@ -2935,7 +3179,6 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, ptr += vsa_llen + vsa_offset; vendorlen -= vsa_tlen + vsa_llen + vsa_offset + attrlen; - if (vendorlen == 0) vendorcode = 0; packet_length -= (vsa_tlen + vsa_llen + vsa_offset); /* @@ -3029,6 +3272,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, } next: + if (vendorlen == 0) vendorcode = 0; ptr += attrlen; packet_length -= attrlen; }