-/*
- * radius.c Functions to send/receive radius packets.
+/**
+ * @file radius.c
+ * @brief Functions to send/receive radius packets.
*
* Version: $Id$
*
#include <malloc.h>
#endif
+#if 0
+#define VP_TRACE if (fr_debug_flag) printf
+#else
+#define VP_TRACE(_x, ...)
+#endif
+
+
/*
* The RFC says 4096 octets max, and most packets are less than 256.
*/
return;
}
-/*
- * Wrapper for sendto which handles sendfromto, IPv6, and all
+static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+
+static void print_hex_data(const uint8_t *ptr, int attrlen, int depth)
+{
+ int i;
+
+ for (i = 0; i < attrlen; i++) {
+ if ((i > 0) && ((i & 0x0f) == 0x00))
+ fprintf(fr_log_fp, "%.*s", depth, tabs);
+ fprintf(fr_log_fp, "%02x ", ptr[i]);
+ if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
+ }
+ if ((i & 0x0f) != 0) fprintf(fr_log_fp, "\n");
+}
+
+
+void rad_print_hex(RADIUS_PACKET *packet)
+{
+ int i;
+
+ if (!packet->data || !fr_log_fp) return;
+
+ fprintf(fr_log_fp, " Code:\t\t%u\n", packet->data[0]);
+ fprintf(fr_log_fp, " Id:\t\t%u\n", packet->data[1]);
+ fprintf(fr_log_fp, " Length:\t%u\n", ((packet->data[2] << 8) |
+ (packet->data[3])));
+ fprintf(fr_log_fp, " Vector:\t");
+ for (i = 4; i < 20; i++) {
+ fprintf(fr_log_fp, "%02x", packet->data[i]);
+ }
+ fprintf(fr_log_fp, "\n");
+
+ if (packet->data_len > 20) {
+ int total;
+ const uint8_t *ptr;
+ fprintf(fr_log_fp, " Data:");
+
+ total = packet->data_len - 20;
+ ptr = packet->data + 20;
+
+ while (total > 0) {
+ int attrlen;
+ unsigned int vendor = 0;
+
+ fprintf(fr_log_fp, "\t\t");
+ if (total < 2) { /* too short */
+ fprintf(fr_log_fp, "%02x\n", *ptr);
+ break;
+ }
+
+ if (ptr[1] > total) { /* too long */
+ for (i = 0; i < total; i++) {
+ fprintf(fr_log_fp, "%02x ", ptr[i]);
+ }
+ break;
+ }
+
+ fprintf(fr_log_fp, "%02x %02x ", ptr[0], ptr[1]);
+ attrlen = ptr[1] - 2;
+
+ if ((ptr[0] == PW_VENDOR_SPECIFIC) &&
+ (attrlen > 4)) {
+ vendor = (ptr[3] << 16) | (ptr[4] << 8) | ptr[5];
+ fprintf(fr_log_fp, "%02x%02x%02x%02x (%u) ",
+ ptr[2], ptr[3], ptr[4], ptr[5], vendor);
+ attrlen -= 4;
+ ptr += 6;
+ total -= 6;
+
+ } else {
+ ptr += 2;
+ total -= 2;
+ }
+
+ print_hex_data(ptr, attrlen, 3);
+
+ ptr += attrlen;
+ total -= attrlen;
+ }
+ }
+ fflush(stdout);
+}
+
+/**
+ * @brief Wrapper for sendto which handles sendfromto, IPv6, and all
* possible combinations.
*/
static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
fr_ipaddr_t *src_ipaddr, int src_port,
fr_ipaddr_t *dst_ipaddr, int dst_port)
{
+ int rcode;
struct sockaddr_storage dst;
socklen_t sizeof_dst;
#ifdef WITH_UDPFROMTO
/*
- * Only IPv4 is supported for udpfromto.
- *
* And if they don't specify a source IP address, don't
* use udpfromto.
*/
- if ((dst_ipaddr->af == AF_INET) ||
- (src_ipaddr->af != AF_UNSPEC)) {
- return sendfromto(sockfd, data, data_len, flags,
- (struct sockaddr *)&src, sizeof_src,
- (struct sockaddr *)&dst, sizeof_dst);
+ if (((dst_ipaddr->af == AF_INET) || (dst_ipaddr->af == AF_INET6)) &&
+ (src_ipaddr->af != AF_UNSPEC) &&
+ !fr_inaddr_any(src_ipaddr)) {
+ rcode = sendfromto(sockfd, data, data_len, flags,
+ (struct sockaddr *)&src, sizeof_src,
+ (struct sockaddr *)&dst, sizeof_dst);
+ goto done;
}
#else
src_ipaddr = src_ipaddr; /* -Wunused */
#endif
/*
- * No udpfromto, OR an IPv6 socket, fail gracefully.
+ * No udpfromto, fail gracefully.
*/
- return sendto(sockfd, data, data_len, flags,
- (struct sockaddr *) &dst, sizeof_dst);
+ rcode = sendto(sockfd, data, data_len, flags,
+ (struct sockaddr *) &dst, sizeof_dst);
+#ifdef WITH_UDPFROMTO
+done:
+#endif
+ if (rcode < 0) {
+ DEBUG("rad_send() failed: %s\n", strerror(errno));
+ }
+
+ return rcode;
}
}
-/*
- * wrapper for recvfrom, which handles recvfromto, IPv6, and all
+/**
+ * @brief wrapper for recvfrom, which handles recvfromto, IPv6, and all
* possible combinations.
*/
static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
* packet after "len" bytes.
*/
#ifdef WITH_UDPFROMTO
- if (dst.ss_family == AF_INET) {
+ if ((dst.ss_family == AF_INET) || (dst.ss_family == AF_INET6)) {
data_len = recvfromto(sockfd, buf, len, flags,
(struct sockaddr *)&src, &sizeof_src,
(struct sockaddr *)&dst, &sizeof_dst);
} else
#endif
/*
- * No udpfromto, OR an IPv6 socket. Fail gracefully.
+ * No udpfromto, fail gracefully.
*/
data_len = recvfrom(sockfd, buf, len, flags,
(struct sockaddr *)&src, &sizeof_src);
#define AUTH_PASS_LEN (AUTH_VECTOR_LEN)
-/*************************************************************************
- *
- * Function: make_secret
- *
- * Purpose: Build an encrypted secret value to return in a reply
- * packet. The secret is hidden by xoring with a MD5 digest
+/**
+ * @brief Build an encrypted secret value to return in a reply packet
+ *
+ * The secret is hidden by xoring with a MD5 digest
* created from the shared secret and the authentication
* vector. We put them into MD5 in the reverse order from
* that used when encrypting passwords to RADIUS.
*
- *************************************************************************/
+ */
static void make_secret(uint8_t *digest, const uint8_t *vector,
const char *secret, const uint8_t *value)
{
}
#define MAX_PASS_LEN (128)
-static void make_passwd(uint8_t *output, int *outlen,
- const uint8_t *input, int inlen,
+static void make_passwd(uint8_t *output, ssize_t *outlen,
+ const uint8_t *input, size_t inlen,
const char *secret, const uint8_t *vector)
{
FR_MD5_CTX context, old;
memcpy(output, passwd, len);
}
-static void make_tunnel_passwd(uint8_t *output, int *outlen,
- const uint8_t *input, int inlen, int room,
+static void make_tunnel_passwd(uint8_t *output, ssize_t *outlen,
+ const uint8_t *input, size_t inlen, size_t room,
const char *secret, const uint8_t *vector)
{
FR_MD5_CTX context, old;
}
fr_MD5Final(digest, &context);
+
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + 2 + n] ^= digest[i];
}
memcpy(output, passwd, len + 2);
}
-static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR *vp, uint8_t *ptr,
- int offset, int room)
+extern int fr_attr_max_tlv;
+extern int fr_attr_shift[];
+extern int fr_attr_mask[];
+
+static int do_next_tlv(const VALUE_PAIR *vp, const VALUE_PAIR *next, int nest)
+{
+ unsigned int tlv1, tlv2;
+
+ if (nest > fr_attr_max_tlv) return 0;
+
+ if (!vp) return 0;
+
+ /*
+ * Keep encoding TLVs which have the same scope.
+ * e.g. two attributes of:
+ * ATTR.TLV1.TLV2.TLV3 = data1
+ * ATTR.TLV1.TLV2.TLV4 = data2
+ * both get put into a container of "ATTR.TLV1.TLV2"
+ */
+
+ /*
+ * Nothing to follow, we're done.
+ */
+ if (!next) return 0;
+
+ /*
+ * Not from the same vendor, skip it.
+ */
+ if (vp->vendor != next->vendor) return 0;
+
+ /*
+ * In a different TLV space, skip it.
+ */
+ tlv1 = vp->attribute;
+ tlv2 = next->attribute;
+
+ tlv1 &= ((1 << fr_attr_shift[nest]) - 1);
+ tlv2 &= ((1 << fr_attr_shift[nest]) - 1);
+
+ if (tlv1 != tlv2) return 0;
+
+ return 1;
+}
+
+
+static ssize_t vp2data_any(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int nest,
+ const VALUE_PAIR **pvp,
+ uint8_t *start, size_t room);
+
+static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp,
+ unsigned int attribute, uint8_t *ptr, size_t room);
+
+/**
+ * @brief This is really a sub-function of vp2data_any(). It encodes
+ * the *data* portion of the TLV, and assumes that the encapsulating
+ * attribute has already been encoded.
+ */
+static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int nest,
+ const VALUE_PAIR **pvp,
+ uint8_t *start, size_t room)
+{
+ ssize_t len;
+ size_t my_room;
+ uint8_t *ptr = start;
+ const VALUE_PAIR *vp = *pvp;
+ const VALUE_PAIR *svp = vp;
+
+ if (!svp) return 0;
+
+#ifndef NDEBUG
+ if (nest > fr_attr_max_tlv) {
+ fr_strerror_printf("vp2data_tlvs: attribute nesting overflow");
+ return -1;
+ }
+#endif
+
+ while (vp) {
+ if (room < 2) return ptr - start;
+
+ ptr[0] = (vp->attribute >> fr_attr_shift[nest]) & fr_attr_mask[nest];
+ ptr[1] = 2;
+
+ my_room = room;
+ if (room > 255) my_room = 255;
+
+ len = vp2data_any(packet, original, secret, nest,
+ &vp, ptr + 2, my_room - 2);
+ if (len < 0) return len;
+ if (len == 0) return ptr - start;
+ /* len can NEVER be more than 253 */
+
+ ptr[1] += len;
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
+ print_hex_data(ptr + 2, len, 3);
+ }
+#endif
+
+ room -= ptr[1];
+ ptr += ptr[1];
+ *pvp = vp;
+
+ if (!do_next_tlv(svp, vp, nest)) break;
+ }
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ DICT_ATTR *da;
+
+ da = dict_attrbyvalue(svp->attribute & ((1 << fr_attr_shift[nest ]) - 1), svp->vendor);
+ if (da) fprintf(fr_log_fp, "\t%s = ...\n", da->name);
+ }
+#endif
+
+ return ptr - start;
+}
+
+
+/**
+ * @brief Encodes the data portion of an attribute.
+ * @return -1 on error, or the length of the data portion.
+ */
+static ssize_t vp2data_any(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int nest,
+ const VALUE_PAIR **pvp,
+ uint8_t *start, size_t room)
{
uint32_t lvalue;
- size_t len;
+ ssize_t len;
const uint8_t *data;
+ uint8_t *ptr = start;
uint8_t array[4];
+ uint64_t lvalue64;
+ const VALUE_PAIR *vp = *pvp;
+
+ /*
+ * See if we need to encode a TLV. The low portion of
+ * the attribute has already been placed into the packer.
+ * If there are still attribute bytes left, then go
+ * encode them as TLVs.
+ *
+ * If we cared about the stack, we could unroll the loop.
+ */
+ if (vp->flags.is_tlv && (nest < fr_attr_max_tlv) &&
+ ((vp->attribute >> fr_attr_shift[nest + 1]) != 0)) {
+ return vp2data_tlvs(packet, original, secret, nest + 1, pvp,
+ start, room);
+ }
+
+ debug_pair(vp);
/*
* Set up the default sources for the data.
data = vp->vp_octets;
len = vp->length;
+ /*
+ * Short-circuit it for long attributes. They can't be
+ * encrypted, tagged, etc.
+ */
+ if ((vp->type & PW_FLAG_LONG) != 0) goto do_tlv;
+
switch(vp->type) {
case PW_TYPE_STRING:
case PW_TYPE_OCTETS:
len = 1; /* just in case */
array[0] = vp->vp_integer & 0xff;
data = array;
- offset = 0;
break;
case PW_TYPE_SHORT:
array[0] = (vp->vp_integer >> 8) & 0xff;
array[1] = vp->vp_integer & 0xff;
data = array;
- offset = 0;
break;
case PW_TYPE_INTEGER:
len = 4; /* just in case */
lvalue = htonl(vp->vp_integer);
memcpy(array, &lvalue, sizeof(lvalue));
+ data = array;
+ break;
- /*
- * Perhaps discard the first octet.
- */
- data = &array[offset];
- len -= offset;
+ case PW_TYPE_INTEGER64:
+ len = 8; /* just in case */
+ lvalue64 = htonll(vp->vp_integer64);
+ data = (uint8_t *) &lvalue64;
break;
case PW_TYPE_IPADDR:
}
case PW_TYPE_TLV:
+ do_tlv:
data = vp->vp_tlv;
if (!data) {
fr_strerror_printf("ERROR: Cannot encode NULL TLV");
}
/*
- * Bound the data to 255 bytes.
+ * No data: skip it.
*/
- if (len + offset > room) {
- len = room - offset;
+ if (len == 0) {
+ *pvp = vp->next;
+ return 0;
}
/*
+ * Bound the data to the calling size
+ */
+ if (len > (ssize_t) room) len = room;
+
+ /*
* Encrypt the various password styles
*
* Attributes with encrypted values MUST be less than
*/
switch (vp->flags.encrypt) {
case FLAG_ENCRYPT_USER_PASSWORD:
- make_passwd(ptr + offset, &len,
- data, len,
+ make_passwd(ptr, &len, data, len,
secret, packet->vector);
break;
case FLAG_ENCRYPT_TUNNEL_PASSWORD:
+ lvalue = 0;
+ if (vp->flags.has_tag) lvalue = 1;
+
/*
- * Check if 255 - offset - total_length is less
- * than 18. If so, we can't fit the data into
- * the available space, and we discard the
- * attribute.
+ * 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 - offset) < 18) 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;
}
- make_tunnel_passwd(ptr + offset, &len,
- data, len, room - offset,
+
+ 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 + offset, &len,
- data, len, room - offset,
+ ptr[0] = vp->flags.tag;
+ make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
secret, packet->vector);
break;
}
* always fits.
*/
case FLAG_ENCRYPT_ASCEND_SECRET:
- make_secret(ptr + offset, packet->vector,
- secret, data);
+ make_secret(ptr, packet->vector, secret, data);
len = AUTH_VECTOR_LEN;
break;
default:
- /*
- * Just copy the data over
- */
- memcpy(ptr + offset, data, len);
+ if (vp->flags.has_tag && TAG_VALID(vp->flags.tag)) {
+ if (vp->type == PW_TYPE_STRING) {
+ if (len > ((ssize_t) (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 len;
+ *pvp = vp->next;
+ return len + (ptr - start);
}
-
-static VALUE_PAIR *rad_vp2tlv(VALUE_PAIR *vps)
+static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
+ uint8_t *ptr, int hdr_len, ssize_t len,
+ int flag_offset, int vsa_offset)
{
- int maxattr = 0;
- int length, attribute;
- uint8_t *ptr;
- VALUE_PAIR *vp, *tlv;
-
- attribute = vps->attribute & 0xffff00ff;
- maxattr = vps->attribute & 0x0ff;
-
- tlv = paircreate(attribute, PW_TYPE_TLV);
- if (!tlv) return NULL;
+ int check_len = len - ptr[1];
+ int total = len + hdr_len;
+
+ /*
+ * Pass 1: Check if the addition of the headers
+ * overflows the available room. If so, return
+ * what we were capable of encoding.
+ */
+
+ while (check_len > (255 - hdr_len)) {
+ total += hdr_len;
+ check_len -= (255 - hdr_len);
+ }
- 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->attribute & 0xffff00ff) != attribute) ||
- ((vp->attribute & 0x0000ff00) <= maxattr)) {
+ /*
+ * Note that this results in a number of attributes maybe
+ * being marked as "encoded", but which aren't in the
+ * packet. Oh well. The solution is to fix the
+ * "vp2data_any" function to take into account the header
+ * lengths.
+ */
+ if ((ptr + ptr[1] + total) > end) {
+ return (ptr + ptr[1]) - start;
+ }
+
+ /*
+ * Pass 2: Now that we know there's enough room,
+ * re-arrange the data to form a set of valid
+ * RADIUS attributes.
+ */
+ while (1) {
+ int sublen = 255 - ptr[1];
+
+ if (len <= sublen) {
break;
}
+
+ len -= sublen;
+ memmove(ptr + 255 + hdr_len, ptr + 255, sublen);
+ memcpy(ptr + 255, ptr, hdr_len);
+ ptr[1] += sublen;
+ if (vsa_offset) ptr[vsa_offset] += sublen;
+ ptr[flag_offset] |= 0x80;
+
+ ptr += 255;
+ ptr[1] = hdr_len;
+ if (vsa_offset) ptr[vsa_offset] = 3;
+ }
+
+ ptr[1] += len;
+ if (vsa_offset) ptr[vsa_offset] += len;
- maxattr = vp->attribute & 0xff00;
- tlv->length += vp->length + 2;
+ return (ptr + ptr[1]) - start;
+}
+
+
+/**
+ * @brief Encode an "extended" attribute.
+ */
+int rad_vp2extended(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp,
+ uint8_t *ptr, size_t room)
+{
+ int len;
+ int hdr_len;
+ int nest = 1;
+ uint8_t *start = ptr;
+ const VALUE_PAIR *vp = *pvp;
+
+ if (vp->vendor < VENDORPEC_EXTENDED) {
+ fr_strerror_printf("rad_vp2extended called for non-extended attribute");
+ return -1;
}
- if (!tlv->length) {
- pairfree(&tlv);
- return NULL;
+ if (room < 3) return 0;
+
+ ptr[0] = vp->attribute & 0xff;
+ ptr[1] = 3;
+
+ if (vp->flags.extended) {
+ ptr[2] = (vp->attribute & 0xff00) >> 8;
+
+ } else if (vp->flags.extended_flags) {
+ if (room < 4) return 0;
+
+ ptr[1] = 4;
+ ptr[2] = (vp->attribute & 0xff00) >> 8;
+ ptr[3] = 0;
}
- tlv->vp_tlv = malloc(tlv->length);
- if (!tlv->vp_tlv) {
- pairfree(&tlv);
- return NULL;
+ /*
+ * Only "flagged" attributes can be longer than one
+ * attribute.
+ */
+ if (!vp->flags.extended_flags && (room > 255)) {
+ room = 255;
}
- 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;
- }
+ /*
+ * Handle EVS VSAs.
+ */
+ if (vp->flags.evs) {
+ uint8_t *evs = ptr + ptr[1];
- maxattr = vp->attribute & 0xff00;
- length = vp2data(NULL, NULL, NULL, vp, ptr + 2, 0,
- tlv->vp_tlv + tlv->length - ptr);
- if (length < 0) {
- vp->length = ptr - vp->vp_tlv;
- return tlv; /* should be a more serious error... */
- }
+ if (room < (size_t) (ptr[1] + 5)) return 0;
/*
- * Pack the attribute.
+ * RADIUS Attribute Type is packed into the high byte
+ * of the Vendor Id. So over-write it in the packet.
+ *
+ * And hard-code Extended-Type to Vendor-Specific.
*/
- ptr[0] = (vp->attribute & 0xff00) >> 8;
- ptr[1] = (length & 0xff) + 2;
+ ptr[0] = (vp->vendor >> 24) & 0xff;
+ ptr[2] = 26;
+
+ evs[0] = 0; /* always zero */
+ evs[1] = (vp->vendor >> 16) & 0xff;
+ evs[2] = (vp->vendor >> 8) & 0xff;
+ evs[3] = vp->vendor & 0xff;
+ evs[4] = vp->attribute & 0xff;
- ptr += vp->length + 2;
- vp->flags.encoded = 1;
+ ptr[1] += 5;
+ nest = 0;
}
+ hdr_len = ptr[1];
- return tlv;
-}
+ len = vp2data_any(packet, original, secret, nest,
+ pvp, ptr + ptr[1], room - hdr_len);
+ if (len <= 0) return len;
-/*
- * 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)
-{
- size_t left, piece;
- size_t hsize = (ptr - start);
- uint8_t *this = start;
- const uint8_t *data;
- uint8_t header[16];
-
/*
- * If it's too long and marked as encrypted, ignore it.
+ * There may be more than 252 octets of data encoded in
+ * the attribute. If so, move the data up in the packet,
+ * and copy the existing header over. Set the "M" flag ONLY
+ * after copying the rest of the data.
*/
- if (vp->flags.encrypt != FLAG_ENCRYPT_NONE) {
- return 0;
+ if (vp->flags.extended_flags && (len > (255 - ptr[1]))) {
+ return attr_shift(start, start + room, ptr, 4, len, 3, 0);
}
-
- memcpy(header, start, hsize);
- left = vp->length;
+ ptr[1] += len;
- switch (vp->type) {
- case PW_TYPE_TLV:
- data = vp->vp_tlv;
- break;
-
- case PW_TYPE_OCTETS:
- case PW_TYPE_STRING:
- data = vp->vp_octets;
- break;
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ int jump = 3;
- /*
- * This is invalid.
- */
- default:
- 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;
+ fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
+ if (!vp->flags.extended_flags) {
+ fprintf(fr_log_fp, "%02x ", ptr[2]);
+
} else {
- piece = left;
- *(ptr++) = 0x00;
+ fprintf(fr_log_fp, "%02x %02x ", ptr[2], ptr[3]);
+ jump = 4;
}
-
- memcpy(ptr, data, piece);
- this[1] = hsize + piece + 1;
- /*
- *
- */
- this[hsize - 1] = hsize - 6 + 1 + piece;
- data += piece;
- ptr += piece;
- left -= piece;
- this = ptr;
+ if (vp->flags.evs) {
+ fprintf(fr_log_fp, "%02x%02x%02x%02x (%u) %02x ",
+ ptr[jump], ptr[jump + 1],
+ ptr[jump + 2], ptr[jump + 3],
+ ((ptr[jump + 1] << 16) |
+ (ptr[jump + 2] << 8) |
+ ptr[jump + 3]),
+ ptr[jump + 4]);
+ jump += 5;
+ }
+
+ print_hex_data(ptr + jump, len, 3);
}
-
- return (ptr - start);
+#endif
+
+ return (ptr + ptr[1]) - start;
}
-/*
- * Parse a data structure into a RADIUS attribute.
+/**
+ * @brief Encode a WiMAX attribute.
*/
-int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR *vp, uint8_t *start)
+int rad_vp2wimax(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp,
+ uint8_t *ptr, size_t room)
{
- int vendorcode;
- int offset, len, total_length;
- uint32_t lvalue;
- uint8_t *ptr, *length_ptr, *vsa_length_ptr, *tlv_length_ptr;
+ int len;
+ uint32_t lvalue;
+ int hdr_len;
+ uint8_t *start = ptr;
+ const VALUE_PAIR *vp = *pvp;
- ptr = start;
- vendorcode = total_length = 0;
- length_ptr = vsa_length_ptr = tlv_length_ptr = NULL;
+ /*
+ * Double-check for WiMAX format.
+ */
+ if (!vp->flags.wimax) {
+ fr_strerror_printf("rad_vp2wimax called for non-WIMAX VSA");
+ return -1;
+ }
/*
- * For interoperability, always put vendor attributes
- * into their own VSA.
+ * Not enough room for:
+ * attr, len, vendor-id, vsa, vsalen, continuation
*/
- if ((vendorcode = VENDOR(vp->attribute)) == 0) {
- *(ptr++) = vp->attribute & 0xFF;
- length_ptr = ptr;
- *(ptr++) = 2;
- total_length += 2;
+ if (room < 9) 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[0] = PW_VENDOR_SPECIFIC;
+ ptr[1] = 9;
+ lvalue = htonl(vp->vendor);
+ memcpy(ptr + 2, &lvalue, 4);
+ ptr[6] = (vp->attribute & fr_attr_mask[1]);
+ ptr[7] = 3;
+ ptr[8] = 0; /* continuation byte */
- /*
- * 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;
- }
+ hdr_len = 9;
- /*
- * 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;
-
- switch (vsa_tlen) {
- case 1:
- ptr[0] = (vp->attribute & 0xFF);
- break;
+ len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1],
+ room - hdr_len);
+ if (len <= 0) return len;
- case 2:
- ptr[0] = ((vp->attribute >> 8) & 0xFF);
- ptr[1] = (vp->attribute & 0xFF);
- break;
+ /*
+ * There may be more than 252 octets of data encoded in
+ * the attribute. If so, move the data up in the packet,
+ * and copy the existing header over. Set the "C" flag
+ * ONLY after copying the rest of the data.
+ */
+ if (len > (255 - ptr[1])) {
+ return attr_shift(start, start + room, ptr, hdr_len, len, 8, 7);
+ }
- case 4:
- ptr[0] = 0;
- ptr[1] = 0;
- ptr[2] = ((vp->attribute >> 8) & 0xFF);
- ptr[3] = (vp->attribute & 0xFF);
- break;
+ ptr[1] += len;
+ ptr[7] += len;
- default:
- return 0; /* silently discard it */
- }
- ptr += vsa_tlen;
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x %02x%02x%02x%02x (%u) %02x %02x %02x ",
+ ptr[0], ptr[1],
+ ptr[2], ptr[3], ptr[4], ptr[5],
+ (ptr[3] << 16) | (ptr[4] << 8) | ptr[5],
+ ptr[6], ptr[7], ptr[8]);
+ print_hex_data(ptr + 9, len, 3);
+ }
+#endif
- 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;
+ return (ptr + ptr[1]) - start;
+}
- default:
- return 0; /* silently discard it */
- }
- ptr += vsa_llen;
+/**
+ * @brief Encode an RFC format TLV.
+ *
+ * This could be a standard attribute,
+ * or a TLV data type. If it's a standard attribute, then
+ * vp->attribute == attribute. Otherwise, attribute may be
+ * something else.
+ */
+static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp,
+ unsigned int attribute, uint8_t *ptr, size_t room)
+{
+ ssize_t len;
- /*
- * 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 > (254 - (ptr - start))) {
- return rad_vp2continuation(vp, start, ptr);
- }
+ if (room < 2) return 0;
- ptr[0] = 0x00;
- ptr++;
+ ptr[0] = attribute & 0xff;
+ ptr[1] = 2;
- /*
- * 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 (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
+
+ len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1], room);
+ if (len <= 0) return len;
+
+ ptr[1] += len;
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
+ print_hex_data(ptr + 2, len, 3);
+ }
+#endif
+
+ return ptr[1];
+}
+
+
+/**
+ * @brief Encode a VSA which is a TLV. If it's in the RFC format, call
+ * vp2attr_rfc. Otherwise, encode it here.
+ */
+static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp,
+ unsigned int attribute, unsigned int vendor,
+ uint8_t *ptr, size_t room)
+{
+ ssize_t len;
+ DICT_VENDOR *dv;
+ const VALUE_PAIR *vp = *pvp;
+
+ /*
+ * Unknown vendor: RFC format.
+ * Known vendor and RFC format: go do that.
+ */
+ dv = dict_vendorbyvalue(vendor);
+ if (!dv ||
+ (!vp->flags.is_tlv && (dv->type == 1) && (dv->length == 1))) {
+ return vp2attr_rfc(packet, original, secret, pvp,
+ attribute, ptr, room);
+ }
+
+ switch (dv->type) {
+ default:
+ fr_strerror_printf("vp2attr_vsa: Internal sanity check failed,"
+ " type %u", (unsigned) dv->type);
+ return -1;
+
+ case 4:
+ ptr[0] = 0; /* attr must be 24-bit */
+ ptr[1] = (attribute >> 16) & 0xff;
+ ptr[2] = (attribute >> 8) & 0xff;
+ ptr[3] = attribute & 0xff;
+ break;
+
+ case 2:
+ ptr[0] = (attribute >> 8) & 0xff;
+ ptr[1] = attribute & 0xff;
+ break;
+
+ case 1:
+ ptr[0] = attribute & 0xff;
+ break;
+ }
+
+ switch (dv->length) {
+ default:
+ fr_strerror_printf("vp2attr_vsa: Internal sanity check failed,"
+ " length %u", (unsigned) dv->length);
+ return -1;
+
+ case 0:
+ break;
+
+ case 2:
+ ptr[dv->type] = 0;
+ ptr[dv->type + 1] = dv->type + 2;
+ break;
+
+ case 1:
+ ptr[dv->type] = dv->type + 1;
+ break;
+
+ }
+
+ if (room > ((unsigned) 255 - (dv->type + dv->length))) {
+ room = 255 - (dv->type + dv->length);
+ }
+
+ len = vp2data_any(packet, original, secret, 0, pvp,
+ ptr + dv->type + dv->length, room);
+ if (len <= 0) return len;
+
+ if (dv->length) ptr[dv->type + dv->length - 1] += len;
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ switch (dv->type) {
+ default:
+ break;
+
+ case 4:
+ if ((fr_debug_flag > 3) && fr_log_fp)
+ fprintf(fr_log_fp, "\t\t%02x%02x%02x%02x ",
+ ptr[0], ptr[1], ptr[2], ptr[3]);
+ break;
+
+ case 2:
+ if ((fr_debug_flag > 3) && fr_log_fp)
+ fprintf(fr_log_fp, "\t\t%02x%02x ",
+ ptr[0], ptr[1]);
+ break;
+
+ case 1:
+ if ((fr_debug_flag > 3) && fr_log_fp)
+ fprintf(fr_log_fp, "\t\t%02x ", ptr[0]);
+ break;
+ }
+
+ switch (dv->length) {
+ default:
+ break;
+
+ case 0:
+ fprintf(fr_log_fp, " ");
+ break;
+
+ case 1:
+ fprintf(fr_log_fp, "%02x ",
+ ptr[dv->type]);
+ break;
+
+ case 2:
+ fprintf(fr_log_fp, "%02x%02x ",
+ ptr[dv->type], ptr[dv->type] + 1);
+ break;
}
- 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;
+ print_hex_data(ptr + dv->type + dv->length, len, 3);
}
+#endif
- offset = 0;
- if (vp->flags.has_tag) {
- if (TAG_VALID(vp->flags.tag)) {
- ptr[0] = vp->flags.tag & 0xff;
- offset = 1;
+ return dv->type + dv->length + len;
+}
- } else if (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) {
- /*
- * Tunnel passwords REQUIRE a tag, even
- * if don't have a valid tag.
- */
- ptr[0] = 0;
- offset = 1;
- } /* else don't write a tag */
- } /* else the attribute doesn't have a tag */
- len = vp2data(packet, original, secret, vp, ptr, offset,
- 255 - total_length);
- if (len < 0) return -1;
+/**
+ * @brief Encode a Vendor-Specific attribute.
+ */
+int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp, uint8_t *ptr,
+ size_t room)
+{
+ ssize_t len;
+ uint32_t lvalue;
+ const VALUE_PAIR *vp = *pvp;
/*
- * Account for the tag (if any).
+ * Double-check for WiMAX format.
*/
- len += offset;
+ if (vp->flags.wimax) {
+ return rad_vp2wimax(packet, original, secret, pvp,
+ ptr, room);
+ }
+
+ if (vp->vendor > FR_MAX_VENDOR) {
+ fr_strerror_printf("rad_vp2vsa: Invalid arguments");
+ 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?
+ * Not enough room for:
+ * attr, len, vendor-id
*/
- if ((len == 0) &&
- (vp->attribute != PW_CHARGEABLE_USER_IDENTITY)) return 0;
+ if (room < 6) return 0;
/*
- * Update the various lengths.
+ * Build the Vendor-Specific header
*/
- *length_ptr += len;
- if (vsa_length_ptr) *vsa_length_ptr += len;
- if (tlv_length_ptr) *tlv_length_ptr += len;
- ptr += len;
- total_length += len;
+ ptr[0] = PW_VENDOR_SPECIFIC;
+ ptr[1] = 6;
+ lvalue = htonl(vp->vendor);
+ memcpy(ptr + 2, &lvalue, 4);
+
+ if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
+
+ len = vp2attr_vsa(packet, original, secret, pvp,
+ vp->attribute, vp->vendor,
+ ptr + ptr[1], room);
+ if (len < 0) return len;
- return total_length; /* of attribute */
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x %02x%02x%02x%02x (%u) ",
+ ptr[0], ptr[1],
+ ptr[2], ptr[3], ptr[4], ptr[5],
+ (ptr[3] << 16) | (ptr[4] << 8) | ptr[5]);
+ print_hex_data(ptr + 6, len, 3);
+ }
+#endif
+
+ ptr[1] += len;
+
+ return ptr[1];
}
-/*
- * Encode a packet.
+/**
+ * @brief Encode an RFC standard attribute 1..255
+ */
+int rad_vp2rfc(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp,
+ uint8_t *ptr, size_t room)
+{
+ const VALUE_PAIR *vp = *pvp;
+
+ if (vp->vendor != 0) {
+ fr_strerror_printf("rad_vp2rfc called with VSA");
+ return -1;
+ }
+
+ if ((vp->attribute == 0) || (vp->attribute > 255)) {
+ fr_strerror_printf("rad_vp2rfc called with non-standard attribute %u", vp->attribute);
+ return -1;
+ }
+
+ /*
+ * Only CUI is allowed to have zero length.
+ * Thank you, WiMAX!
+ */
+ if ((vp->length == 0) &&
+ (vp->attribute == PW_CHARGEABLE_USER_IDENTITY)) {
+ ptr[0] = PW_CHARGEABLE_USER_IDENTITY;
+ ptr[1] = 2;
+
+ *pvp = vp->next;
+ return 2;
+ }
+
+ return vp2attr_rfc(packet, original, secret, pvp, vp->attribute,
+ ptr, room);
+}
+
+
+/**
+ * @brief 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 **pvp, uint8_t *start,
+ size_t room)
+{
+ const VALUE_PAIR *vp;
+
+ if (!pvp || !*pvp || !start || (room <= 2)) return -1;
+
+ vp = *pvp;
+
+ /*
+ * RFC format attributes take the fast path.
+ */
+ if (vp->vendor == 0) {
+ if (vp->attribute > 255) return 0;
+
+ /*
+ * Message-Authenticator is hard-coded.
+ */
+ if (vp->attribute == PW_MESSAGE_AUTHENTICATOR) {
+ if (room < 18) return -1;
+
+ debug_pair(vp);
+ start[0] = PW_MESSAGE_AUTHENTICATOR;
+ start[1] = 18;
+ memset(start + 2, 0, 16);
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t50 12 ...\n");
+ }
+#endif
+
+ *pvp = (*pvp)->next;
+ return 18;
+ }
+
+ return rad_vp2rfc(packet, original, secret, pvp,
+ start, room);
+ }
+
+ if (vp->vendor > FR_MAX_VENDOR) {
+ return rad_vp2extended(packet, original, secret, pvp,
+ start, room);
+ }
+
+ if (vp->flags.wimax) {
+ return rad_vp2wimax(packet, original, secret, pvp,
+ start, room);
+ }
+
+ return rad_vp2vsa(packet, original, secret, pvp,
+ start, room);
+}
+
+
+/**
+ * @brief Encode a packet.
*/
int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
const char *secret)
uint8_t *ptr;
uint16_t total_length;
int len;
- VALUE_PAIR *reply;
+ const VALUE_PAIR *reply;
const char *what;
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];
/*
* Loop over the reply attributes for the packet.
*/
- for (reply = packet->vps; reply; reply = reply->next) {
+ reply = packet->vps;
+ while (reply) {
+ size_t last_len;
+ const char *last_name = NULL;
+
/*
- * Ignore non-wire attributes
+ * Ignore non-wire attributes, but allow extended
+ * attributes.
*/
- if ((VENDOR(reply->attribute) == 0) &&
- ((reply->attribute & 0xFFFF) > 0xff)) {
+ if ((reply->vendor == 0) &&
+ ((reply->attribute & 0xFFFF) >= 256) &&
+ !reply->flags.extended && !reply->flags.extended_flags) {
#ifndef NDEBUG
/*
* Permit the admin to send BADLY formatted
if (reply->attribute == PW_RAW_ATTRIBUTE) {
memcpy(ptr, reply->vp_octets, reply->length);
len = reply->length;
+ reply = reply->next;
goto next;
}
#endif
+ reply = reply->next;
continue;
}
* length and initial value.
*/
if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
- reply->length = AUTH_VECTOR_LEN;
- memset(reply->vp_strvalue, 0, AUTH_VECTOR_LEN);
-
/*
* Cache the offset to the
* Message-Authenticator
*/
packet->offset = total_length;
+ last_len = 16;
+ } else {
+ last_len = reply->length;
}
+ last_name = reply->name;
- /*
- * Print out ONLY the attributes which
- * we're sending over the wire, and print
- * them out BEFORE they're encrypted.
- */
- debug_pair(reply);
-
- /*
- * Print them in order, even if they were encoded
- * already.
- */
- 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;
- }
-
- /*
- * The encoded flag MUST be set in reply!
- */
- reply = reply->next;
- }
-
- 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) {
- break;
+ if (len == 0) {
+ if (last_len != 0) {
+ DEBUG("WARNING: Failed encoding attribute %s\n", last_name);
+ } else {
+ DEBUG("WARNING: Skipping zero-length attribute %s\n", last_name);
+ }
}
- next:
+#ifndef NDEBUG
+ next: /* Used only for Raw-Attribute */
+#endif
ptr += len;
total_length += len;
} /* done looping over all attributes */
}
-/*
- * Sign a previously encoded packet.
+/**
+ * @brief Sign a previously encoded packet.
*/
int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
const char *secret)
uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
switch (packet->code) {
- case PW_ACCOUNTING_REQUEST:
case PW_ACCOUNTING_RESPONSE:
+ if (original && original->code == PW_STATUS_SERVER) {
+ goto do_ack;
+ }
+
+ case PW_ACCOUNTING_REQUEST:
case PW_DISCONNECT_REQUEST:
case PW_DISCONNECT_ACK:
case PW_DISCONNECT_NAK:
memset(hdr->vector, 0, AUTH_VECTOR_LEN);
break;
+ do_ack:
case PW_AUTHENTICATION_ACK:
case PW_AUTHENTICATION_REJECT:
case PW_ACCESS_CHALLENGE:
return 0;
}
-/*
- * Reply to the request. Also attach
+/**
+ * @brief Reply to the request. Also attach
* reply attribute value pairs and any user message provided.
*/
int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
packet->dst_port);
for (reply = packet->vps; reply; reply = reply->next) {
- if ((VENDOR(reply->attribute) == 0) &&
+ if ((reply->vendor == 0) &&
((reply->attribute & 0xFFFF) > 0xff)) continue;
debug_pair(reply);
}
}
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+#endif
+
/*
* And send it on it's way.
*/
&packet->dst_ipaddr, packet->dst_port);
}
+/**
+ * @brief Do a comparison of two authentication digests by comparing
+ * the FULL digest.
+ *
+ * Otherwise, the server can be subject to
+ * timing attacks that allow attackers find a valid message
+ * authenticator.
+ *
+ * http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf
+ */
+int rad_digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
+{
+ int result = 0;
+ size_t i;
+
+ for (i = 0; i < length; i++) {
+ result |= a[i] ^ b[i];
+ }
-/*
- * Validates the requesting client NAS. Calculates the
+ return result; /* 0 is OK, !0 is !OK, just like memcmp */
+}
+
+
+/**
+ * @brief Validates the requesting client NAS. Calculates the
* signature based on the clients private key.
*/
static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
/*
* Return 0 if OK, 2 if not OK.
*/
- if (memcmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
+ if (rad_digest_cmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
return 0;
}
-/*
- * Validates the requesting client NAS. Calculates the
+/**
+ * @brief Validates the requesting client NAS. Calculates the
* signature based on the clients private key.
*/
static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
/*
* Return 0 if OK, 2 if not OK.
*/
- if (memcmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
+ if (rad_digest_cmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
return 0;
}
-/*
- * See if the data pointed to by PTR is a valid RADIUS packet.
+/**
+ * @brief Check if a set of RADIUS formatted TLVs are OK.
+ */
+int rad_tlv_ok(const uint8_t *data, size_t length,
+ size_t dv_type, size_t dv_length)
+{
+ const uint8_t *end = data + length;
+
+ if ((dv_length > 2) || (dv_type == 0) || (dv_type > 4)) {
+ fr_strerror_printf("rad_tlv_ok: Invalid arguments");
+ return -1;
+ }
+
+ while (data < end) {
+ size_t attrlen;
+
+ if ((data + dv_type + dv_length) > end) {
+ fr_strerror_printf("Attribute header overflow");
+ return -1;
+ }
+
+ switch (dv_type) {
+ case 4:
+ if ((data[0] == 0) && (data[1] == 0) &&
+ (data[2] == 0) && (data[3] == 0)) {
+ zero:
+ fr_strerror_printf("Invalid attribute 0");
+ return -1;
+ }
+
+ if (data[0] != 0) {
+ fr_strerror_printf("Invalid attribute > 2^24");
+ return -1;
+ }
+ break;
+
+ case 2:
+ if ((data[1] == 0) && (data[1] == 0)) goto zero;
+ break;
+
+ case 1:
+ if (data[0] == 0) goto zero;
+ break;
+
+ default:
+ fr_strerror_printf("Internal sanity check failed");
+ return -1;
+ }
+
+ switch (dv_length) {
+ case 0:
+ return 0;
+
+ case 2:
+ if (data[dv_type + 1] != 0) {
+ fr_strerror_printf("Attribute is longer than 256 octets");
+ return -1;
+ }
+ /* FALL-THROUGH */
+ case 1:
+ attrlen = data[dv_type + dv_length - 1];
+ break;
+
+
+ default:
+ fr_strerror_printf("Internal sanity check failed");
+ return -1;
+ }
+
+ if (attrlen < (dv_type + dv_length)) {
+ fr_strerror_printf("Attribute header has invalid length");
+ return -1;
+ }
+
+ if (attrlen > length) {
+ fr_strerror_printf("Attribute overflows container");
+ return -1;
+ }
+
+ data += attrlen;
+ length -= attrlen;
+ }
+
+ return 0;
+}
+
+
+/**
+ * @brief See if the data pointed to by PTR is a valid RADIUS packet.
*
* packet is not 'const * const' because we may update data_len,
* if there's more data in the UDP packet than in the RADIUS packet.
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
- packet->data_len, AUTH_HDR_LEN);
+ (int) packet->data_len, AUTH_HDR_LEN);
return 0;
}
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
- packet->data_len, MAX_PACKET_LEN);
+ (int) packet->data_len, MAX_PACKET_LEN);
return 0;
}
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
- packet->data_len, totallen);
+ (int) packet->data_len, totallen);
return 0;
}
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]) {
}
-/*
- * Receive UDP client requests, and fill in
+/**
+ * @brief Receive UDP client requests, and fill in
* the basics of a RADIUS_PACKET structure.
*/
RADIUS_PACKET *rad_recv(int fd, int flags)
{
- int sockflags = 0;
+ int sock_flags = 0;
RADIUS_PACKET *packet;
/*
packet->src_port,
packet->code);
}
- DEBUG(", id=%d, length=%d\n", packet->id, packet->data_len);
+ DEBUG(", id=%d, length=%d\n",
+ packet->id, (int) packet->data_len);
}
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+#endif
+
return packet;
}
-/*
- * Verify the signature of a packet.
+/**
+ * @brief Verify the signature of a packet.
*/
int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
const char *secret)
default:
break;
- case PW_ACCOUNTING_REQUEST:
case PW_ACCOUNTING_RESPONSE:
+ if (original &&
+ (original->code == PW_STATUS_SERVER)) {
+ goto do_ack;
+ }
+
+ case PW_ACCOUNTING_REQUEST:
case PW_DISCONNECT_REQUEST:
case PW_DISCONNECT_ACK:
case PW_DISCONNECT_NAK:
memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
break;
+ do_ack:
case PW_AUTHENTICATION_ACK:
case PW_AUTHENTICATION_REJECT:
case PW_ACCESS_CHALLENGE:
fr_hmac_md5(packet->data, packet->data_len,
(const uint8_t *) secret, strlen(secret),
calc_auth_vector);
- if (memcmp(calc_auth_vector, msg_auth_vector,
+ if (rad_digest_cmp(calc_auth_vector, msg_auth_vector,
sizeof(calc_auth_vector)) != 0) {
char buffer[32];
fr_strerror_printf("Received packet from %s with invalid Message-Authenticator! (Shared secret is incorrect.)",
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;
}
-static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, int attribute, int length,
- const uint8_t *data, VALUE_PAIR *vp)
+/**
+ * @brief Create a "raw" attribute from the attribute contents.
+ */
+static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
+ UNUSED const RADIUS_PACKET *original,
+ UNUSED const char *secret,
+ unsigned int attribute, unsigned int vendor,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
{
- int offset = 0;
+ VALUE_PAIR *vp;
/*
- * If length is greater than 253, something is SERIOUSLY
- * wrong.
+ * Keep the next function happy.
*/
- if (length > 253) length = 253; /* paranoia (pair-anoia?) */
+ vp = pairalloc(NULL);
+ vp = paircreate_raw(attribute, vendor, PW_TYPE_OCTETS, vp);
+ if (!vp) {
+ fr_strerror_printf("data2vp_raw: Failed creating attribute");
+ return -1;
+ }
vp->length = length;
- vp->operator = T_OP_EQ;
- vp->next = NULL;
+
+ /*
+ * If it's short, put it into the array. If it's too
+ * long, flag it as such, and put it somewhere else;
+ */
+ if (length <= sizeof(vp->vp_octets)) {
+ memcpy(vp->vp_octets, data, length);
+ } else {
+ vp->type |= PW_FLAG_LONG;
+ vp->vp_tlv = malloc(length);
+ if (!vp->vp_tlv) {
+ pairfree(&vp);
+ return -1;
+ }
+ memcpy(vp->vp_tlv, data, length);
+ }
+
+ *pvp = vp;
+
+ return length;
+}
+
+
+static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ unsigned int attribute, unsigned int vendor,
+ int nest,
+ const uint8_t *start, size_t length,
+ VALUE_PAIR **pvp);
+
+/**
+ * @brief Create any kind of VP from the attribute contents.
+ * @return -1 on error, or "length".
+ */
+static ssize_t data2vp_any(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int nest,
+ unsigned int attribute, unsigned int vendor,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
+{
+ int data_offset = 0;
+ DICT_ATTR *da;
+ VALUE_PAIR *vp = NULL;
+ uint8_t buffer[256];
+
+ if (length == 0) {
+ /*
+ * Hacks for CUI. The WiMAX spec says that it
+ * can be zero length, even though this is
+ * forbidden by the RADIUS specs. So... we make
+ * a special case for it.
+ */
+ if ((vendor == 0) &&
+ (attribute == PW_CHARGEABLE_USER_IDENTITY)) {
+ data = (const uint8_t *) "";
+ length = 1;
+ } else {
+ *pvp = NULL;
+ return 0;
+ }
+ }
+
+ da = dict_attrbyvalue(attribute, vendor);
+
+ /*
+ * Unknown attribute. Create it as a "raw" attribute.
+ */
+ if (!da) {
+ VP_TRACE("Not found %u.%u\n", vendor, attribute);
+ raw:
+ if (vp) pairfree(&vp);
+ return data2vp_raw(packet, original, secret,
+ attribute, vendor, data, length, pvp);
+ }
+
+ /*
+ * TLVs are handled first. They can't be tagged, and
+ * they can't be encrypted.
+ */
+ if (da->type == PW_TYPE_TLV) {
+ VP_TRACE("Found TLV %u.%u\n", vendor, attribute);
+ return data2vp_tlvs(packet, original, secret,
+ attribute, vendor, nest,
+ data, length, pvp);
+ }
+
+ /*
+ * The data is very long.
+ */
+ if (length > sizeof(vp->vp_octets)) {
+ /*
+ * Long encrypted attributes are forbidden.
+ */
+ if (da->flags.encrypt != FLAG_ENCRYPT_NONE) goto raw;
+
+#ifndef NDEBUG
+ /*
+ * Catch programming errors.
+ */
+ if ((da->type != PW_TYPE_STRING) &&
+ (da->type != PW_TYPE_OCTETS)) goto raw;
+
+#endif
+
+ /*
+ * FIXME: Figure out how to deal with long
+ * strings and binary data!
+ */
+ goto raw;
+ }
+
+ /*
+ * The attribute is known, and well formed. We can now
+ * create it. The main failure from here on in is being
+ * out of memory.
+ */
+ vp = pairalloc(da);
+ if (!vp) return -1;
/*
* Handle tags.
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;
+ data_offset = 1;
+ }
}
}
/*
* Copy the data to be decrypted
*/
- memcpy(&vp->vp_octets[0], data + offset, length - offset);
- vp->length -= offset;
+ vp->length = length - data_offset;
+ memcpy(buffer, data + data_offset, vp->length);
/*
* Decrypt the attribute.
*/
- switch (vp->flags.encrypt) {
+ if (secret && packet) switch (vp->flags.encrypt) {
/*
* User-Password
*/
case FLAG_ENCRYPT_USER_PASSWORD:
if (original) {
- rad_pwdecode((char *)vp->vp_strvalue,
+ rad_pwdecode((char *) buffer,
vp->length, secret,
original->vector);
} else {
- rad_pwdecode((char *)vp->vp_strvalue,
+ rad_pwdecode((char *) buffer,
vp->length, secret,
packet->vector);
}
+ buffer[253] = '\0';
if (vp->attribute == PW_USER_PASSWORD) {
- vp->length = strlen(vp->vp_strvalue);
+ vp->length = strlen((char *) buffer);
}
break;
case FLAG_ENCRYPT_TUNNEL_PASSWORD:
if (!original) goto raw;
- if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
+ if (rad_tunnel_pwdecode(buffer, &vp->length,
secret, original->vector) < 0) {
goto raw;
}
make_secret(my_digest,
original->vector,
secret, data);
- memcpy(vp->vp_strvalue, my_digest,
+ memcpy(buffer, my_digest,
AUTH_VECTOR_LEN );
- vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
- vp->length = strlen(vp->vp_strvalue);
+ buffer[AUTH_VECTOR_LEN] = '\0';
+ vp->length = strlen((char *) buffer);
}
break;
switch (vp->type) {
case PW_TYPE_STRING:
+ memcpy(vp->vp_strvalue, buffer, vp->length);
+ vp->vp_strvalue[vp->length] = '\0';
+ break;
+
case PW_TYPE_OCTETS:
case PW_TYPE_ABINARY:
- /* nothing more to do */
+ memcpy(vp->vp_octets, buffer, vp->length);
break;
case PW_TYPE_BYTE:
if (vp->length != 1) goto raw;
- vp->vp_integer = vp->vp_octets[0];
+ vp->vp_integer = buffer[0];
break;
case PW_TYPE_SHORT:
if (vp->length != 2) goto raw;
- vp->vp_integer = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
+ vp->vp_integer = (buffer[0] << 8) | buffer[1];
break;
case PW_TYPE_INTEGER:
if (vp->length != 4) goto raw;
- memcpy(&vp->vp_integer, vp->vp_octets, 4);
+ memcpy(&vp->vp_integer, buffer, 4);
vp->vp_integer = ntohl(vp->vp_integer);
if (vp->flags.has_tag) vp->vp_integer &= 0x00ffffff;
+ break;
- /*
- * Try to get named VALUEs
- */
- {
- DICT_VALUE *dval;
- dval = dict_valbyattr(vp->attribute,
- vp->vp_integer);
- if (dval) {
- strlcpy(vp->vp_strvalue,
- dval->name,
- sizeof(vp->vp_strvalue));
- }
- }
+ case PW_TYPE_INTEGER64:
+ if (vp->length != 8) goto raw;
+
+ /* vp_integer64 is a union with vp_octets */
+ memcpy(&vp->vp_integer64, buffer, 8);
+ vp->vp_integer64 = ntohll(vp->vp_integer64);
break;
case PW_TYPE_DATE:
if (vp->length != 4) goto raw;
- memcpy(&vp->vp_date, vp->vp_octets, 4);
+ memcpy(&vp->vp_date, buffer, 4);
vp->vp_date = ntohl(vp->vp_date);
break;
case PW_TYPE_IPADDR:
if (vp->length != 4) goto raw;
- memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+ memcpy(&vp->vp_ipaddr, buffer, 4);
break;
/*
*/
case PW_TYPE_IFID:
if (vp->length != 8) goto raw;
- /* vp->vp_ifid == vp->vp_octets */
+ memcpy(&vp->vp_ifid, buffer, 8);
break;
/*
*/
case PW_TYPE_IPV6ADDR:
if (vp->length != 16) goto raw;
- /* vp->vp_ipv6addr == vp->vp_octets */
+ memcpy(&vp->vp_ipv6addr, buffer, 16);
break;
/*
*/
case PW_TYPE_IPV6PREFIX:
if (vp->length < 2 || vp->length > 18) goto raw;
- if (vp->vp_octets[1] > 128) goto raw;
+ if (buffer[1] > 128) goto raw;
/*
* FIXME: double-check that
* (vp->vp_octets[1] >> 3) matches vp->length + 2
*/
+ memcpy(&vp->vp_ipv6prefix, buffer, vp->length);
if (vp->length < 18) {
- memset(vp->vp_octets + vp->length, 0,
+ memset(((uint8_t *)vp->vp_ipv6prefix) + vp->length, 0,
18 - vp->length);
}
break;
* Overload vp_integer for ntohl, which takes
* uint32_t, not int32_t
*/
- memcpy(&vp->vp_integer, vp->vp_octets, 4);
+ memcpy(&vp->vp_integer, buffer, 4);
vp->vp_integer = ntohl(vp->vp_integer);
- memcpy(&vp->vp_signed, &vp->vp_integer, 4);
break;
case PW_TYPE_TLV:
- vp->length = length;
- vp->vp_tlv = malloc(length);
- if (!vp->vp_tlv) {
- pairfree(&vp);
- fr_strerror_printf("No memory");
- return NULL;
- }
- memcpy(vp->vp_tlv, data, length);
- break;
+ pairfree(&vp);
+ fr_strerror_printf("data2vp_any: Internal sanity check failed");
+ return -1;
case PW_TYPE_COMBO_IP:
if (vp->length == 4) {
vp->type = PW_TYPE_IPADDR;
- memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+ memcpy(&vp->vp_ipaddr, buffer, 4);
break;
} else if (vp->length == 16) {
vp->type = PW_TYPE_IPV6ADDR;
- /* vp->vp_ipv6addr == vp->vp_octets */
+ memcpy(&vp->vp_ipv6addr, buffer, 16);
break;
}
/* FALL-THROUGH */
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.
- */
- memset(&vp->flags, 0, sizeof(vp->flags));
+ goto raw;
}
- return vp;
-}
-
-static void rad_sortvp(VALUE_PAIR **head)
-{
- int swapped;
- VALUE_PAIR *vp, **tail;
+ *pvp = vp;
- /*
- * 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);
+ return length;
}
-/*
- * 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.
+/**
+ * @brief Convert a top-level VSA to a VP.
*/
-static uint8_t *rad_coalesce(int attribute, size_t length, uint8_t *data,
- size_t packet_length, size_t *ptlv_length)
-
+static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, unsigned int vendor,
+ size_t dv_type, size_t dv_length,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
{
- 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]) {
- if ((ptr[0] != PW_VENDOR_SPECIFIC) ||
- (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */
- (ptr[2] != 0) || (ptr[3] != 0)) { /* our requirement */
- continue;
- }
+ unsigned int attribute;
+ ssize_t attrlen, my_len;
- memcpy(&lvalue, ptr + 2, 4); /* Vendor Id */
- lvalue = ntohl(lvalue);
- lvalue <<= 16;
- lvalue |= ptr[2 + 4]; /* add in VSA number */
- if (lvalue != attribute) continue;
+#ifndef NDEBUG
+ if (length <= (dv_type + dv_length)) {
+ fr_strerror_printf("attr2vp_vsa: Failure to call rad_tlv_ok");
+ return -1;
+ }
+#endif
+
+ switch (dv_type) {
+ case 4:
+ /* data[0] must be zero */
+ attribute = data[1] << 16;
+ attribute |= data[2] << 8;
+ attribute |= data[3];
+ break;
- /*
- * If the vendor-length is too small, it's badly
- * formed, so we stop.
- */
- if ((ptr[2 + 4 + 1]) < 3) break;
+ case 2:
+ attribute = data[0] << 8;
+ attribute |= data[1];
+ break;
- tlv_length += ptr[2 + 4 + 1] - 3;
- if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
- }
+ case 1:
+ attribute = data[0];
+ break;
- tlv = tlv_data = malloc(tlv_length);
- if (!tlv_data) return NULL;
+ default:
+ fr_strerror_printf("attr2vp_vsa: Internal sanity check failed");
+ return -1;
+ }
- memcpy(tlv, data, length);
- tlv += length;
+ switch (dv_length) {
+ case 2:
+ /* data[dv_type] must be zero */
+ attrlen = data[dv_type + 1];
+ break;
- /*
- * 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;
+ case 1:
+ attrlen = data[dv_type];
+ break;
- if ((ptr[0] != PW_VENDOR_SPECIFIC) ||
- (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */
- (ptr[2] != 0) || (ptr[3] != 0)) { /* our requirement */
- continue;
- }
+ case 0:
+ attrlen = length;
+ break;
- memcpy(&lvalue, ptr + 2, 4);
- lvalue = ntohl(lvalue);
- lvalue <<= 16;
- lvalue |= ptr[2 + 4];
- if (lvalue != attribute) continue;
+ default:
+ fr_strerror_printf("attr2vp_vsa: Internal sanity check failed");
+ return -1;
+ }
- /*
- * If the vendor-length is too small, it's badly
- * formed, so we stop.
- */
- if ((ptr[2 + 4 + 1]) < 3) break;
+#ifndef NDEBUG
+ if (attrlen <= (ssize_t) (dv_type + dv_length)) {
+ fr_strerror_printf("attr2vp_vsa: Failure to call rad_tlv_ok");
+ return -1;
+ }
+#endif
- this_length = ptr[2 + 4 + 1] - 3;
- memcpy(tlv, ptr + 2 + 4 + 3, this_length);
- tlv += this_length;
+ attrlen -= (dv_type + dv_length);
+
+ my_len = data2vp_any(packet, original, secret, 0,
+ attribute, vendor,
+ data + dv_type + dv_length, attrlen, pvp);
+ if (my_len < 0) return my_len;
- ptr[2 + 4] = 0; /* What a hack! */
- if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
+#ifndef NDEBUG
+ if (my_len != attrlen) {
+ pairfree(pvp);
+ fr_strerror_printf("attr2vp_vsa: Incomplete decode %d != %d",
+ (int) my_len, (int) attrlen);
+ return -1;
}
+#endif
- *ptlv_length = tlv_length;
- return tlv_data;
+ return dv_type + dv_length + attrlen;
}
-/*
- * 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.
+/**
+ * @brief Convert one or more TLVs to VALUE_PAIRs. This function can
+ * be called recursively...
*/
-static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, int attribute,
- int length, /* CANNOT be zero */
- uint8_t *data, size_t packet_length,
- int flag, DICT_ATTR *da)
+static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ unsigned int attribute, unsigned int vendor,
+ int nest,
+ const uint8_t *start, size_t length,
+ VALUE_PAIR **pvp)
{
- size_t tlv_length, left;
- uint8_t *ptr;
- uint8_t *tlv_data;
- VALUE_PAIR *vp, *head, **tail;
+ size_t dv_type, dv_length;
+ const uint8_t *data, *end;
+ VALUE_PAIR *head, **last, *vp;
- /*
- * Ensure we have data that hasn't been split across
- * multiple attributes.
- */
- if (flag) {
- tlv_data = rad_coalesce(attribute, length,
- data, packet_length, &tlv_length);
- if (!tlv_data) return NULL;
- } else {
- tlv_data = data;
- tlv_length = length;
- }
+ data = start;
/*
- * 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
+ * The default format for a VSA is the RFC recommended
+ * format.
*/
- if (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, 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 */
+ dv_type = 1;
+ dv_length = 1;
/*
- * Now (sigh) we walk over the TLV, seeing if it is
- * well-formed.
+ * Top-level TLVs can be of a weird format. TLVs
+ * encapsulated in a TLV can only be in the RFC format.
*/
- 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;
+ if (nest == 1) {
+ DICT_VENDOR *dv;
+ dv = dict_vendorbyvalue(vendor);
+ if (dv) {
+ dv_type = dv->type;
+ dv_length = dv->length;
+ /* dict.c enforces sane values on the above fields */
}
- left -= ptr[1];
+ }
+
+ if (nest >= fr_attr_max_tlv) {
+ fr_strerror_printf("data2vp_tlvs: Internal sanity check failed in recursion");
+ return -1;
}
/*
- * Now we walk over the TLV *again*, creating sub-tlv's.
+ * The VSAs do not exactly fill the data,
+ * The *entire* TLV is malformed.
*/
+ if (rad_tlv_ok(data, length, dv_type, dv_length) < 0) {
+ VP_TRACE("TLV malformed %u.%u\n", vendor, attribute);
+ return data2vp_raw(packet, original, secret,
+ attribute, vendor, data, length, pvp);
+ }
+
+ end = data + length;
head = NULL;
- tail = &head;
+ last = &head;
+
+ while (data < end) {
+ unsigned int my_attr;
+ unsigned int my_len;
+
+#ifndef NDEBUG
+ if ((data + dv_type + dv_length) > end) {
+ fr_strerror_printf("data2vp_tlvs: Internal sanity check failed in tlvs: Insufficient data");
+ pairfree(&head);
+ return -1;
+ }
+#endif
+
+ switch (dv_type) {
+ case 1:
+ my_attr = attribute;
+ my_attr |= ((data[0] & fr_attr_mask[nest + 1])
+ << fr_attr_shift[nest + 1]);
+ break;
+ case 2:
+ my_attr = (data[0] << 8) | data[1];
+ break;
+
+ case 4:
+ my_attr = (data[1] << 16) | (data[1] << 8) | data[3];
+ break;
+
+ default:
+ fr_strerror_printf("data2vp_tlvs: Internal sanity check failed");
+ return -1;
+ }
+
+ switch (dv_length) {
+ case 0:
+ my_len = length;
+ break;
- for (ptr = tlv_data;
- ptr != (tlv_data + tlv_length);
- ptr += ptr[1]) {
- vp = paircreate(attribute | (ptr[0] << 8), PW_TYPE_OCTETS);
- if (!vp) {
+ case 1:
+ case 2:
+ my_len = data[dv_type + dv_length - 1];
+ break;
+
+ default:
+ fr_strerror_printf("data2vp_tlvs: Internal sanity check failed");
+ return -1;
+ }
+
+#ifndef NDEBUG
+ if (my_len < (dv_type + dv_length)) {
+ fr_strerror_printf("data2vp_tlvs: Internal sanity check failed in tlvs: underflow");
pairfree(&head);
- goto not_well_formed;
+ return -1;
}
- if (!data2vp(packet, original, secret,
- ptr[0], ptr[1] - 2, ptr + 2, vp)) {
+ if ((data + my_len) > end) {
+ fr_strerror_printf("data2vp_tlvs: Internal sanity check failed in tlvs: overflow");
pairfree(&head);
- goto not_well_formed;
+ return -1;
}
+#endif
- *tail = vp;
- tail = &(vp->next);
+ my_len -= dv_type + dv_length;
+
+ /*
+ * If this returns > 0, it returns "my_len"
+ */
+ if (data2vp_any(packet, original, secret, nest + 1,
+ my_attr, vendor,
+ data + dv_type + dv_length, my_len, &vp) < 0) {
+ pairfree(&head);
+ return -1;
+ }
+
+ data += my_len + dv_type + dv_length;
+ *last = vp;
+
+ while (vp) {
+ last = &(vp->next);
+ vp = vp->next;
+ }
}
+ *pvp = head;
+ return data - start;
+}
+
+
+/**
+ * @brief Group "continued" attributes together, and create VPs from them.
+ *
+ * The caller ensures that the RADIUS packet is OK, and that the
+ * continuations have all been checked.
+ */
+static ssize_t data2vp_continued(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ const uint8_t *start, size_t length,
+ VALUE_PAIR **pvp, int nest,
+ unsigned int attribute, unsigned int vendor,
+ int first_offset, int later_offset,
+ ssize_t attrlen)
+{
+ ssize_t left;
+ uint8_t *attr, *ptr;
+ const uint8_t *data;
+
+ attr = malloc(attrlen);
+ if (!attr) {
+ fr_strerror_printf("Out of memory");
+ return -1;
+ }
+
+ left = attrlen;
+ ptr = attr;
+ data = start;
+
/*
- * TLV's MAY be continued, but sometimes they're not.
+ * Do the first one.
*/
- if (tlv_data != data) free(tlv_data);
+ memcpy(ptr, data + first_offset, data[1] - first_offset);
+ ptr += data[1] - first_offset;
+ left -= data[1] - first_offset;
+ data += data[1];
+
+ while (left > 0) {
+#ifndef NDEBUG
+ if (data >= (start + length)) {
+ free(attr);
+ fr_strerror_printf("data2vp_continued: Internal sanity check failed");
+ return -1;
+ }
+#endif
+ memcpy(ptr, data + later_offset, data[1] - later_offset);
+ ptr += data[1] - later_offset;
+ left -= data[1] - later_offset;
+ data += data[1];
+ }
- if (head->next) rad_sortvp(&head);
+ left = data2vp_any(packet, original, secret, nest,
+ attribute, vendor,
+ attr, attrlen, pvp);
+ free(attr);
+ if (left < 0) return left;
- return head;
+ return data - start;
}
-/*
- * Parse a RADIUS attribute into a data structure.
+/**
+ * @brief Create a "raw" VALUE_PAIR from a RADIUS attribute.
*/
-VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, int attribute, int length,
- const uint8_t *data)
+ssize_t rad_attr2vp_raw(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
{
- VALUE_PAIR *vp;
+ ssize_t my_len;
- vp = paircreate(attribute, PW_TYPE_OCTETS);
- if (!vp) return NULL;
+ if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
+ fr_strerror_printf("rad_attr2vp_raw: Invalid length");
+ return -1;
+ }
- return data2vp(packet, original, secret, attribute, length, data, vp);
+ my_len = data2vp_raw(packet, original, secret, data[0], 0,
+ data + 2, data[1] - 2, pvp);
+ if (my_len < 0) return my_len;
+
+ return data[1];
}
-/*
- * Calculate/check digest, and decode radius attributes.
- * Returns:
- * -1 on decoding error
- * 0 on success
+/**
+ * @brief Get the length of the data portion of all of the contiguous
+ * continued attributes.
+ * @return
+ * 0 for "no continuation"
+ * -1 on malformed packets (continuation followed by non-wimax, etc.)
*/
-int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
- const char *secret)
+static ssize_t wimax_attrlen(uint32_t vendor,
+ const uint8_t *start, const uint8_t *end)
{
- uint32_t lvalue;
- uint32_t vendorcode;
- VALUE_PAIR **tail;
- VALUE_PAIR *pair;
- uint8_t *ptr, *vsa_ptr;
- int packet_length;
- int attribute;
- int attrlen;
- int vendorlen;
- radius_packet_t *hdr;
- int vsa_tlen, vsa_llen, vsa_offset;
- DICT_VENDOR *dv = NULL;
- int num_attributes = 0;
+ ssize_t total;
+ const uint8_t *data = start;
+
+ if ((data[8] & 0x80) == 0) return 0;
+ total = data[7] - 3;
+ data += data[1];
+
+ while (data < end) {
+
+ if ((data + 9) > end) return -1;
+
+ if ((data[0] != PW_VENDOR_SPECIFIC) ||
+ (data[1] < 9) ||
+ (memcmp(data + 2, &vendor, 4) != 0) ||
+ (data[6] != start[6]) ||
+ ((data[7] + 6) != data[1])) return -1;
+
+ total += data[7] - 3;
+ if ((data[8] & 0x80) == 0) break;
+ data += data[1];
+ }
+
+ return total;
+}
+
+
+/**
+ * @brief Get the length of the data portion of all of the contiguous
+ * continued attributes.
+ *
+ * @return
+ * 0 for "no continuation"
+ * -1 on malformed packets (continuation followed by non-wimax, etc.)
+ */
+static ssize_t extended_attrlen(const uint8_t *start, const uint8_t *end)
+{
+ ssize_t total;
+ const uint8_t *data = start;
+
+ if ((data[3] & 0x80) == 0) return 0;
+ total = data[1] - 4;
+ data += data[1];
+
+ while (data < end) {
+ if ((data + 4) > end) return -1;
+
+ if ((data[0] != start[0]) ||
+ (data[1] < 4) ||
+ (data[2] != start[2])) return -1;
+
+ total += data[1] - 4;
+ if ((data[3] & 0x80) == 0) break;
+ data += data[1];
+ }
+
+ return total;
+}
+
+
+/**
+ * @brief Create WiMAX VALUE_PAIRs from a RADIUS attribute.
+ */
+ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
+{
+ ssize_t my_len;
+ unsigned int attribute;
+ uint32_t lvalue;
+
+ if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
+ fr_strerror_printf("rad_attr2vp_wimax: Invalid length");
+ return -1;
+ }
+
+ if (data[0] != PW_VENDOR_SPECIFIC) {
+ fr_strerror_printf("rad_attr2vp_wimax: Invalid attribute");
+ return -1;
+ }
/*
- * Extract attribute-value pairs
+ * Not enough room for a Vendor-Id. + WiMAX header
*/
- hdr = (radius_packet_t *)packet->data;
- ptr = hdr->data;
- packet_length = packet->data_len - AUTH_HDR_LEN;
+ if (data[1] < 9) {
+ return rad_attr2vp_raw(packet, original, secret,
+ data, length, pvp);
+ }
+
+ memcpy(&lvalue, data + 2, 4);
+ lvalue = ntohl(lvalue);
/*
- * There may be VP's already in the packet. Don't
- * destroy them.
+ * Not WiMAX format.
*/
- for (tail = &packet->vps; *tail != NULL; tail = &((*tail)->next)) {
- /* nothing */
+ if (lvalue != VENDORPEC_WIMAX) {
+ DICT_VENDOR *dv;
+
+ dv = dict_vendorbyvalue(lvalue);
+ if (!dv || !dv->flags) {
+ fr_strerror_printf("rad_attr2vp_wimax: Not a WiMAX attribute");
+ return -1;
+ }
}
- vendorcode = 0;
- vendorlen = 0;
- vsa_tlen = vsa_llen = 1;
- vsa_offset = 0;
+ /*
+ * The WiMAX attribute is encapsulated in a VSA. If the
+ * WiMAX length disagrees with the VSA length, it's malformed.
+ */
+ if ((data[7] + 6) != data[1]) {
+ return rad_attr2vp_raw(packet, original, secret,
+ data, length, pvp);
+ }
+
+ attribute = data[6];
/*
- * We have to read at least two bytes.
- *
- * rad_recv() above ensures that this is OK.
+ * Attribute is continued. Do some more work.
*/
- while (packet_length > 0) {
- attribute = -1;
- attrlen = -1;
+ if (data[8] != 0) {
+ my_len = wimax_attrlen(htonl(lvalue), data, data + length);
+ if (my_len < 0) {
+ return rad_attr2vp_raw(packet, original, secret,
+ data, length, pvp);
+ }
- /*
- * Normal attribute, handle it like normal.
- */
- if (vendorcode == 0) {
- /*
- * No room to read attr/length,
- * or bad attribute, or attribute is
- * too short, or attribute is too long,
- * stop processing the packet.
- */
- if ((packet_length < 2) ||
- (ptr[0] == 0) || (ptr[1] < 2) ||
- (ptr[1] > packet_length)) break;
+ return data2vp_continued(packet, original, secret,
+ data, length, pvp, 0,
+ data[6], lvalue,
+ 9, 9, my_len);
+ }
- attribute = *ptr++;
- attrlen = *ptr++;
+ my_len = data2vp_any(packet, original, secret, 0, attribute, lvalue,
+ data + 9, data[1] - 9, pvp);
+ if (my_len < 0) return my_len;
- attrlen -= 2;
- packet_length -= 2;
+ return data[1];
+}
- if (attribute != PW_VENDOR_SPECIFIC) goto create_pair;
+/**
+ * @brief Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
+ */
+ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
+{
+ size_t dv_type, dv_length;
+ ssize_t my_len;
+ uint32_t lvalue;
+ DICT_VENDOR *dv;
- /*
- * No vendor code, or ONLY vendor code.
- */
- if (attrlen <= 4) goto create_pair;
+ if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
+ fr_strerror_printf("rad_attr2vp_vsa: Invalid length");
+ return -1;
+ }
- vendorlen = 0;
- }
+ if (data[0] != PW_VENDOR_SPECIFIC) {
+ fr_strerror_printf("rad_attr2vp_vsa: Invalid attribute");
+ return -1;
+ }
- /*
- * Handle Vendor-Specific
- */
- if (vendorlen == 0) {
- uint8_t *subptr;
- int sublen;
- int myvendor;
+ /*
+ * Not enough room for a Vendor-Id.
+ * Or the high octet of the Vendor-Id is set.
+ */
+ if ((data[1] < 6) || (data[2] != 0)) {
+ return rad_attr2vp_raw(packet, original, secret,
+ data, length, pvp);
+ }
- /*
- * attrlen was checked above.
- */
- memcpy(&lvalue, ptr, 4);
- myvendor = ntohl(lvalue);
+ memcpy(&lvalue, data + 2, 4);
+ lvalue = ntohl(lvalue);
- /*
- * Zero isn't allowed.
- */
- if (myvendor == 0) goto create_pair;
+ /*
+ * WiMAX gets its own set of magic.
+ */
+ if (lvalue == VENDORPEC_WIMAX) {
+ wimax:
+ return rad_attr2vp_wimax(packet, original, secret,
+ data, length, pvp);
+ }
- /*
- * 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.
- */
- if (myvendor > 65535) goto create_pair;
-
- vsa_tlen = vsa_llen = 1;
- vsa_offset = 0;
- dv = dict_vendorbyvalue(myvendor);
- if (dv) {
- vsa_tlen = dv->type;
- vsa_llen = dv->length;
- if (dv->flags) vsa_offset = 1;
- }
+ dv_type = dv_length = 1;
+ dv = dict_vendorbyvalue(lvalue);
+ if (dv) {
+ dv_type = dv->type;
+ dv_length = dv->length;
- /*
- * Sweep through the list of VSA's,
- * seeing if they exactly fill the
- * outer Vendor-Specific attribute.
- *
- * If not, create a raw Vendor-Specific.
- */
- subptr = ptr + 4;
- sublen = attrlen - 4;
+ if (dv->flags) goto wimax;
+ }
- /*
- * See if we can parse it.
- */
- do {
- int myattr = 0;
-
- /*
- * Not enough room for one more
- * attribute. Die!
- */
- if (sublen < (vsa_tlen + vsa_llen + vsa_offset)) goto create_pair;
-
- /*
- * Ensure that the attribute number
- * is OK.
- */
- switch (vsa_tlen) {
- case 1:
- myattr = subptr[0];
- break;
-
- case 2:
- myattr = (subptr[0] << 8) | subptr[1];
- break;
-
- case 4:
- if ((subptr[0] != 0) ||
- (subptr[1] != 0)) goto create_pair;
-
- myattr = (subptr[2] << 8) | subptr[3];
- break;
-
- /*
- * Our dictionary is broken.
- */
- default:
- goto create_pair;
- }
+ /*
+ * Attribute is not in the correct form.
+ */
+ if (rad_tlv_ok(data + 6, data[1] - 6, dv_type, dv_length) < 0) {
+ return rad_attr2vp_raw(packet, original, secret,
+ data, length, pvp);
+ }
- switch (vsa_llen) {
- case 0:
- attribute = (myvendor << 16) | myattr;
- ptr += 4 + vsa_tlen;
- attrlen -= (4 + vsa_tlen);
- packet_length -= 4 + vsa_tlen;
- goto create_pair;
-
- case 1:
- if (subptr[vsa_tlen] < (vsa_tlen + vsa_llen + vsa_offset))
- goto create_pair;
-
- if (subptr[vsa_tlen] > sublen)
- goto create_pair;
-
- /*
- * WiMAX: 0bCrrrrrrr
- * Reserved bits MUST be
- * zero.
- */
- if (vsa_offset &&
- ((subptr[vsa_tlen + vsa_llen] & 0x7f) != 0))
- goto create_pair;
-
- sublen -= subptr[vsa_tlen];
- subptr += subptr[vsa_tlen];
- break;
-
- case 2:
- if (subptr[vsa_tlen] != 0) goto create_pair;
- if (subptr[vsa_tlen + 1] < (vsa_tlen + vsa_llen))
- goto create_pair;
- if (subptr[vsa_tlen + 1] > sublen)
- goto create_pair;
- sublen -= subptr[vsa_tlen + 1];
- subptr += subptr[vsa_tlen + 1];
- break;
-
- /*
- * Our dictionaries are
- * broken.
- */
- default:
- goto create_pair;
- }
- } while (sublen > 0);
+ my_len = attr2vp_vsa(packet, original, secret,
+ lvalue, dv_type, dv_length,
+ data + 6, data[1] - 6, pvp);
+ if (my_len < 0) return my_len;
- vendorcode = myvendor;
- vendorlen = attrlen - 4;
- packet_length -= 4;
+ /*
+ * Incomplete decode means that something is wrong
+ * with the attribute. Back up, and make it "raw".
+ */
+ if (my_len != (data[1] - 6)) {
+ pairfree(pvp);
+ return rad_attr2vp_raw(packet, original, secret,
+ data, length, pvp);
+ }
- ptr += 4;
- }
+ return data[1];
+}
- /*
- * attrlen is the length of this attribute.
- * total_len is the length of the encompassing
- * attribute.
- */
- switch (vsa_tlen) {
- case 1:
- attribute = ptr[0];
- break;
+/**
+ * @brief Create an "extended" VALUE_PAIR from a RADIUS attribute.
+ */
+ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ const uint8_t *start, size_t length,
+ VALUE_PAIR **pvp)
+{
+ unsigned int attribute;
+ int shift = 1;
+ int continued = 0;
+ unsigned int vendor = VENDORPEC_EXTENDED;
+ size_t data_len = length;
+ const uint8_t *data;
+ DICT_ATTR *da;
- case 2:
- attribute = (ptr[0] << 8) | ptr[1];
- break;
+ data = start;
- default: /* can't hit this. */
- return -1;
- }
- attribute |= (vendorcode << 16);
- vsa_ptr = ptr;
- ptr += vsa_tlen;
+ if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
+ fr_strerror_printf("rad_attr2vp_extended: Invalid length");
+ return -1;
+ }
- switch (vsa_llen) {
- case 1:
- attrlen = ptr[0] - (vsa_tlen + vsa_llen + vsa_offset);
- break;
+ da = dict_attrbyvalue(data[0], vendor);
+ if (!da ||
+ (!da->flags.extended && !da->flags.extended_flags)) {
+ fr_strerror_printf("rad_attr2vp_extended: Attribute is not extended format");
+ return -1;
+ }
- case 2:
- attrlen = ptr[1] - (vsa_tlen + vsa_llen);
- break;
+ data = start;
- default: /* can't hit this. */
- return -1;
- }
+ /*
+ * No Extended-Type. It's a raw attribute.
+ * Also, if there's no data following the Extended-Type,
+ * it's a raw attribute.
+ */
+ if (data[1] <= 3) {
+ raw:
+ return rad_attr2vp_raw(packet, original, secret, start,
+ length, pvp);
+ }
- 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);
+ /*
+ * The attribute is "241.1", for example. Go look that
+ * up to see what type it is.
+ */
+ attribute = data[0];
+ attribute |= (data[2] << fr_attr_shift[1]);
+
+ da = dict_attrbyvalue(attribute, vendor);
+ if (!da) goto raw;
+
+ vendor = VENDORPEC_EXTENDED;
+ data_len = length;
+ if (data[1] < length) data_len = data[1];
+
+ data += 3;
+ data_len -= 3;
+
+ /*
+ * If there's supposed to be a flag octet. If not, it's
+ * a raw attribute. If the flag is set, it's supposed to
+ * be continued.
+ */
+ if (da->flags.extended_flags) {
+ if (data_len == 0) goto raw;
+
+ continued = ((data[0] & 0x80) != 0);
+ data++;
+ data_len--;
+ }
+
+ /*
+ * Extended VSAs have 4 octets of
+ * Vendor-Id followed by one octet of
+ * Vendor-Type.
+ */
+ if (da->flags.evs) {
+ if (data_len < 5) goto raw;
+
/*
- * Ignore VSAs that have no data.
+ * Vendor Ids can only be 24-bit.
*/
- if (attrlen == 0) goto next;
-
+ if (data[0] != 0) goto raw;
+
+ vendor = ((data[1] << 16) |
+ (data[2] << 8) |
+ data[3]);
+
/*
- * WiMAX attributes of type 0 are ignored. They
- * are a secret flag to us that the attribute has
- * already been dealt with.
+ * Pack the *encapsulating* attribute number into
+ * the vendor id. This number should be >= 241.
*/
- if (attribute == 0x60b50000) goto next;
+ vendor |= start[0] * FR_MAX_VENDOR;
+ shift = 0;
+
+ /*
+ * Over-write the attribute with the
+ * VSA.
+ */
+ attribute = data[4];
+ data += 5;
+ data_len -= 5;
+ }
- if (vsa_offset) {
- DICT_ATTR *da;
+ if (continued) {
+ int first_offset = 4;
+ ssize_t my_len;
- da = dict_attrbyvalue(attribute);
+ if (vendor != VENDORPEC_EXTENDED) first_offset += 5;
- /*
- * If it's NOT continued, AND we know
- * about it, AND it's not a TLV, we can
- * create a normal pair.
- */
- if (((vsa_ptr[2] & 0x80) == 0) &&
- da && (da->type != PW_TYPE_TLV)) goto create_pair;
+ my_len = extended_attrlen(start, start + length);
+ if (my_len < 0) goto raw;
- /*
- * Else it IS continued, or it's a TLV.
- * Go do a lot of work to find the stuff.
- */
- pair = rad_continuation2vp(packet, original, secret,
- attribute, attrlen, ptr,
- packet_length,
- ((vsa_ptr[2] & 0x80) != 0),
- da);
- goto created_pair;
- }
+ if (vendor != VENDORPEC_EXTENDED) my_len -= 5;
+
+ return data2vp_continued(packet, original, secret,
+ start, length, pvp, shift,
+ attribute, vendor,
+ first_offset, 4, my_len);
+ }
+
+ if (data2vp_any(packet, original, secret, shift,
+ attribute, vendor, data, data_len, pvp) < 0) {
+ return -1;
+ }
+
+ return (data + data_len) - start;
+}
+
+
+/**
+ * @brief Create a "standard" RFC VALUE_PAIR from the given data.
+ */
+ssize_t rad_attr2vp_rfc(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
+{
+ if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
+ fr_strerror_printf("rad_attr2vp_rfc: Insufficient data");
+ return -1;
+ }
+
+ if (data2vp_any(packet, original, secret, 0,
+ data[0], 0, data + 2, data[1] - 2, pvp) < 0) {
+ return -1;
+ }
+
+ return data[1];
+}
+
+/**
+ * @brief Create a "normal" VALUE_PAIR from the given data.
+ */
+ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ const uint8_t *data, size_t length,
+ VALUE_PAIR **pvp)
+{
+ if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
+ fr_strerror_printf("rad_attr2vp: Insufficient data");
+ return -1;
+ }
+
+ /*
+ * VSAs get their own handler.
+ */
+ if (data[0] == PW_VENDOR_SPECIFIC) {
+ return rad_attr2vp_vsa(packet, original, secret,
+ data, length, pvp);
+ }
+
+ /*
+ * Extended attribute format gets their own handler.
+ */
+ if (dict_attrbyvalue(data[0], VENDORPEC_EXTENDED) != NULL) {
+ return rad_attr2vp_extended(packet, original, secret,
+ data, length, pvp);
+ }
+
+ return rad_attr2vp_rfc(packet, original, secret, data, length, pvp);
+}
+
+
+/**
+ * @brief Calculate/check digest, and decode radius attributes.
+ * @return -1 on decoding error, 0 on success
+ */
+int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
+ const char *secret)
+{
+ int packet_length;
+ int num_attributes;
+ uint8_t *ptr;
+ radius_packet_t *hdr;
+ VALUE_PAIR *head, **tail, *vp;
+
+ /*
+ * Extract attribute-value pairs
+ */
+ hdr = (radius_packet_t *)packet->data;
+ ptr = hdr->data;
+ packet_length = packet->data_len - AUTH_HDR_LEN;
+
+ head = NULL;
+ tail = &head;
+ num_attributes = 0;
+
+ /*
+ * Loop over the attributes, decoding them into VPs.
+ */
+ while (packet_length > 0) {
+ ssize_t my_len;
/*
- * Create the attribute, setting the default type
- * to 'octets'. If the type in the dictionary
- * is different, then the dictionary type will
- * over-ride this one.
- *
- * If the attribute has no data, then discard it.
- *
- * Unless it's CUI. Damn you, CUI!
+ * This may return many VPs
*/
- create_pair:
- if (!attrlen &&
- (attribute != PW_CHARGEABLE_USER_IDENTITY)) goto next;
-
- pair = rad_attr2vp(packet, original, secret,
- attribute, attrlen, ptr);
- if (!pair) {
- pairfree(&packet->vps);
- fr_strerror_printf("out of memory");
+ my_len = rad_attr2vp(packet, original, secret,
+ ptr, packet_length, &vp);
+ if (my_len < 0) {
+ pairfree(&head);
return -1;
}
- created_pair:
- *tail = pair;
- while (pair) {
+ *tail = vp;
+ while (vp) {
num_attributes++;
- debug_pair(pair);
- tail = &pair->next;
- pair = pair->next;
+ debug_pair(vp);
+ tail = &(vp->next);
+ vp = vp->next;
}
/*
(num_attributes > fr_max_attributes)) {
char host_ipaddr[128];
- pairfree(&packet->vps);
+ pairfree(&head);
fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
return -1;
}
- next:
- ptr += attrlen;
- packet_length -= attrlen;
+ ptr += my_len;
+ packet_length -= my_len;
}
/*
* random pool.
*/
fr_rand_seed(packet->data, AUTH_HDR_LEN);
+
+ /*
+ * There may be VP's already in the packet. Don't
+ * destroy them. Instead, add the decoded attributes to
+ * the tail of the list.
+ */
+ for (tail = &packet->vps; *tail != NULL; tail = &((*tail)->next)) {
+ /* nothing */
+ }
+ *tail = head;
return 0;
}
-/*
- * Encode password.
+/**
+ * @brief Encode password.
*
* We assume that the passwd buffer passed is big enough.
* RFC2138 says the password is max 128 chars, so the size
return 0;
}
-/*
- * Decode password.
+/**
+ * @brief Decode password.
*/
int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
const uint8_t *vector)
}
-/*
- * Encode Tunnel-Password attributes when sending them out on the wire.
+/**
+ * @brief Encode Tunnel-Password attributes when sending them out on the wire.
*
* int *pwlen is updated to the new length of the encrypted
* password - a multiple of 16 bytes.
return 0;
}
-/*
- * Decode Tunnel-Password encrypted attributes.
+/**
+ * @brief Decode Tunnel-Password encrypted attributes.
*
* Defined in RFC-2868, this uses a two char SALT along with the
* initial intermediate value, to differentiate it from the
return reallen;
}
-/*
- * Encode a CHAP password
+/**
+ * @brief Encode a CHAP password
*
- * FIXME: might not work with Ascend because
+ * @bug FIXME: might not work with Ascend because
* we use vp->length, and Ascend gear likes
* to send an extra '\0' in the string!
*/
* Use Chap-Challenge pair if present,
* Request-Authenticator otherwise.
*/
- challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE);
+ challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE, 0);
if (challenge) {
memcpy(ptr, challenge->vp_strvalue, challenge->length);
i += challenge->length;
}
-/*
- * Seed the random number generator.
+/**
+ * @brief Seed the random number generator.
*
* May be called any number of times.
*/
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);
}
-/*
- * Return a 32-bit random number.
+/**
+ * @brief Return a 32-bit random number.
*/
uint32_t fr_rand(void)
{
}
-/*
- * Allocate a new RADIUS_PACKET
+/**
+ * @brief Allocate a new RADIUS_PACKET
*/
RADIUS_PACKET *rad_alloc(int newvector)
{
}
-/*
- * Free a RADIUS_PACKET
+/**
+ * @brief Free a RADIUS_PACKET
*/
void rad_free(RADIUS_PACKET **radius_packet_ptr)
{