Fixes from clang / scan-build
[freeradius.git] / src / lib / radius.c
index 66ee8e7..439feec 100644 (file)
@@ -233,7 +233,7 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
         *      And if they don't specify a source IP address, don't
         *      use udpfromto.
         */
-       if ((dst_ipaddr->af == AF_INET) ||
+       if ((dst_ipaddr->af == AF_INET) &&
            (src_ipaddr->af != AF_UNSPEC)) {
                return sendfromto(sockfd, data, data_len, flags,
                                  (struct sockaddr *)&src, sizeof_src,
@@ -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];
                }
@@ -738,6 +739,9 @@ static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                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.
@@ -745,7 +749,7 @@ static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      This is ONLY a problem if we have multiple VSA's
                 *      in one Vendor-Specific, though.
                 */
-               if (room < 19) return 0;
+               if (room < (18 + lvalue)) return 0;
 
                switch (packet->code) {
                case PW_AUTHENTICATION_ACK:
@@ -756,8 +760,10 @@ static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                                fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name);
                                return -1;
                        }
-                       ptr[0] = vp->flags.tag;
-                       make_tunnel_passwd(ptr + 1, &len, data, len, room - 1,
+
+                       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:
@@ -810,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;
@@ -818,29 +825,36 @@ static int rad_vp2rfc(const RADIUS_PACKET *packet,
        return ptr[1];
 }
 
+extern int fr_wimax_max_tlv;
+extern int fr_wimax_shift[];
+extern int fr_wimax_mask[];
+
 static int tlv2data(const RADIUS_PACKET *packet,
                    const RADIUS_PACKET *original,
-                   const char *secret, const VALUE_PAIR *vp, int attribute,
-                   uint8_t *ptr, size_t room)
+                   const char *secret, const VALUE_PAIR *vp,
+                   uint8_t *ptr, size_t room, int nest)
 {
        int len;
 
+       if (nest > fr_wimax_max_tlv) return -1;
+
        if (room < 2) return 0;
        room -= 2;
 
-       ptr[0] = attribute & 0xff;
+       ptr[0] = (vp->attribute >> fr_wimax_shift[nest]) & fr_wimax_mask[nest];
        ptr[1] = 2;
 
        /*
         *      No more nested TLVs: pack the data.
         */
-       if ((attribute & ~0xff) == 0) {
+       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, attribute >> 8,
-                              ptr + 2, room);
+               len = tlv2data(packet, original, secret, vp, ptr + 2, room,
+                              nest + 1);
        }
-       if (len <= 0) return -1;
+       if (len <= 0) return len;
 
        ptr[1] += len;
 
@@ -896,11 +910,10 @@ static int wimax2data(const RADIUS_PACKET *packet,
        if (!vp->flags.is_tlv) {
                len = vp2data(packet, original, secret, vp, ptr, room);
        } else {
-               len = tlv2data(packet, original, secret, vp, vp->attribute >> 8,
-                              ptr, room);
+               len = tlv2data(packet, original, secret, vp, ptr, room, 1);
        }
 
-       if (len <= 0) return -1;
+       if (len <= 0) return len;
 
        start[VS_OFF] += len;
        start[WM_OFF] += len;
@@ -909,6 +922,49 @@ 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;
+
+       } else if (vp->flags.extended_flags) {
+               if (room < 4) return 0;
+
+               ptr[1] = 4;
+               ptr[2] = (attribute & 0xff00) >> 8;
+               ptr[3] = 0;
+       }
+
+       /*
+        *      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.
  */
@@ -925,23 +981,13 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      RFC format attributes take the fast path.
         */
        if (vp->vendor == 0) {
-               len = rad_vp2rfc(packet, original, secret, vp,
-                                vp->attribute, start, room);
-               if (len < 0) return -1;
-
-               /*
-                *      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?
-                */
-               if ((len == 0) &&
-                   (vp->attribute != PW_CHARGEABLE_USER_IDENTITY)) return 0;
-
-               return len;
+               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);
        }
 
        /*
@@ -1040,14 +1086,19 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
  */
 #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, total_len = 0;
-       uint8_t *wimax = NULL;
-       uint8_t *ptr = start;
+       int len, redo;
+       uint32_t lvalue;
+       uint8_t *ptr, *vsa;
        uint32_t maxattr;
        VALUE_PAIR *vp = reply;
 
@@ -1057,40 +1108,46 @@ static int rad_encode_wimax(const RADIUS_PACKET *packet,
         */
        maxattr = REORDER(vp->attribute);
 
-redo:
-       len = rad_vp2attr(packet, original, secret, vp, ptr,
-                         (start + room) - ptr);
-       if (len <= 0) return total_len;
-
        /*
-        *      After adding an attribute with the simplest encoding,
-        *      check to see if we can append it to the previous one.
+        *      Build the Vendor-Specific header
         */
-       if (wimax) {
-               if ((wimax[1] + (ptr[1] - 6)) <= 255) {
-                       unsigned int hack;
+       ptr = start;
+       redo = 0;
 
-                       hack = ptr[7] - 3;
-                       memmove(ptr, ptr + 9, hack);
-                       wimax[1] += hack;
-                       wimax[7] += hack;
-                       len -= 9;
+redo_vsa:
+       vsa = ptr;
 
-                       /*
-                        *      See if we can nest sub-TLVs, too, in
-                        *      order to shorten the encoding.
-                        */
+       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;
 
-               } else {
-                       wimax[8] = 0x80; /* set continuation */
-                       wimax = ptr;
-               }
-       } else {
-               wimax = ptr;
+redo_tlv:
+       len = tlv2data(packet, original, secret, vp, ptr, room, 1);
+       if (len < 0) return len;
+
+       /*
+        *      Not enough room.  Do a continuation.
+        */
+       if ((len == 0) || ((vsa[VS_OFF] + len) > 255)) {
+               if (redo) return (start - vsa);
+
+               vsa[8] = 0x80;
+               redo = 1;
+               goto redo_vsa;
        }
+       redo = 0;
 
-       total_len += len;
        ptr += len;
+       vsa[VS_OFF] += len;
+       vsa[WM_OFF] += len;
 
        vp->flags.encoded = 1;
        vp = vp->next;
@@ -1108,11 +1165,11 @@ redo:
                attr = REORDER(vp->attribute);
                if (attr >= maxattr) {
                        maxattr = attr;
-                       goto redo;
+                       goto redo_tlv;
                }
        }
 
-       return total_len;
+       return ptr - start;
 }
 
 
@@ -1210,10 +1267,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
@@ -1255,7 +1314,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);
@@ -1267,6 +1326,16 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 
                if (len < 0) return -1;
 
+               /*
+                *      Failed to encode the attribute, likely because
+                *      the packet is full.
+                */
+               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;
+               }
+
        next:
                ptr += len;
                total_length += len;
@@ -1742,6 +1811,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) {
@@ -1757,7 +1838,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)),
@@ -1766,6 +1847,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]) {
@@ -2097,7 +2191,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,
@@ -2118,13 +2212,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;
@@ -2162,6 +2255,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) {
@@ -2174,7 +2276,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;
+                       }
                }
        }
 
@@ -2378,16 +2483,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;
@@ -2516,22 +2640,23 @@ static uint8_t *rad_coalesce(unsigned int attribute, int vendor,
        return tlv_data;
 }
 
+
 /*
- *     Walk over Evil WIMAX Hell, creating attributes.
- *
- *     Won't someone think of the children?  What if they read this code?
+ *     Walk over Evil WIMAX TLVs, creating attributes.
  */
-static VALUE_PAIR *recurse_evil(const RADIUS_PACKET *packet,
-                               const RADIUS_PACKET *original,
-                               const char *secret,
-                               int attribute, int vendor,
-                               uint8_t *ptr, size_t len)
+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.
         */
@@ -2540,24 +2665,44 @@ static VALUE_PAIR *recurse_evil(const RADIUS_PACKET *packet,
                    (y[1] < 2) || ((y + y[1]) > (ptr + len))) {
                        return NULL;
                }
-       }
 
-       for (y = ptr; y < (ptr + len); y += y[1]) {
-               vp = paircreate(attribute | (ptr[0] << 16), vendor,
-                               PW_TYPE_OCTETS);
-               if (!vp) {
-               error:
-                       pairfree(&head);
+               /*
+                *      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;
                }
+       }
 
-               if (!data2vp(packet, original, secret,
-                            y[1] - 2, y + 2, vp)) {
-                       goto error;
+       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;
-               tail = &(vp->next);
+               while (*tail) tail = &((*tail)->next);
        }
 
        return head;
@@ -2653,28 +2798,25 @@ static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet,
             ptr != (tlv_data + tlv_length);
             ptr += ptr[1]) {
 
-               tlv_da = dict_attrbyvalue(attribute | (ptr[0] << 8), vendor);
+               tlv_da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[1]), vendor);
                if (tlv_da && (tlv_da->type == PW_TYPE_TLV)) {
-                       vp = recurse_evil(packet, original, secret,
-                                         attribute | (ptr[0] << 8),
-                                         vendor, ptr + 2, ptr[1] - 2);
+                       vp = tlv2wimax(packet, original, secret,
+                                      attribute | (ptr[0] << 8),
+                                      vendor, ptr + 2, ptr[1] - 2, 2);
 
-                       if (!vp) {
-                               pairfree(&head);
-                               goto not_well_formed;
-                       }
+                       if (!vp) goto error;
                } else {
-                       vp = paircreate(attribute | (ptr[0] << 8), vendor,
+                       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)) {
-                               pairfree(&head);
-                               goto not_well_formed;
+                               goto error;
                        }
                }
 
@@ -2695,6 +2837,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,
@@ -2704,6 +2871,58 @@ 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) {       /* flags.extended MUST be set */
+
+                       /*
+                        *      MUST have at least an "extended type" octet.
+                        */
+                       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);
+                       }
+               }
+
+               /*
+                *      We could avoid another dictionary lookup here
+                *      by using pairalloc(da), but it's not serious...
+                */
+       }
        vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
        if (!vp) return NULL;
 
@@ -2813,13 +3032,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;
@@ -3486,7 +3702,7 @@ void fr_rand_seed(const void *data, size_t size)
                        size_t total;
                        ssize_t this;
 
-                       total = this = 0;
+                       total = 0;
                        while (total < sizeof(fr_rand_pool.randrsl)) {
                                this = read(fd, fr_rand_pool.randrsl,
                                            sizeof(fr_rand_pool.randrsl) - total);