+static void rad_sortvp(VALUE_PAIR **head)
+{
+ int swapped;
+ VALUE_PAIR *vp, **tail;
+
+ /*
+ * Walk over the VP's, sorting them in order. Did I
+ * mention that I hate WiMAX continuations?
+ *
+ * And bubble sort! WTF is up with that?
+ */
+ do {
+ swapped = 0;
+ tail = head;
+ while (*tail) {
+ vp = *tail;
+ if (!vp->next) break;
+
+ if (vp->attribute > vp->next->attribute) {
+ *tail = vp->next;
+ vp->next = (*tail)->next;
+ (*tail)->next = vp;
+ swapped = 1;
+ }
+ tail = &(vp->next);
+ }
+ } while (swapped);
+}
+
+
+/*
+ * Walk the packet, looking for continuations of this attribute.
+ *
+ * This is (worst-case) O(N^2) in the number of RADIUS
+ * attributes. That happens only when perverse clients create
+ * continued attributes, AND separate the fragmented portions
+ * with a lot of other attributes.
+ *
+ * Sane clients should put the fragments next to each other, in
+ * which case this is O(N), in the number of fragments.
+ */
+static uint8_t *rad_coalesce(unsigned int attribute, int vendor,
+ size_t length, uint8_t *data,
+ size_t packet_length, size_t *ptlv_length)
+
+{
+ uint32_t lvalue;
+ size_t tlv_length = length;
+ uint8_t *ptr, *tlv, *tlv_data;
+
+ for (ptr = data + length;
+ ptr != (data + packet_length);
+ ptr += ptr[1]) {
+ /* FIXME: Check that there are 6 bytes of data here... */
+ if ((ptr[0] != PW_VENDOR_SPECIFIC) ||
+ (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */
+ (ptr[2] != 0) || (ptr[3] != 0) || /* our requirement */
+ (ptr[4] != ((vendor >> 8) & 0xff)) ||
+ (ptr[5] != (vendor & 0xff))) {
+ continue;
+ }
+
+ memcpy(&lvalue, ptr + 2, 4); /* Vendor Id */
+ lvalue = ntohl(lvalue);
+ lvalue <<= 16;
+ lvalue |= ptr[2 + 4]; /* add in VSA number */
+ if (lvalue != attribute) continue;
+
+ /*
+ * If the vendor-length is too small, it's badly
+ * formed, so we stop.
+ */
+ if ((ptr[2 + 4 + 1]) < 3) break;
+
+ tlv_length += ptr[2 + 4 + 1] - 3;
+ if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
+ }
+
+ tlv = tlv_data = malloc(tlv_length);
+ if (!tlv_data) return NULL;
+
+ memcpy(tlv, data, length);
+ tlv += length;
+
+ /*
+ * Now we walk the list again, copying the data over to
+ * our newly created memory.
+ */
+ for (ptr = data + length;
+ ptr != (data + packet_length);
+ ptr += ptr[1]) {
+ int this_length;
+
+ if ((ptr[0] != PW_VENDOR_SPECIFIC) ||
+ (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */
+ (ptr[2] != 0) || (ptr[3] != 0)) { /* our requirement */
+ continue;
+ }
+
+ memcpy(&lvalue, ptr + 2, 4);
+ lvalue = ntohl(lvalue);
+ lvalue <<= 16;
+ lvalue |= ptr[2 + 4];
+ if (lvalue != attribute) continue;
+
+ /*
+ * If the vendor-length is too small, it's badly
+ * formed, so we stop.
+ */
+ if ((ptr[2 + 4 + 1]) < 3) break;
+
+ this_length = ptr[2 + 4 + 1] - 3;
+ memcpy(tlv, ptr + 2 + 4 + 3, this_length);
+ tlv += this_length;
+
+ ptr[2 + 4] = 0; /* What a hack! */
+ if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
+ }
+
+ *ptlv_length = tlv_length;
+ 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
+ * add it's contents to our contents.
+ */
+static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int attribute,
+ int vendor,
+ int length, /* CANNOT be zero */
+ uint8_t *data, size_t packet_length,
+ int flag, DICT_ATTR *da)
+{
+ size_t tlv_length, left;
+ 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
+ * multiple attributes.
+ */
+ if (flag) {
+ tlv_data = rad_coalesce(attribute, vendor, length,
+ data, packet_length, &tlv_length);
+ if (!tlv_data) return NULL;
+ } else {
+ tlv_data = data;
+ tlv_length = length;
+ }
+
+ /*
+ * Non-TLV types cannot be continued across multiple
+ * attributes. This is true even of keys that are
+ * encrypted with the tunnel-password method. The spec
+ * says that they can be continued... but also that the
+ * keys are 160 bits, which means that they CANNOT be
+ * continued. <sigh>
+ *
+ * Note that we don't check "flag" here. The calling
+ * code ensures that
+ */
+ if (!da || (da->type != PW_TYPE_TLV)) {
+ not_well_formed:
+ if (tlv_data == data) { /* true if we had 'goto' */
+ tlv_data = malloc(tlv_length);
+ if (!tlv_data) return NULL;
+ memcpy(tlv_data, data, tlv_length);
+ }
+
+ vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
+ if (!vp) return NULL;
+
+ vp->type = PW_TYPE_TLV;
+ vp->flags.encrypt = FLAG_ENCRYPT_NONE;
+ vp->flags.has_tag = 0;
+ vp->flags.is_tlv = 0;
+ vp->vp_tlv = tlv_data;
+ vp->length = tlv_length;
+ return vp;
+ } /* else it WAS a TLV, go decode the sub-tlv's */
+
+ /*
+ * Now (sigh) we walk over the TLV, seeing if it is
+ * well-formed.
+ */
+ left = tlv_length;
+ for (ptr = tlv_data;
+ ptr != (tlv_data + tlv_length);
+ ptr += ptr[1]) {
+ if ((left < 2) ||
+ (ptr[1] < 2) ||
+ (ptr[1] > left)) {
+ goto not_well_formed;
+ }
+
+ left -= ptr[1];
+ }
+
+ /*
+ * Now we walk over the TLV *again*, creating sub-tlv's.
+ */
+ head = NULL;
+ tail = &head;
+
+ for (ptr = tlv_data;
+ ptr != (tlv_data + tlv_length);
+ ptr += ptr[1]) {
+
+ 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;
+
+ while (*tail) tail = &((*tail)->next);
+ }
+
+ /*
+ * TLV's MAY be continued, but sometimes they're not.
+ */
+ if (tlv_data != data) free(tlv_data);
+
+ if (head->next) rad_sortvp(&head);
+
+ return head;
+}
+
+
+/*
+ * 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,
+ const RADIUS_PACKET *original,
+ const char *secret, int attribute, int vendor,
+ int length, const uint8_t *data)
+{
+ 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;
+
+ return data2vp(packet, original, secret, length, data, vp);
+}
+