* 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,
}
fr_MD5Final(digest, &context);
+
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + 2 + n] ^= digest[i];
}
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.
* 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:
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:
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;
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,
+ 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] = (vp->attribute >> fr_wimax_shift[nest]) & fr_wimax_mask[nest];
+ ptr[1] = 2;
+
+ /*
+ * 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;
+
+ ptr[1] += len;
+
+ return ptr[1];
+}
+
static int wimax2data(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
const char *secret, const VALUE_PAIR *vp,
*/
if (!vp->flags.is_tlv) {
len = vp2data(packet, original, secret, vp, ptr, room);
- if (len <= 0) return -1;
+ } 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];
+}
- 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;
+
+ } else if (vp->flags.extended_flags) {
+ if (room < 4) return 0;
+
+ ptr[1] = 4;
+ ptr[2] = (attribute & 0xff00) >> 8;
+ ptr[3] = 0;
}
/*
- * Otherwise it's a TLV. We need to do more work to
- * encode it.
+ * 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];
- return 0;
-}
+ 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.
* 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);
}
/*
*/
#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;
*/
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;
attr = REORDER(vp->attribute);
if (attr >= maxattr) {
maxattr = attr;
- goto redo;
+ goto redo_tlv;
}
}
- return total_len;
+ return ptr - start;
}
*/
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
*/
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);
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;
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) {
* 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)),
}
/*
+ * 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]) {
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,
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;
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) {
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;
+ }
}
}
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;
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.
*/
(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;
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;
}
}
/*
+ * 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,
{
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;
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;
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);