#include <freeradius-devel/ident.h>
RCSID("$Id$")
-#include <freeradius-devel/autoconf.h>
+#include <freeradius-devel/libradius.h>
#include <freeradius-devel/md5.h>
-#include <stdlib.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
#include <fcntl.h>
#include <ctype.h>
#include <freeradius-devel/udpfromto.h>
#endif
-
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
-#ifdef WIN32
-#include <process.h>
-#endif
-
-#include <freeradius-devel/missing.h>
-#include <freeradius-devel/libradius.h>
-
/*
* The RFC says 4096 octets max, and most packets are less than 256.
*/
* is unsigned, and the attacker can use resources on the server,
* even if the end request is rejected.
*/
-int librad_max_attributes = 0;
+int fr_max_attributes = 0;
+FILE *fr_log_fp = NULL;
typedef struct radius_packet_t {
uint8_t code;
uint8_t data[1];
} radius_packet_t;
-static lrad_randctx lrad_rand_pool; /* across multiple calls */
-static int lrad_rand_initialized = 0;
+static fr_randctx fr_rand_pool; /* across multiple calls */
+static int fr_rand_initialized = 0;
static unsigned int salt_offset = 0;
-
-#define MAX_PACKET_CODE (52)
-static const char *packet_codes[] = {
+const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
"",
"Access-Request",
"Access-Accept",
};
+void fr_printf_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if ((fr_debug_flag == 0) || !fr_log_fp) {
+ va_end(ap);
+ return;
+ }
+
+ vfprintf(fr_log_fp, fmt, ap);
+ va_end(ap);
+
+ return;
+}
+
+static void print_hex(RADIUS_PACKET *packet)
+{
+ int i;
+
+ if (!packet->data) return;
+
+ printf(" Code:\t\t%u\n", packet->data[0]);
+ printf(" Id:\t\t%u\n", packet->data[1]);
+ printf(" Length:\t%u\n", ((packet->data[2] << 8) |
+ (packet->data[3])));
+ printf(" Vector:\t");
+ for (i = 4; i < 20; i++) {
+ printf("%02x", packet->data[i]);
+ }
+ printf("\n");
+
+ if (packet->data_len > 20) {
+ int total;
+ const uint8_t *ptr;
+ printf(" Data:");
+
+ total = packet->data_len - 20;
+ ptr = packet->data + 20;
+
+ while (total > 0) {
+ int attrlen;
+
+ printf("\t\t");
+ if (total < 2) { /* too short */
+ printf("%02x\n", *ptr);
+ break;
+ }
+
+ if (ptr[1] > total) { /* too long */
+ for (i = 0; i < total; i++) {
+ printf("%02x ", ptr[i]);
+ }
+ break;
+ }
+
+ printf("%02x %02x ", ptr[0], ptr[1]);
+ attrlen = ptr[1] - 2;
+ ptr += 2;
+ total -= 2;
+
+ for (i = 0; i < attrlen; i++) {
+ if ((i > 0) && ((i & 0x0f) == 0x00))
+ printf("\t\t\t");
+ printf("%02x ", ptr[i]);
+ if ((i & 0x0f) == 0x0f) printf("\n");
+ }
+
+ if ((attrlen & 0x0f) != 0x00) printf("\n");
+
+ ptr += attrlen;
+ total -= attrlen;
+ }
+ }
+ fflush(stdout);
+}
+
+
/*
* 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,
- lrad_ipaddr_t *src_ipaddr, lrad_ipaddr_t *dst_ipaddr,
- int dst_port)
+ fr_ipaddr_t *src_ipaddr, int src_port,
+ fr_ipaddr_t *dst_ipaddr, int dst_port)
{
struct sockaddr_storage dst;
- socklen_t sizeof_dst = sizeof(dst);
+ socklen_t sizeof_dst;
#ifdef WITH_UDPFROMTO
struct sockaddr_storage src;
- socklen_t sizeof_src = sizeof(src);
-
- memset(&src, 0, sizeof(src));
-#endif
- memset(&dst, 0, sizeof(dst));
-
- /*
- * IPv4 is supported.
- */
- if (dst_ipaddr->af == AF_INET) {
- struct sockaddr_in *s4;
-
- s4 = (struct sockaddr_in *)&dst;
- sizeof_dst = sizeof(struct sockaddr_in);
+ socklen_t sizeof_src;
- s4->sin_family = AF_INET;
- s4->sin_addr = dst_ipaddr->ipaddr.ip4addr;
- s4->sin_port = htons(dst_port);
-
-#ifdef WITH_UDPFROMTO
- s4 = (struct sockaddr_in *)&src;
- sizeof_src = sizeof(struct sockaddr_in);
-
- s4->sin_family = AF_INET;
- s4->sin_addr = src_ipaddr->ipaddr.ip4addr;
+ fr_ipaddr2sockaddr(src_ipaddr, src_port, &src, &sizeof_src);
+#else
+ src_port = src_port; /* -Wunused */
#endif
- /*
- * IPv6 MAY be supported.
- */
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- } else if (dst_ipaddr->af == AF_INET6) {
- struct sockaddr_in6 *s6;
-
- s6 = (struct sockaddr_in6 *)&dst;
- sizeof_dst = sizeof(struct sockaddr_in6);
-
- s6->sin6_family = AF_INET6;
- s6->sin6_addr = dst_ipaddr->ipaddr.ip6addr;
- s6->sin6_port = htons(dst_port);
-
-#ifdef WITH_UDPFROMTO
- return -1; /* UDPFROMTO && IPv6 are not supported */
-#if 0
- s6 = (struct sockaddr_in6 *)&src;
- sizeof_src = sizeof(struct sockaddr_in6);
-
- s6->sin6_family = AF_INET6;
- s6->sin6_addr = src_ipaddr->ipaddr.ip6addr;
-#endif /* #if 0 */
-#endif /* WITH_UDPFROMTO */
-#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
- } else return -1; /* Unknown address family, Die Die Die! */
+ if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) {
+ return -1;
+ }
#ifdef WITH_UDPFROMTO
/*
* 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,
+ (struct sockaddr *)&src, sizeof_src,
(struct sockaddr *)&dst, sizeof_dst);
}
#else
/*
* No udpfromto, OR an IPv6 socket, fail gracefully.
*/
- return sendto(sockfd, data, data_len, flags,
- (struct sockaddr *)&dst, sizeof_dst);
+ return sendto(sockfd, data, data_len, flags,
+ (struct sockaddr *) &dst, sizeof_dst);
+}
+
+
+void rad_recv_discard(int sockfd)
+{
+ uint8_t header[4];
+ struct sockaddr_storage src;
+ socklen_t sizeof_src = sizeof(src);
+
+ recvfrom(sockfd, header, sizeof(header), 0,
+ (struct sockaddr *)&src, &sizeof_src);
+}
+
+
+ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
+ int *code)
+{
+ ssize_t data_len, packet_len;
+ uint8_t header[4];
+ struct sockaddr_storage src;
+ socklen_t sizeof_src = sizeof(src);
+
+ data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
+ (struct sockaddr *)&src, &sizeof_src);
+ if (data_len < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR)) return 0;
+ return -1;
+ }
+
+ /*
+ * Too little data is available, discard the packet.
+ */
+ if (data_len < 4) {
+ recvfrom(sockfd, header, sizeof(header), 0,
+ (struct sockaddr *)&src, &sizeof_src);
+ return 1;
+
+ } else { /* we got 4 bytes of data. */
+ /*
+ * See how long the packet says it is.
+ */
+ packet_len = (header[2] * 256) + header[3];
+
+ /*
+ * The length in the packet says it's less than
+ * a RADIUS header length: discard it.
+ */
+ if (packet_len < AUTH_HDR_LEN) {
+ recvfrom(sockfd, header, sizeof(header), 0,
+ (struct sockaddr *)&src, &sizeof_src);
+ return 1;
+
+ /*
+ * Enforce RFC requirements, for sanity.
+ * Anything after 4k will be discarded.
+ */
+ } else if (packet_len > MAX_PACKET_LEN) {
+ recvfrom(sockfd, header, sizeof(header), 0,
+ (struct sockaddr *)&src, &sizeof_src);
+ return 1;
+ }
+ }
+
+ /*
+ * Convert AF. If unknown, discard packet.
+ */
+ if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
+ recvfrom(sockfd, header, sizeof(header), 0,
+ (struct sockaddr *)&src, &sizeof_src);
+ return 1;
+ }
+
+ *code = header[0];
+
+ /*
+ * The packet says it's this long, but the actual UDP
+ * size could still be smaller.
+ */
+ return packet_len;
}
/*
- * Wrapper for recvfrom, which handles recvfromto, IPv6, and all
+ * wrapper for recvfrom, which handles recvfromto, IPv6, and all
* possible combinations.
*/
static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
- lrad_ipaddr_t *src_ipaddr, uint16_t *src_port,
- lrad_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
+ fr_ipaddr_t *src_ipaddr, uint16_t *src_port,
+ fr_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
{
struct sockaddr_storage src;
struct sockaddr_storage dst;
uint8_t header[4];
void *buf;
size_t len;
+ int port;
memset(&src, 0, sizeof_src);
memset(&dst, 0, sizeof_dst);
*/
data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
(struct sockaddr *)&src, &sizeof_src);
- if (data_len < 0) return -1;
+ if (data_len < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR)) return 0;
+ return -1;
+ }
/*
- * Too little data is available, round it up to 4 bytes.
+ * Too little data is available, discard the packet.
*/
if (data_len < 4) {
- len = 4;
+ recvfrom(sockfd, header, sizeof(header), flags,
+ (struct sockaddr *)&src, &sizeof_src);
+ return 0;
+
} else { /* we got 4 bytes of data. */
/*
* See how long the packet says it is.
len = (header[2] * 256) + header[3];
/*
- * Too short: read 4 bytes, and discard the rest.
+ * The length in the packet says it's less than
+ * a RADIUS header length: discard it.
*/
- if (len < 4) {
- len = 4;
+ if (len < AUTH_HDR_LEN) {
+ recvfrom(sockfd, header, sizeof(header), flags,
+ (struct sockaddr *)&src, &sizeof_src);
+ return 0;
/*
* Enforce RFC requirements, for sanity.
* Anything after 4k will be discarded.
*/
} else if (len > MAX_PACKET_LEN) {
- len = MAX_PACKET_LEN;
+ recvfrom(sockfd, header, sizeof(header), flags,
+ (struct sockaddr *)&src, &sizeof_src);
+ return len;
}
}
#ifdef WITH_UDPFROMTO
if (dst.ss_family == AF_INET) {
data_len = recvfromto(sockfd, buf, len, flags,
- (struct sockaddr *)&src, &sizeof_src,
+ (struct sockaddr *)&src, &sizeof_src,
(struct sockaddr *)&dst, &sizeof_dst);
} else
#endif
/*
* No udpfromto, OR an IPv6 socket. Fail gracefully.
*/
- data_len = recvfrom(sockfd, buf, len, flags,
+ data_len = recvfrom(sockfd, buf, len, flags,
(struct sockaddr *)&src, &sizeof_src);
if (data_len < 0) {
free(buf);
return data_len;
}
- /*
- * Check address families, and update src/dst ports, etc.
- */
- if (src.ss_family == AF_INET) {
- struct sockaddr_in *s4;
-
- s4 = (struct sockaddr_in *)&src;
- src_ipaddr->af = AF_INET;
- src_ipaddr->ipaddr.ip4addr = s4->sin_addr;
- *src_port = ntohs(s4->sin_port);
-
- s4 = (struct sockaddr_in *)&dst;
- dst_ipaddr->af = AF_INET;
- dst_ipaddr->ipaddr.ip4addr = s4->sin_addr;
- *dst_port = ntohs(s4->sin_port);
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- } else if (src.ss_family == AF_INET6) {
- struct sockaddr_in6 *s6;
-
- s6 = (struct sockaddr_in6 *)&src;
- src_ipaddr->af = AF_INET6;
- src_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
- *src_port = ntohs(s6->sin6_port);
-
- s6 = (struct sockaddr_in6 *)&dst;
- dst_ipaddr->af = AF_INET6;
- dst_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
- *dst_port = ntohs(s6->sin6_port);
-#endif
- } else {
+ if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) {
free(buf);
return -1; /* Unknown address family, Die Die Die! */
}
-
+ *src_port = port;
+
+ fr_sockaddr2ipaddr(&dst, sizeof_dst, dst_ipaddr, &port);
+ *dst_port = port;
+
/*
* Different address families should never happen.
*/
static void make_secret(uint8_t *digest, const uint8_t *vector,
const char *secret, const uint8_t *value)
{
- lrad_MD5_CTX context;
+ FR_MD5_CTX context;
int i;
- lrad_MD5Init(&context);
- lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- lrad_MD5Update(&context, secret, strlen(secret));
- lrad_MD5Final(digest, &context);
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+ fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+ fr_MD5Final(digest, &context);
for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
digest[i] ^= value[i];
}
#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, size_t *outlen,
+ const uint8_t *input, size_t inlen,
const char *secret, const uint8_t *vector)
{
- lrad_MD5_CTX context, old;
+ FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
uint8_t passwd[MAX_PASS_LEN];
int i, n;
* If the length is zero, round it up.
*/
len = inlen;
+
+ if (len > MAX_PASS_LEN) len = MAX_PASS_LEN;
+
+ memcpy(passwd, input, len);
+ memset(passwd + len, 0, sizeof(passwd) - len);
+
if (len == 0) {
len = AUTH_PASS_LEN;
}
- else if (len > MAX_PASS_LEN) len = MAX_PASS_LEN;
else if ((len & 0x0f) != 0) {
len += 0x0f;
}
*outlen = len;
- memcpy(passwd, input, len);
- memset(passwd + len, 0, sizeof(passwd) - len);
-
- lrad_MD5Init(&context);
- lrad_MD5Update(&context, secret, strlen(secret));
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
old = context;
/*
* Do first pass.
*/
- lrad_MD5Update(&context, vector, AUTH_PASS_LEN);
+ fr_MD5Update(&context, vector, AUTH_PASS_LEN);
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
context = old;
- lrad_MD5Update(&context,
+ fr_MD5Update(&context,
passwd + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
}
- lrad_MD5Final(digest, &context);
+ fr_MD5Final(digest, &context);
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + n] ^= digest[i];
}
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, size_t *outlen,
+ const uint8_t *input, size_t inlen, size_t room,
const char *secret, const uint8_t *vector)
{
- lrad_MD5_CTX context, old;
+ FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
uint8_t passwd[MAX_STRING_LEN + AUTH_VECTOR_LEN];
int i, n;
* add in some CSPRNG data. should be OK..
*/
passwd[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
- (lrad_rand() & 0x07));
- passwd[1] = lrad_rand();
+ (fr_rand() & 0x07));
+ passwd[1] = fr_rand();
passwd[2] = inlen; /* length of the password string */
- lrad_MD5Init(&context);
- lrad_MD5Update(&context, secret, strlen(secret));
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
old = context;
- lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- lrad_MD5Update(&context, &passwd[0], 2);
+ fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+ fr_MD5Update(&context, &passwd[0], 2);
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
context = old;
- lrad_MD5Update(&context,
+ fr_MD5Update(&context,
passwd + 2 + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
}
- lrad_MD5Final(digest, &context);
+ fr_MD5Final(digest, &context);
+
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + 2 + n] ^= digest[i];
}
}
/*
- * Hack for IETF RADEXT. Don't use in a production environment!
- *
- * FIXME: Pack multiple diameter attributes into one
- * Extended-Attribute!
- */
-static int vp2diameter(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original, const char *secret,
- const VALUE_PAIR *vp, uint8_t *ptr)
-{
- uint32_t attr;
- uint32_t length = vp->length;
- uint32_t vendor;
- int total, align;
- uint8_t *length_ptr;
-
- *(ptr++) = PW_EXTENDED_ATTRIBUTE;
- length_ptr = ptr++; /* fill this in later. */
- total = 2;
-
- attr = vp->attribute;
- attr &= ~(1 << 31); /* implemented limitations, see dict_addattr */
- vendor = VENDOR(attr);
- attr = attr & 0xffff;
- attr = ntohl(attr);
-
- memcpy(ptr, &attr, sizeof(attr));
- ptr += 4;
- total += 4;
- length += 8; /* includes 8 bytes of attr & length */
- align = 0; /* no padding */
-
- if (vendor != 0) {
- length += 4; /* include 4 bytes of vendor */
-
- length |= (1 << 31);
- length = ntohl(length);
- memcpy(ptr, &length, sizeof(length));
- ptr += 4;
- total += 4;
-
- vendor = ntohl(vendor);
- memcpy(ptr, &vendor, sizeof(vendor));
- ptr += 4;
- total += 4;
- } else {
- length = ntohl(length);
- memcpy(ptr, &length, sizeof(length));
- ptr += 4;
- total += 4;
- }
- *length_ptr = total;
-
- switch (vp->type) {
- case PW_TYPE_INTEGER:
- case PW_TYPE_DATE:
- attr = ntohl(vp->lvalue); /* stored in host order */
- memcpy(ptr, &attr, sizeof(attr));
- length = 4;
- break;
-
- case PW_TYPE_IPADDR:
- attr = vp->lvalue; /* stored in network order */
- memcpy(ptr, &attr, sizeof(attr));
- length = 4;
- break;
-
- case PW_TYPE_IFID:
- case PW_TYPE_IPV6ADDR:
- case PW_TYPE_IPV6PREFIX:
- case PW_TYPE_ABINARY:
- memcpy(ptr, vp->vp_strvalue, vp->length);
- length = vp->length;
- if ((length & 0x03) != 0) align = 4 - (length & 0x03);
- break;
-
- /*
- * Length MAY be larger than can fit into the
- * encapsulating attribute!
- */
- case PW_TYPE_STRING:
- case PW_TYPE_OCTETS:
- default:
- {
- uint32_t offset = 0;
-
- length = vp->length;
- if ((length & 0x03) != 0) align = 4 - (length & 0x03);
-
- if (total + length > 255) {
- *length_ptr = 255;
- offset = 255 - total;
-
- memcpy(ptr, vp->vp_octets, offset);
- ptr += offset;
- *(ptr++) = PW_EXTENDED_ATTRIBUTE;
- length_ptr = ptr++;
- *length_ptr = 2;
- length -= offset;
-
- total += offset + 2;
- }
-
- memcpy(ptr, vp->vp_octets + offset, length);
- }
- break;
- }
-
- /*
- * Skip to the end of the data.
- */
- ptr += length;
- total += length;
- *length_ptr += length;
-
- /*
- * Align the data to a multiple of 4 bytes.
- */
- if (align != 0) {
- unsigned int i;
-
- *length_ptr += align;
- for (i = 0; i < align; i++) {
- *(ptr++) = '\0';
- total++;
- }
- }
-
- return total;
-}
-
-
-/*
- * Parse a data structure into a RADIUS attribute.
+ * Returns the end of the data.
*/
-int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR *vp, uint8_t *ptr)
+static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR *vp, uint8_t *start,
+ size_t room)
{
- int vendorcode;
- int offset, len, total_length;
- uint32_t lvalue;
- uint8_t *length_ptr, *vsa_length_ptr;
- const uint8_t *data = NULL;
- uint8_t array[4];
-
- vendorcode = total_length = 0;
- length_ptr = vsa_length_ptr = NULL;
-
- /*
- * For interoperability, always put vendor attributes
- * into their own VSA.
- */
- if ((vendorcode = VENDOR(vp->attribute)) == 0) {
- *(ptr++) = vp->attribute & 0xFF;
- length_ptr = ptr;
- *(ptr++) = 2;
- total_length += 2;
+ uint32_t lvalue;
+ size_t len;
+ const uint8_t *data;
+ uint8_t *ptr = start;
+ uint8_t array[4];
- } else {
- int vsa_tlen = 1;
- int vsa_llen = 1;
- DICT_VENDOR *dv = dict_vendorbyvalue(vendorcode);
-
- /*
- * 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;
- }
-
- /*
- * 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;
-
- case 2:
- ptr[0] = ((vp->attribute >> 8) & 0xFF);
- ptr[1] = (vp->attribute & 0xFF);
- break;
-
- case 4:
- ptr[0] = 0;
- ptr[1] = 0;
- ptr[2] = ((vp->attribute >> 8) & 0xFF);
- ptr[3] = (vp->attribute & 0xFF);
- break;
-
- default:
- return 0; /* silently discard it */
- }
- ptr += vsa_tlen;
-
- 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;
-
- default:
- return 0; /* silently discard it */
- }
- ptr += vsa_llen;
-
- total_length += vsa_tlen + vsa_llen;
- if (vsa_length_ptr) *vsa_length_ptr += vsa_tlen + vsa_llen;
- *length_ptr += vsa_tlen + vsa_llen;
- }
-
- offset = 0;
- if (vp->flags.has_tag) {
- if (TAG_VALID(vp->flags.tag)) {
- ptr[0] = vp->flags.tag & 0xff;
- offset = 1;
-
- } 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 */
-
/*
* Set up the default sources for the data.
*/
case PW_TYPE_ABINARY:
/* nothing more to do */
break;
-
+
case PW_TYPE_BYTE:
len = 1; /* just in case */
- array[0] = vp->lvalue & 0xff;
+ array[0] = vp->vp_integer & 0xff;
data = array;
- offset = 0;
break;
-
case PW_TYPE_SHORT:
len = 2; /* just in case */
- array[0] = (vp->lvalue >> 8) & 0xff;
- array[1] = vp->lvalue & 0xff;
+ 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->lvalue);
+ lvalue = htonl(vp->vp_integer);
memcpy(array, &lvalue, sizeof(lvalue));
-
- /*
- * Perhaps discard the first octet.
- */
- data = &array[offset];
- len -= offset;
+ data = array;
break;
-
+
case PW_TYPE_IPADDR:
- data = (const uint8_t *) &vp->lvalue;
+ data = (const uint8_t *) &vp->vp_ipaddr;
len = 4; /* just in case */
break;
* There are no tagged date attributes.
*/
case PW_TYPE_DATE:
- lvalue = htonl(vp->lvalue);
+ lvalue = htonl(vp->vp_date);
data = (const uint8_t *) &lvalue;
len = 4; /* just in case */
break;
- default: /* unknown type: ignore it */
- librad_log("ERROR: Unknown attribute type %d", vp->type);
- return -1;
- }
+ case PW_TYPE_SIGNED:
+ {
+ int32_t slvalue;
- /*
- * Bound the data to 255 bytes.
+ len = 4; /* just in case */
+ slvalue = htonl(vp->vp_signed);
+ memcpy(array, &slvalue, sizeof(slvalue));
+ break;
+ }
+
+ case PW_TYPE_TLV:
+ data = vp->vp_tlv;
+ if (!data) {
+ fr_strerror_printf("ERROR: Cannot encode NULL TLV");
+ return -1;
+ }
+ if (vp->length > room) return 0; /* can't chop TLVs to fit */
+ break;
+
+ default: /* unknown type: ignore it */
+ fr_strerror_printf("ERROR: Unknown attribute type %d", vp->type);
+ return -1;
+ }
+
+ /*
+ * Bound the data to the calling size
*/
- if (len + offset + total_length > 255) {
- len = 255 - offset - total_length;
- }
+ if (len > room) len = room;
/*
* Encrypt the various password styles
*/
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:
- if (!original) {
- librad_log("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name);
- return -1;
- }
+ 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 ((255 - offset - total_length) < 18) return 0;
+ if (room < (18 + lvalue)) return 0;
+
+ switch (packet->code) {
+ case PW_AUTHENTICATION_ACK:
+ case PW_AUTHENTICATION_REJECT:
+ case PW_ACCESS_CHALLENGE:
+ default:
+ if (!original) {
+ 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, 255 - offset - total_length,
- secret, original->vector);
+ 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:
+ ptr[0] = vp->flags.tag;
+ make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
+ secret, packet->vector);
+ break;
+ }
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 > (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 + (ptr - start);;
+}
+
+
+static int rad_vp2rfc(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR *vp,
+ unsigned int attribute, uint8_t *ptr, size_t room)
+{
+ int len;
+
+ if (room < 2) return 0;
+
+ ptr[0] = attribute & 0xff; /* NOT vp->attribute */
+ ptr[1] = 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,
+ uint8_t *start, size_t room, uint8_t *ptr)
+{
+ int len;
+
+ /*
+ * Offsets to Vendor-Specific length, and to length of
+ * WiMAX attribute.
+ */
+#define VS_OFF (1)
+#define WM_OFF (7)
+
+ if (room < 1) return 0;
+ room--;
+
+ /*
+ * Account for continuation bytes. The caller has
+ * already accounting for the continuation byte in the
+ * Vendor-Specific "length" field.
+ */
+ start[WM_OFF]++;
+ *(ptr++) = 0;
+
+ /*
+ * Chop everything to fit in one attribute.
+ */
+ if (room > (255 - 9)) room = (255 - 9);
+
+ /*
+ * The attribute contains TLVs that we have NOT decoded
+ * properly, OR it contains TLV that the user has encoded
+ * manually. If it has no data, OR it's too long,
+ * discard it. We're not going to walk through its
+ * contents trying to figure out how to chop it across
+ * multiple continuations.
+ */
+ if (vp->flags.has_tlv && (!vp->vp_tlv || (vp->length > room))) {
+ return 0;
+ }
+
+ /*
+ * The attribute is a top-level integer, ipaddr, etc.
+ * Encode it.
+ */
+ if (!vp->flags.is_tlv) {
+ len = vp2data(packet, original, secret, vp, ptr, room);
+ } else {
+ len = tlv2data(packet, original, secret, vp, ptr, room, 1);
+ }
+
+ if (len <= 0) return len;
+
+ start[VS_OFF] += len;
+ start[WM_OFF] += len;
+
+ return start[VS_OFF];
+}
+
+
+static int rad_vp2extended(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR *vp,
+ unsigned int attribute, uint8_t *ptr, size_t room)
+{
+ int len = 2;
+
+ if (room < 3) return 0;
+
+ ptr[0] = attribute & 0xff; /* NOT vp->attribute */
+ ptr[1] = 3;
+
+ if (vp->flags.extended) {
+ ptr[2] = (attribute & 0xff00) >> 8;
+ len++;
+
+ } else if (vp->flags.extended_flags) {
+ if (room < 4) return 0;
+
+ ptr[1] = 4;
+ ptr[2] = (attribute & 0xff00) >> 8;
+ ptr[3] = 0;
+
+ len += 2;
+ }
+
+ /*
+ * For now, no extended attribute can be longer than the
+ * encapsulating attribute. Once we add support for the
+ * "M" bit, this restriction will be relaxed.
+ */
+ if (room > (255 - ptr[1])) room = 255 - ptr[1];
+
+ if (!vp->flags.is_tlv) {
+ len = vp2data(packet, original, secret, vp, ptr + ptr[1], room);
+ } else {
+ len = tlv2data(packet, original, secret, vp, ptr + ptr[1], room, 2);
+ }
+
+ if (len < 0) return len;
+
+ ptr[1] += len;
+
+ return ptr[1];
+}
+
+/*
+ * Parse a data structure into a RADIUS attribute.
+ */
+int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR *vp, uint8_t *start,
+ size_t room)
+{
+ int len;
+ uint32_t lvalue;
+ uint8_t *ptr;
+ DICT_VENDOR *dv;
+
+ /*
+ * RFC format attributes take the fast path.
+ */
+ if (vp->vendor == 0) {
+ return rad_vp2rfc(packet, original, secret, vp,
+ vp->attribute, start, room);
+ }
+
+ if (vp->vendor == VENDORPEC_EXTENDED) {
+ return rad_vp2extended(packet, original, secret, vp,
+ vp->attribute, start, room);
+ }
+
+ /*
+ * Not enough room for:
+ * attr, len, vendor-id, vsa, vsalen
+ */
+ if (room < 8) return 0;
+
+ /*
+ * Build the Vendor-Specific header
+ */
+ ptr = start;
+ *ptr++ = PW_VENDOR_SPECIFIC;
+ *ptr++ = 6;
+ room -= 6;
+ lvalue = htonl(vp->vendor);
+ memcpy(ptr, &lvalue, 4);
+ ptr += 4;
+
+ /*
+ * Unknown vendors, and type=1,length=1,no-continuation
+ * are RFC format attributes.
+ */
+ dv = dict_vendorbyvalue(vp->vendor);
+ if (!dv ||
+ ((dv->type == 1) && (dv->length = 1) && !dv->flags)) {
+ len = rad_vp2rfc(packet, original, secret, vp,
+ vp->attribute, ptr, room);
+ if (len <= 0) return len;
+
+ start[1] += len;
+ return start[1];
+ }
+
+ if (room < (dv->type + dv->length + dv->flags)) return 0;
+ room -= (dv->type + dv->length + dv->flags);
+ start[1] += (dv->type + dv->length + dv->flags);
+
+ switch (dv->type) {
+ case 1:
+ ptr[0] = (vp->attribute & 0xFF);
+ break;
+
+ case 2:
+ ptr[0] = ((vp->attribute >> 8) & 0xFF);
+ ptr[1] = (vp->attribute & 0xFF);
+ break;
+
+ case 4:
+ ptr[0] = 0;
+ ptr[1] = ((vp->attribute >> 16) & 0xFF);
+ ptr[2] = ((vp->attribute >> 8) & 0xFF);
+ ptr[3] = (vp->attribute & 0xFF);
+ break;
+
+ default:
+ return 0; /* silently discard it */
+ }
+ ptr += dv->type;
+
+ switch (dv->length) {
+ case 0:
+ break;
+ case 1:
+ ptr[0] = dv->type + 1;
+ break;
+ case 2:
+ ptr[0] = 0;
+ ptr[1] = dv->type + 2;
+ break;
+
+ default:
+ return 0; /* silently discard it */
+ }
+ ptr += dv->length;
+
+ /*
+ * WiMAX attributes take their own path through the
+ * system.
+ */
+ if (dv->flags) return wimax2data(packet, original, secret, vp,
+ start, room, ptr);
+
+ len = vp2data(packet, original, secret, vp, ptr, room);
+ if (len <= 0) return len;
+
+ if (dv->length != 0) ptr[-1] += len;
+
+ start[1] += len;
+
+ return start[1];
+}
+
+/*
+ * Swap 123a -> 0321
+ */
+#define REORDER(x) ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000 >> 24))
+
+
+/*
+ * Encode a WiMAX sub-TLV. It must NOT be called for WiMAX
+ * attributes that are of type integer, string, etc.
+ */
+static int rad_encode_wimax(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, VALUE_PAIR *reply,
+ uint8_t *start, size_t room)
+{
+ int len, redo;
+ uint32_t lvalue;
+ uint8_t *ptr = start, *vsa = start;
+ uint32_t maxattr;
+ VALUE_PAIR *vp = reply;
+
/*
- * Account for the tag (if any).
+ * Swap the order of the WiMAX hacks, to make later
+ * comparisons easier.
*/
- len += offset;
+ maxattr = REORDER(vp->attribute);
/*
- * RFC 2865 section 5 says that zero-length attributes
- * MUST NOT be sent.
+ * Build the Vendor-Specific header
*/
- if (len == 0) return 0;
+ ptr = start;
+ redo = 0;
+
+redo_vsa:
+ vsa = ptr;
+
+ if (room < 9) return 0;
+ *ptr++ = PW_VENDOR_SPECIFIC;
+ *ptr++ = 9;
+ room -= 9;
+ lvalue = htonl(vp->vendor);
+ memcpy(ptr, &lvalue, 4);
+ ptr += 4;
+ *(ptr++) = vp->attribute & 0xff;
+ *(ptr++) = 3;
+ *(ptr++) = 0; /* continuation */
+ room -= 9;
+
+redo_tlv:
+ len = tlv2data(packet, original, secret, vp, ptr, room, 1);
+ if (len < 0) return len;
/*
- * Update the various lengths.
+ * Not enough room. Do a continuation.
*/
- *length_ptr += len;
- if (vsa_length_ptr) *vsa_length_ptr += len;
+ if ((len == 0) || ((vsa[VS_OFF] + len) > 255)) {
+ if (redo) return (start - vsa);
+
+ vsa[8] = 0x80;
+ redo = 1;
+ goto redo_vsa;
+ }
+ redo = 0;
+
ptr += len;
- total_length += len;
+ vsa[VS_OFF] += len;
+ vsa[WM_OFF] += len;
+
+ vp->flags.encoded = 1;
+ vp = vp->next;
- return total_length; /* of attribute */
+ /*
+ * Look at the NEXT tlv. Ensure that we encode
+ * attributes into a common VSA *only* if they are for
+ * the same WiMAX VSA, AND if the TLVs are in numerically
+ * increasing order.
+ */
+ if (vp && vp->flags.is_tlv && (reply->vendor == vp->vendor) &&
+ ((reply->attribute & 0xff) == (vp->attribute & 0xff))) {
+ uint32_t attr;
+
+ attr = REORDER(vp->attribute);
+ if (attr >= maxattr) {
+ maxattr = attr;
+ goto redo_tlv;
+ }
+ }
+
+ return ptr - start;
}
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 < MAX_PACKET_CODE)) {
- what = packet_codes[packet->code];
+ if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+ what = fr_packet_codes[packet->code];
} else {
what = "Reply";
}
case PW_AUTHENTICATION_REJECT:
case PW_ACCESS_CHALLENGE:
if (!original) {
- librad_log("ERROR: Cannot sign response packet without a request packet.");
+ fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
return -1;
}
break;
-
+
/*
* These packet vectors start off as all zero.
*/
case PW_COA_REQUEST:
memset(packet->vector, 0, sizeof(packet->vector));
break;
-
+
default:
break;
}
-
+
/*
* Use memory on the stack, until we know how
* large the packet will be.
*/
hdr = (radius_packet_t *) data;
-
+
/*
* Build standard header
*/
hdr->code = packet->code;
hdr->id = packet->id;
-
+
memcpy(hdr->vector, packet->vector, sizeof(hdr->vector));
total_length = AUTH_HDR_LEN;
- packet->verified = 0;
-
+
/*
* Load up the configuration values for the user
*/
ptr = hdr->data;
+ packet->offset = 0;
/*
* FIXME: Loop twice over the reply list. The first time,
* Hmm... this may be slower than just doing a small
* memcpy.
*/
-
+
/*
* Loop over the reply attributes for the packet.
*/
for (reply = packet->vps; reply; reply = reply->next) {
/*
- * Ignore non-wire attributes
+ * Ignore non-wire attributes, but allow extended
+ * attributes.
*/
- if ((VENDOR(reply->attribute) == 0) &&
- ((reply->attribute & 0xFFFF) > 0xff) &&
- !reply->flags.diameter) {
+ if ((reply->vendor == 0) &&
+ ((reply->attribute & 0xFFFF) >= 256) &&
+ !reply->flags.extended && !reply->flags.extended_flags) {
+#ifndef NDEBUG
+ /*
+ * Permit the admin to send BADLY formatted
+ * attributes with a debug build.
+ */
+ if (reply->attribute == PW_RAW_ATTRIBUTE) {
+ memcpy(ptr, reply->vp_octets, reply->length);
+ len = reply->length;
+ goto next;
+ }
+#endif
continue;
}
-
- /*
- * Check that the packet is no more than 4k in
- * size, AFTER over-flowing the 4k boundary.
- * Note that the 'data' buffer, above, is one
- * attribute longer than necessary, in order to
- * permit this overflow.
- */
- if (total_length > MAX_PACKET_LEN) {
- librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
- return -1;
- }
-
+
/*
* Set the Message-Authenticator to the correct
* length and initial value.
if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
reply->length = AUTH_VECTOR_LEN;
memset(reply->vp_strvalue, 0, AUTH_VECTOR_LEN);
- packet->verified = total_length; /* HACK! */
+
+ /*
+ * Cache the offset to the
+ * Message-Authenticator
+ */
+ packet->offset = total_length;
}
/*
- * Check for overflow.
+ * Print out ONLY the attributes which
+ * we're sending over the wire, and print
+ * them out BEFORE they're encrypted.
*/
- if (reply->flags.diameter) {
- len = reply->length;
-
- if (len >= 4096) {
- librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
- return -1;
- }
+ debug_pair(reply);
- len += 8; /* Code, length */
- if (VENDOR(reply->attribute) != 0) len += 4;
- if ((len & 0x03) != 0) len += 4 - (len & 0x03);
+ /*
+ * Skip attributes that are encoded.
+ */
+ if (reply->flags.encoded) continue;
- if (len > 253) len += (len / 253) * 2;
+ if ((reply->vendor == VENDORPEC_WIMAX) && reply->flags.is_tlv) {
+ len = rad_encode_wimax(packet, original, secret,
+ reply, ptr,
+ ((uint8_t *) data) + sizeof(data) - ptr);
+ } else {
- if ((total_length + len) > MAX_PACKET_LEN) {
- librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
- return -1;
- }
+ len = rad_vp2attr(packet, original, secret, reply, ptr,
+ ((uint8_t *) data) + sizeof(data) - ptr);
+ }
- debug_pair(reply);
+ if (len < 0) return -1;
- len = vp2diameter(packet, original, secret, reply, ptr);
- } else {
- /*
- * Print out ONLY the attributes which
- * we're sending over the wire, and print
- * them out BEFORE they're encrypted.
- */
- debug_pair(reply);
-
- len = rad_vp2attr(packet, original, secret, reply, ptr);
+ /*
+ * 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;
}
- if (len < 0) return -1;
+ next:
ptr += len;
total_length += len;
} /* done looping over all attributes */
-
+
/*
* Fill in the rest of the fields, and copy the data over
* from the local stack to the newly allocated memory.
packet->data_len = total_length;
packet->data = (uint8_t *) malloc(packet->data_len);
if (!packet->data) {
- librad_log("Out of memory");
+ fr_strerror_printf("Out of memory");
return -1;
}
- memcpy(packet->data, data, packet->data_len);
+ memcpy(packet->data, hdr, packet->data_len);
hdr = (radius_packet_t *) packet->data;
-
+
total_length = htons(total_length);
memcpy(hdr->length, &total_length, sizeof(total_length));
* It wasn't assigned an Id, this is bad!
*/
if (packet->id < 0) {
- librad_log("ERROR: RADIUS packets must be assigned an Id.");
+ fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id.");
return -1;
}
if (!packet->data || (packet->data_len < AUTH_HDR_LEN) ||
- (packet->verified < 0)) {
- librad_log("ERROR: You must call rad_encode() before rad_sign()");
+ (packet->offset < 0)) {
+ fr_strerror_printf("ERROR: You must call rad_encode() before rad_sign()");
return -1;
}
/*
* If there's a Message-Authenticator, update it
* now, BEFORE updating the authentication vector.
- *
- * This is a hack...
*/
- if (packet->verified > 0) {
+ if (packet->offset > 0) {
uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
-
+
switch (packet->code) {
case PW_ACCOUNTING_REQUEST:
case PW_ACCOUNTING_RESPONSE:
case PW_AUTHENTICATION_REJECT:
case PW_ACCESS_CHALLENGE:
if (!original) {
- librad_log("ERROR: Cannot sign response packet without a request packet.");
+ fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
return -1;
}
memcpy(hdr->vector, original->vector,
default: /* others have vector already set to zero */
break;
-
+
}
-
+
/*
* Set the authentication vector to zero,
* calculate the signature, and put it
* into the Message-Authenticator
* attribute.
*/
- lrad_hmac_md5(packet->data, packet->data_len,
- secret, strlen(secret),
- calc_auth_vector);
- memcpy(packet->data + packet->verified + 2,
+ fr_hmac_md5(packet->data, packet->data_len,
+ (const uint8_t *) secret, strlen(secret),
+ calc_auth_vector);
+ memcpy(packet->data + packet->offset + 2,
calc_auth_vector, AUTH_VECTOR_LEN);
-
+
/*
* Copy the original request vector back
* to the raw packet.
*/
memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
}
-
+
/*
* Switch over the packet code, deciding how to
* sign the packet.
case PW_AUTHENTICATION_REQUEST:
case PW_STATUS_SERVER:
break;
-
+
/*
* Reply packets are signed with the
* authentication vector of the request.
default:
{
uint8_t digest[16];
-
- MD5_CTX context;
- MD5Init(&context);
- MD5Update(&context, packet->data, packet->data_len);
- MD5Update(&context, secret, strlen(secret));
- MD5Final(digest, &context);
-
+
+ FR_MD5_CTX context;
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, packet->data, packet->data_len);
+ fr_MD5Update(&context, (const uint8_t *) secret,
+ strlen(secret));
+ fr_MD5Final(digest, &context);
+
memcpy(hdr->vector, digest, AUTH_VECTOR_LEN);
memcpy(packet->vector, digest, AUTH_VECTOR_LEN);
break;
return 0;
}
- if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) {
- what = packet_codes[packet->code];
+ if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+ what = fr_packet_codes[packet->code];
} else {
what = "Reply";
}
if (rad_encode(packet, original, secret) < 0) {
return -1;
}
-
+
/*
* Re-sign it, including updating the
* Message-Authenticator.
* If packet->data points to data, then we print out
* the VP list again only for debugging.
*/
- } else if (librad_debug) {
- DEBUG("Re-sending %s of id %d to %s port %d\n", what, packet->id,
+ } else if (fr_debug_flag) {
+ DEBUG("Sending %s of id %d to %s port %d\n", what, packet->id,
inet_ntop(packet->dst_ipaddr.af,
&packet->dst_ipaddr.ipaddr,
ip_buffer, sizeof(ip_buffer)),
packet->dst_port);
for (reply = packet->vps; reply; reply = reply->next) {
- /* FIXME: ignore attributes > 0xff */
+ if ((reply->vendor == 0) &&
+ ((reply->attribute & 0xFFFF) > 0xff)) continue;
debug_pair(reply);
}
}
* And send it on it's way.
*/
return rad_sendto(packet->sockfd, packet->data, packet->data_len, 0,
- &packet->src_ipaddr, &packet->dst_ipaddr,
- packet->dst_port);
+ &packet->src_ipaddr, packet->src_port,
+ &packet->dst_ipaddr, packet->dst_port);
}
-
/*
- * Validates the requesting client NAS. Calculates the
- * signature based on the clients private key.
+ * Do a comparison of two authentication digests by comparing
+ * the FULL digest. Otehrwise, 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
*/
-static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
+static int digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
{
- uint8_t digest[AUTH_VECTOR_LEN];
- MD5_CTX context;
+ int result = 0;
+ size_t i;
- /*
- * Older clients have the authentication vector set to
- * all zeros. Return `1' in that case.
- */
- memset(digest, 0, sizeof(digest));
- if (memcmp(packet->vector, digest, AUTH_VECTOR_LEN) == 0) {
- packet->verified = 1;
- return 1;
+ for (i = 0; i < length; i++) {
+ result |= a[i] ^ b[i];
}
+ return result; /* 0 is OK, !0 is !OK, just like memcmp */
+}
+
+
+/*
+ * Validates the requesting client NAS. Calculates the
+ * signature based on the clients private key.
+ */
+static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
+{
+ uint8_t digest[AUTH_VECTOR_LEN];
+ FR_MD5_CTX context;
+
/*
* Zero out the auth_vector in the received packet.
* Then append the shared secret to the received packet,
/*
* MD5(packet + secret);
*/
- MD5Init(&context);
- MD5Update(&context, packet->data, packet->data_len);
- MD5Update(&context, secret, strlen(secret));
- MD5Final(digest, &context);
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, packet->data, packet->data_len);
+ fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+ fr_MD5Final(digest, &context);
/*
* Return 0 if OK, 2 if not OK.
*/
- packet->verified =
- memcmp(digest, packet->vector, AUTH_VECTOR_LEN) ? 2 : 0;
-
- return packet->verified;
+ if (digest_cmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
+ return 0;
}
+
/*
* Validates the requesting client NAS. Calculates the
* signature based on the clients private key.
const char *secret)
{
uint8_t calc_digest[AUTH_VECTOR_LEN];
- MD5_CTX context;
+ FR_MD5_CTX context;
/*
* Very bad!
/*
* MD5(packet + secret);
*/
- MD5Init(&context);
- MD5Update(&context, packet->data, packet->data_len);
- MD5Update(&context, secret, strlen(secret));
- MD5Final(calc_digest, &context);
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, packet->data, packet->data_len);
+ fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+ fr_MD5Final(calc_digest, &context);
/*
* Copy the packet's vector back to the packet.
/*
* Return 0 if OK, 2 if not OK.
*/
- packet->verified =
- memcmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) ? 2 : 0;
- return packet->verified;
+ if (digest_cmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
+ return 0;
}
* 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.
*/
-int rad_packet_ok(RADIUS_PACKET *packet)
+int rad_packet_ok(RADIUS_PACKET *packet, int flags)
{
uint8_t *attr;
int totallen;
int count;
radius_packet_t *hdr;
char host_ipaddr[128];
- int seen_eap;
+ int require_ma = 0;
+ int seen_ma = 0;
int num_attributes;
/*
* "The minimum length is 20 ..."
*/
if (packet->data_len < AUTH_HDR_LEN) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)",
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;
}
* " ... and maximum length is 4096."
*/
if (packet->data_len > MAX_PACKET_LEN) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)",
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;
}
* Code of 16 or greate is not understood.
*/
if ((hdr->code == 0) ||
- (hdr->code >= MAX_PACKET_CODE)) {
- librad_log("WARNING: Bad RADIUS packet from host %s: unknown packet code %d",
+ (hdr->code >= FR_MAX_PACKET_CODE)) {
+ fr_strerror_printf("WARNING: Bad RADIUS packet from host %s: unknown packet code%d ",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
}
/*
+ * Message-Authenticator is required in Status-Server
+ * packets, otherwise they can be trivially forged.
+ */
+ if (hdr->code == PW_STATUS_SERVER) require_ma = 1;
+
+ /*
+ * It's also required if the caller asks for it.
+ */
+ if (flags) require_ma = 1;
+
+ /*
* Repeat the length checks. This time, instead of
* looking at the data we received, look at the value
* of the 'length' field inside of the packet.
* "The minimum length is 20 ..."
*/
if (totallen < AUTH_HDR_LEN) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* " ... and maximum length is 4096."
*/
if (totallen > MAX_PACKET_LEN) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* i.e. No response to the NAS.
*/
if (packet->data_len < totallen) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d",
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;
}
*/
attr = hdr->data;
count = totallen - AUTH_HDR_LEN;
- seen_eap = 0;
num_attributes = 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) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
* fields. Anything shorter is an invalid attribute.
*/
if (attr[1] < 2) {
- librad_log("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]) {
default: /* don't do anything by default */
break;
+ /*
+ * If there's an EAP-Message, we require
+ * a Message-Authenticator.
+ */
case PW_EAP_MESSAGE:
- seen_eap |= PW_EAP_MESSAGE;
+ require_ma = 1;
break;
case PW_MESSAGE_AUTHENTICATOR:
if (attr[1] != 2 + AUTH_VECTOR_LEN) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
attr[1] - 2);
return 0;
}
- seen_eap |= PW_MESSAGE_AUTHENTICATOR;
+ seen_ma = 1;
break;
}
* If not, we complain, and throw the packet away.
*/
if (count != 0) {
- librad_log("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
+ fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
* attributes, and we've seen more than that maximum,
* then throw the packet away, as a possible DoS.
*/
- if ((librad_max_attributes > 0) &&
- (num_attributes > librad_max_attributes)) {
- librad_log("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+ if ((fr_max_attributes > 0) &&
+ (num_attributes > fr_max_attributes)) {
+ 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,
host_ipaddr, sizeof(host_ipaddr)),
- num_attributes, librad_max_attributes);
+ num_attributes, fr_max_attributes);
return 0;
}
* a Message-Authenticator attribute.
*
* A Message-Authenticator all by itself is OK, though.
+ *
+ * Similarly, Status-Server packets MUST contain
+ * Message-Authenticator attributes.
*/
- if (seen_eap &&
- (seen_eap != PW_MESSAGE_AUTHENTICATOR) &&
- (seen_eap != (PW_EAP_MESSAGE | PW_MESSAGE_AUTHENTICATOR))) {
- librad_log("WARNING: Insecure packet from host %s: Received EAP-Message with no Message-Authenticator.",
+ if (require_ma && ! seen_ma) {
+ fr_strerror_printf("WARNING: Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
* Receive UDP client requests, and fill in
* the basics of a RADIUS_PACKET structure.
*/
-RADIUS_PACKET *rad_recv(int fd)
+RADIUS_PACKET *rad_recv(int fd, int flags)
{
+ int sock_flags = 0;
RADIUS_PACKET *packet;
/*
* Allocate the new request data structure
*/
if ((packet = malloc(sizeof(*packet))) == NULL) {
- librad_log("out of memory");
+ fr_strerror_printf("out of memory");
return NULL;
}
memset(packet, 0, sizeof(*packet));
- packet->data_len = rad_recvfrom(fd, &packet->data, 0,
+ if (flags & 0x02) {
+ sock_flags = MSG_PEEK;
+ flags &= ~0x02;
+ }
+
+ packet->data_len = rad_recvfrom(fd, &packet->data, sock_flags,
&packet->src_ipaddr, &packet->src_port,
&packet->dst_ipaddr, &packet->dst_port);
* Check for socket errors.
*/
if (packet->data_len < 0) {
- librad_log("Error receiving packet: %s", strerror(errno));
+ fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+ /* packet->data is NULL */
+ free(packet);
+ return NULL;
+ }
+
+ /*
+ * If the packet is too big, then rad_recvfrom did NOT
+ * allocate memory. Instead, it just discarded the
+ * packet.
+ */
+ if (packet->data_len > MAX_PACKET_LEN) {
+ fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
/* packet->data is NULL */
free(packet);
return NULL;
}
/*
+ * Read no data. Continue.
+ * This check is AFTER the MAX_PACKET_LEN check above, because
+ * if the packet is larger than MAX_PACKET_LEN, we also have
+ * packet->data == NULL
+ */
+ if ((packet->data_len == 0) || !packet->data) {
+ fr_strerror_printf("Empty packet: Socket is not ready.");
+ free(packet);
+ return NULL;
+ }
+
+ /*
* See if it's a well-formed RADIUS packet.
*/
- if (!rad_packet_ok(packet)) {
+ if (!rad_packet_ok(packet, flags)) {
rad_free(&packet);
return NULL;
}
*/
packet->vps = NULL;
- if (librad_debug) {
+ if (fr_debug_flag) {
char host_ipaddr[128];
- if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) {
- printf("rad_recv: %s packet from host %s port %d",
- packet_codes[packet->code],
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- host_ipaddr, sizeof(host_ipaddr)),
- packet->src_port);
+ if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+ DEBUG("rad_recv: %s packet from host %s port %d",
+ fr_packet_codes[packet->code],
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ host_ipaddr, sizeof(host_ipaddr)),
+ packet->src_port);
} else {
- printf("rad_recv: Packet from host %s port %d code=%d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- host_ipaddr, sizeof(host_ipaddr)),
- packet->src_port,
- packet->code);
+ DEBUG("rad_recv: Packet from host %s port %d code=%d",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ host_ipaddr, sizeof(host_ipaddr)),
+ packet->src_port,
+ packet->code);
}
- printf(", id=%d, length=%d\n", packet->id, packet->data_len);
+ DEBUG(", id=%d, length=%d\n",
+ packet->id, (int) packet->data_len);
}
return packet;
case PW_AUTHENTICATION_REJECT:
case PW_ACCESS_CHALLENGE:
if (!original) {
- librad_log("ERROR: Cannot validate Message-Authenticator in response packet without a request packet.");
+ fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet.");
return -1;
}
memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN);
break;
}
- lrad_hmac_md5(packet->data, packet->data_len,
- secret, strlen(secret), calc_auth_vector);
- if (memcmp(calc_auth_vector, msg_auth_vector,
+ fr_hmac_md5(packet->data, packet->data_len,
+ (const uint8_t *) secret, strlen(secret),
+ calc_auth_vector);
+ if (digest_cmp(calc_auth_vector, msg_auth_vector,
sizeof(calc_auth_vector)) != 0) {
char buffer[32];
- librad_log("Received packet from %s with invalid Message-Authenticator! (Shared secret is incorrect.)",
+ fr_strerror_printf("Received packet from %s with invalid Message-Authenticator! (Shared secret is incorrect.)",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
buffer, sizeof(buffer)));
* It looks like a RADIUS packet, but we can't validate
* the signature.
*/
- if ((packet->code == 0) || packet->code >= MAX_PACKET_CODE) {
+ if ((packet->code == 0) || (packet->code >= FR_MAX_PACKET_CODE)) {
char buffer[32];
- librad_log("Received Unknown packet code %d"
- "from client %s port %d: Cannot validate signature",
+ fr_strerror_printf("Received Unknown packet code %d "
+ "from client %s port %d: Cannot validate signature.",
packet->code,
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
case PW_AUTHENTICATION_REQUEST:
case PW_STATUS_SERVER:
- case PW_DISCONNECT_REQUEST:
/*
* The authentication vector is random
* nonsense, invented by the client.
*/
break;
+ case PW_COA_REQUEST:
+ case PW_DISCONNECT_REQUEST:
case PW_ACCOUNTING_REQUEST:
if (calc_acctdigest(packet, secret) > 1) {
- librad_log("Received Accounting-Request packet "
- "from %s with invalid signature! (Shared secret is incorrect.)",
+ fr_strerror_printf("Received %s packet "
+ "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,
buffer, sizeof(buffer)));
case PW_COA_NAK:
rcode = calc_replydigest(packet, original, secret);
if (rcode > 1) {
- librad_log("Received %s packet "
- "from client %s port %d with invalid signature (err=%d)! (Shared secret is incorrect.)",
- packet_codes[packet->code],
+ fr_strerror_printf("Received %s packet "
+ "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;
default:
- librad_log("Received Unknown packet code %d"
+ fr_strerror_printf("Received Unknown packet code %d "
"from client %s port %d: Cannot validate signature",
packet->code,
inet_ntop(packet->src_ipaddr.af,
}
-/*
- * Hack for IETF RADEXT. Don't use in a production environment.
- *
- * Note that due to architecture limitations, we can't handle
- * AVP's with more than 253 bytes of data, but that should be
- * enough for initial inter-operability.
- */
-static int diameter2vp(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original, const char *secret,
- const uint8_t *data, int total_length,
- VALUE_PAIR **pvp)
+static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, size_t length,
+ const uint8_t *data, VALUE_PAIR *vp)
{
- int lookup, offset, radius_length, attr_length, diameter_length, raw;
- uint32_t attr, length, vendor;
- DICT_ATTR *da;
- VALUE_PAIR *head, **tail, *vp;
- uint8_t *header; /* diameter header */
- uint8_t diameter[4096];
-
- diameter_length = radius_length = 0;
- header = diameter;
-
- *pvp = NULL;
+ int offset = 0;
/*
- * Unpack all contiguous Extended-Attributes into a local
- * buffer. It's slow, but it's safe.
+ * If length is greater than 253, something is SERIOUSLY
+ * wrong.
*/
- while (total_length > 0) {
- if (data[0] != PW_EXTENDED_ATTRIBUTE) break;
-
- attr_length = data[1];
- radius_length += attr_length;
- total_length -= attr_length;
- attr_length -= 2;
-
- memcpy(header, data + 2, attr_length);
- header += attr_length;
- diameter_length += attr_length;
-
- data += attr_length + 2;
- }
-
- head = NULL;
- tail = NULL;
- header = diameter;
-
-next_diameter:
- raw = 0;
+ if (length > 253) length = 253; /* paranoia (pair-anoia?) */
- if ((vp = paircreate(PW_EXTENDED_ATTRIBUTE, PW_TYPE_OCTETS)) == NULL) {
- pairfree(&head);
- return radius_length;
- }
+ vp->length = length;
+ vp->operator = T_OP_EQ;
+ vp->next = NULL;
/*
- * Don't add it to the tail until we know it's OK.
+ * 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;
+ }
/*
- * Too little data to contain a diameter header.
+ * Handle tags.
*/
- if (diameter_length < 12) {
- done:
- vp->type = PW_TYPE_OCTETS;
- memcpy(vp->vp_octets, header, diameter_length);
- vp->length = diameter_length;
-
- /*
- * Ensure there's no encryption or tag stuff,
- * we just pass the attribute as-is.
- */
- memset(&vp->flags, 0, sizeof(vp->flags));
-
+ if (vp->flags.has_tag) {
+ if (TAG_VALID(data[0]) ||
+ (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
+ /*
+ * Tunnel passwords REQUIRE a tag, even
+ * if don't have a valid tag.
+ */
+ vp->flags.tag = data[0];
- if (!head) { /* add vp to the list */
- *pvp = vp;
- } else {
- *pvp = head;
- *tail = vp;
+ if ((vp->type == PW_TYPE_STRING) ||
+ (vp->type == PW_TYPE_OCTETS)) {
+ if (length == 0) goto raw;
+ offset = 1;
+ }
}
-
- return radius_length;
}
/*
- * Sanity check the lengths, next. If the length is too
- * short, then the rest of the diameter buffer can't be
- * parsed.
+ * Copy the data to be decrypted
*/
- length = (header[5] << 16) | (header[6] << 8) | header[7];
+ memcpy(&vp->vp_octets[0], data + offset, length - offset);
+ vp->length -= offset;
/*
- * lies about the length (too short)
- * or lies about the length (too long)
- * or VSA
- * with too little diameter data
- * or with too little content
+ * Decrypt the attribute.
*/
- if ((length < 9) ||
- (length > diameter_length) ||
- (((header[4] & 0x80) != 0) &&
- ((diameter_length < 16) || (length < 13)))) {
+ switch (vp->flags.encrypt) {
/*
- * The rest of the data is small enough to fit
- * into one VP. Copy it over, and return it.
+ * User-Password
*/
- if (diameter_length <= 253) goto done;
+ case FLAG_ENCRYPT_USER_PASSWORD:
+ if (original) {
+ rad_pwdecode((char *)vp->vp_strvalue,
+ vp->length, secret,
+ original->vector);
+ } else {
+ rad_pwdecode((char *)vp->vp_strvalue,
+ vp->length, secret,
+ packet->vector);
+ }
+ if (vp->attribute == PW_USER_PASSWORD) {
+ vp->length = strlen(vp->vp_strvalue);
+ }
+ break;
/*
- * FIXME: make multiple VP's of the rest of the
- * data, so that we don't lose anything!
+ * Tunnel-Password's may go ONLY
+ * in response packets.
*/
- pairfree(&vp);
-
- *pvp = head;
- return radius_length;
- }
+ case FLAG_ENCRYPT_TUNNEL_PASSWORD:
+ if (!original) goto raw;
- memcpy(&attr, header, 4);
- attr = ntohl(attr);
- if (attr > 65535) raw = 1; /* implementation limitations */
+ if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
+ secret, original->vector) < 0) {
+ goto raw;
+ }
+ break;
- /*
- * 1..255 are RADIUS, and shouldn't be encapsulated this way.
- * 256.. are Diameter.
- *
- * We've arbitrarily assigned 32768..65535 from the
- * Diameter space to "extended RADIUS" attributes.
- */
- if (attr < 32768) raw = 1;
+ /*
+ * Ascend-Send-Secret
+ * Ascend-Receive-Secret
+ */
+ case FLAG_ENCRYPT_ASCEND_SECRET:
+ if (!original) {
+ goto raw;
+ } else {
+ uint8_t my_digest[AUTH_VECTOR_LEN];
+ make_secret(my_digest,
+ original->vector,
+ secret, data);
+ memcpy(vp->vp_strvalue, my_digest,
+ AUTH_VECTOR_LEN );
+ vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
+ vp->length = strlen(vp->vp_strvalue);
+ }
+ break;
- /*
- * We don't like any non-vendor flag bits being set.
- */
- if ((header[4] & 0x7f) != 0) raw = 1;
+ default:
+ break;
+ } /* switch over encryption flags */
- vendor = 0;
- if ((header[4] & 0x80) != 0) {
- memcpy(&vendor, header + 8 , 4);
- vendor = ntohl(vendor);
- if (vendor > 32767) raw = 1; /* implementation limitations */
- offset = 12;
- } else {
- offset = 8;
- }
- length -= offset;
- header += offset;
- diameter_length -= offset;
+ switch (vp->type) {
+ case PW_TYPE_STRING:
+ case PW_TYPE_OCTETS:
+ case PW_TYPE_ABINARY:
+ /* nothing more to do */
+ break;
- /*
- * FIXME: This is an implementation limitation. We
- * should really allow strings longer than 253 bytes...
- *
- * And bailing out completely (i.e. throwing away the rest
- * of the data) isn't an intelligent thing to do, either.
- */
- if (length > 253) {
- *pvp = head;
- pairfree(&vp);
- return radius_length;
- }
+ case PW_TYPE_BYTE:
+ if (vp->length != 1) goto raw;
- lookup = attr;
- lookup |= (vendor << 16);
- lookup |= (1 << 31); /* see dict_addattr */
+ vp->vp_integer = vp->vp_octets[0];
+ break;
- da = dict_attrbyvalue(lookup);
- if (!da) raw = 1;
- if (!raw) {
- /*
- * Copied from paircreate.
- */
- strcpy(vp->name, da->name);
- vp->type = da->type;
- vp->flags = da->flags;
- vp->attribute = da->attr;
- } else {
- vp->type = PW_TYPE_OCTETS;
- }
+ case PW_TYPE_SHORT:
+ if (vp->length != 2) goto raw;
- switch (vp->type) {
- case PW_TYPE_STRING:
- case PW_TYPE_OCTETS:
- case PW_TYPE_ABINARY:
- memcpy(vp->vp_octets, header, length);
- vp->length = length;
+ vp->vp_integer = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
break;
case PW_TYPE_INTEGER:
- if (length != 4) goto force_octets;
+ if (vp->length != 4) goto raw;
+
+ memcpy(&vp->vp_integer, vp->vp_octets, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
- memcpy(&vp->lvalue, header, 4);
- vp->lvalue = ntohl(vp->lvalue);
- vp->length = 4;
+ if (vp->flags.has_tag) vp->vp_integer &= 0x00ffffff;
/*
* Try to get named VALUEs
*/
{
DICT_VALUE *dval;
- dval = dict_valbyattr(vp->attribute,
- vp->lvalue);
+ dval = dict_valbyattr(vp->attribute, vp->vendor,
+ vp->vp_integer);
if (dval) {
- strNcpy(vp->vp_strvalue,
+ strlcpy(vp->vp_strvalue,
dval->name,
sizeof(vp->vp_strvalue));
}
break;
case PW_TYPE_DATE:
- if (length != 4) goto force_octets;
+ if (vp->length != 4) goto raw;
- memcpy(&vp->lvalue, header, 4);
- vp->lvalue = ntohl(vp->lvalue);
- vp->length = 4;
+ memcpy(&vp->vp_date, vp->vp_octets, 4);
+ vp->vp_date = ntohl(vp->vp_date);
break;
case PW_TYPE_IPADDR:
- if (length != 4) goto force_octets;
+ if (vp->length != 4) goto raw;
- memcpy(&vp->lvalue, header, 4);
- vp->length = 4;
+ memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
break;
/*
* IPv6 interface ID is 8 octets long.
*/
case PW_TYPE_IFID:
- if (length != 8) goto force_octets;
- memcpy(vp->vp_ifid, header, 8);
- vp->length = 8;
+ if (vp->length != 8) goto raw;
+ /* vp->vp_ifid == vp->vp_octets */
break;
-
+
/*
* IPv6 addresses are 16 octets long
*/
case PW_TYPE_IPV6ADDR:
- if (length != 16) goto force_octets;
- memcpy(&vp->vp_ipv6addr, header, 16);
- vp->length = 16;
+ if (vp->length != 16) goto raw;
+ /* vp->vp_ipv6addr == vp->vp_octets */
break;
-
+
/*
* IPv6 prefixes are 2 to 18 octets long.
*
* The prefix length can have value 0 to 128.
*/
case PW_TYPE_IPV6PREFIX:
- if (length < 2 || length > 18) goto force_octets;
- if (header[1] > 128) goto force_octets;
-
- memcpy(vp->vp_ipv6prefix, header, length);
- vp->length = length;
-
+ if (vp->length < 2 || vp->length > 18) goto raw;
+ if (vp->vp_octets[1] > 128) goto raw;
/*
* FIXME: double-check that
}
break;
- default:
- force_octets:
- vp->type = PW_TYPE_OCTETS;
-
+ case PW_TYPE_SIGNED:
+ if (vp->length != 4) goto raw;
+
/*
- * Ensure there's no encryption or tag stuff,
- * we just pass the attribute as-is.
+ * Overload vp_integer for ntohl, which takes
+ * uint32_t, not int32_t
*/
- memset(&vp->flags, 0, sizeof(vp->flags));
+ memcpy(&vp->vp_integer, vp->vp_octets, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
+ memcpy(&vp->vp_signed, &vp->vp_integer, 4);
break;
- }
- if (!head) {
- head = vp;
- } else {
- *tail = vp;
- }
- tail = &vp->next;
+ 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;
- header += length;
- diameter_length -= length;
+ case PW_TYPE_COMBO_IP:
+ if (vp->length == 4) {
+ vp->type = PW_TYPE_IPADDR;
+ memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+ break;
- if ((length & 0x03) != 0) {
- attr_length = 4 - (length & 0x03); /* padding */
+ } else if (vp->length == 16) {
+ vp->type = PW_TYPE_IPV6ADDR;
+ /* vp->vp_ipv6addr == vp->vp_octets */
+ break;
- if (diameter_length < attr_length) {
- *pvp = head;
- return radius_length;
}
+ /* FALL-THROUGH */
+
+ default:
+ raw:
+ /*
+ * Change the name to show the user that the
+ * attribute is not of the correct format.
+ */
+ {
+ int attr = vp->attribute;
+ int vendor = vp->vendor;
+ VALUE_PAIR *vp2;
+
+ vp2 = pairalloc(NULL);
+ if (!vp2) {
+ pairfree(&vp);
+ return NULL;
+ }
+ pairfree(&vp);
+ vp = vp2;
- header += attr_length;
- diameter_length -= attr_length;
+ /*
+ * 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;
}
- if (diameter_length > 0) goto next_diameter;
- *pvp = head;
- return radius_length;
+ return vp;
}
+static void rad_sortvp(VALUE_PAIR **head)
+{
+ int swapped;
+ VALUE_PAIR *vp, **tail;
+
+ /*
+ * Walk over the VP's, sorting them in order. Did I
+ * mention that I hate WiMAX continuations?
+ *
+ * And bubble sort! WTF is up with that?
+ */
+ do {
+ swapped = 0;
+ tail = head;
+ while (*tail) {
+ vp = *tail;
+ if (!vp->next) break;
+
+ if (vp->attribute > vp->next->attribute) {
+ *tail = vp->next;
+ vp->next = (*tail)->next;
+ (*tail)->next = vp;
+ swapped = 1;
+ }
+ tail = &(vp->next);
+ }
+ } while (swapped);
+}
/*
- * Parse a RADIUS attribute into a data structure.
+ * 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.
*/
-VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, int attribute, int length,
- const uint8_t *data)
+static uint8_t *rad_coalesce(unsigned int attribute, int vendor,
+ size_t length, uint8_t *data,
+ size_t packet_length, size_t *ptlv_length)
+
{
- int offset = 0;
- VALUE_PAIR *vp;
+ uint32_t lvalue;
+ size_t tlv_length = length;
+ uint8_t *ptr, *tlv, *tlv_data;
+
+ for (ptr = data + length;
+ ptr != (data + packet_length);
+ ptr += ptr[1]) {
+ /* FIXME: Check that there are 6 bytes of data here... */
+ if ((ptr[0] != PW_VENDOR_SPECIFIC) ||
+ (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */
+ (ptr[2] != 0) || (ptr[3] != 0) || /* our requirement */
+ (ptr[4] != ((vendor >> 8) & 0xff)) ||
+ (ptr[5] != (vendor & 0xff))) {
+ continue;
+ }
- if ((vp = paircreate(attribute, PW_TYPE_OCTETS)) == NULL) {
- return NULL;
+ memcpy(&lvalue, ptr + 2, 4); /* Vendor Id */
+ lvalue = ntohl(lvalue);
+ lvalue <<= 16;
+ lvalue |= ptr[2 + 4]; /* add in VSA number */
+ if (lvalue != attribute) continue;
+
+ /*
+ * If the vendor-length is too small, it's badly
+ * formed, so we stop.
+ */
+ if ((ptr[2 + 4 + 1]) < 3) break;
+
+ tlv_length += ptr[2 + 4 + 1] - 3;
+ if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
}
-
- /*
- * If length is greater than 253, something is SERIOUSLY
- * wrong.
- */
- if (length > 253) length = 253; /* paranoia (pair-anoia?) */
- vp->length = length;
- vp->operator = T_OP_EQ;
- vp->next = NULL;
+ tlv = tlv_data = malloc(tlv_length);
+ if (!tlv_data) return NULL;
+
+ memcpy(tlv, data, length);
+ tlv += length;
/*
- * Handle tags.
+ * Now we walk the list again, copying the data over to
+ * our newly created memory.
*/
- if (vp->flags.has_tag) {
- if (TAG_VALID(data[0]) ||
- (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
- /*
- * Tunnel passwords REQUIRE a tag, even
- * if don't have a valid tag.
- */
- vp->flags.tag = data[0];
+ for (ptr = data + length;
+ ptr != (data + packet_length);
+ ptr += ptr[1]) {
+ int this_length;
- if ((vp->type == PW_TYPE_STRING) ||
- (vp->type == PW_TYPE_OCTETS)) offset = 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;
}
+
+ memcpy(&lvalue, ptr + 2, 4);
+ lvalue = ntohl(lvalue);
+ lvalue <<= 16;
+ lvalue |= ptr[2 + 4];
+ if (lvalue != attribute) continue;
+
+ /*
+ * If the vendor-length is too small, it's badly
+ * formed, so we stop.
+ */
+ if ((ptr[2 + 4 + 1]) < 3) break;
+
+ this_length = ptr[2 + 4 + 1] - 3;
+ memcpy(tlv, ptr + 2 + 4 + 3, this_length);
+ tlv += this_length;
+
+ ptr[2 + 4] = 0; /* What a hack! */
+ if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
}
- /*
- * Copy the data to be decrypted
- */
- memcpy(&vp->vp_octets[0], data + offset, length - offset);
- vp->length -= offset;
+ *ptlv_length = tlv_length;
+ return tlv_data;
+}
+
+
+/*
+ * Walk over Evil WIMAX TLVs, creating attributes.
+ */
+static VALUE_PAIR *tlv2wimax(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret,
+ int attribute, int vendor,
+ uint8_t *ptr, size_t len, int nest)
+{
+ VALUE_PAIR *head = NULL;
+ VALUE_PAIR **tail = &head;
+ VALUE_PAIR *vp;
+ uint8_t *y; /* why do I need to do this? */
+
+ if (nest > fr_wimax_max_tlv) return NULL;
/*
- * Decrypt the attribute.
+ * Sanity check the attribute.
*/
- switch (vp->flags.encrypt) {
- /*
- * User-Password
- */
- case FLAG_ENCRYPT_USER_PASSWORD:
- if (original) {
- rad_pwdecode((char *)vp->vp_strvalue,
- vp->length, secret,
- original->vector);
- } else {
- rad_pwdecode((char *)vp->vp_strvalue,
- vp->length, secret,
- packet->vector);
- }
- if (vp->attribute == PW_USER_PASSWORD) {
- vp->length = strlen(vp->vp_strvalue);
- }
- break;
-
- /*
- * Tunnel-Password's may go ONLY
- * in response packets.
- */
- case FLAG_ENCRYPT_TUNNEL_PASSWORD:
- if (!original) goto raw;
-
- if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
- secret, original->vector) < 0) {
- goto raw;
- }
- break;
-
+ for (y = ptr; y < (ptr + len); y += y[1]) {
+ if ((y[0] == 0) || ((y + 2) > (ptr + len)) ||
+ (y[1] < 2) || ((y + y[1]) > (ptr + len))) {
+ return NULL;
+ }
+
/*
- * Ascend-Send-Secret
- * Ascend-Receive-Secret
+ * Attribute number is too large for us to
+ * represent it in our horrible internal
+ * representation.
*/
- case FLAG_ENCRYPT_ASCEND_SECRET:
- if (!original) {
- goto raw;
- } else {
- uint8_t my_digest[AUTH_VECTOR_LEN];
- make_secret(my_digest,
- original->vector,
- secret, data);
- memcpy(vp->vp_strvalue, my_digest,
- AUTH_VECTOR_LEN );
- vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
- vp->length = strlen(vp->vp_strvalue);
+ if ((ptr[0] & ~fr_wimax_mask[nest]) != 0) {
+ return NULL;
}
- break;
+ }
- default:
- break;
- } /* switch over encryption flags */
+ 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;
+ }
- switch (vp->type) {
- case PW_TYPE_STRING:
- case PW_TYPE_OCTETS:
- case PW_TYPE_ABINARY:
- /* nothing more to do */
- break;
+ if (!data2vp(packet, original, secret,
+ y[1] - 2, y + 2, vp)) {
+ goto error;
+ }
+ }
- case PW_TYPE_BYTE:
- if (vp->length != 1) goto raw;
+ *tail = vp;
+ while (*tail) tail = &((*tail)->next);
+ }
- vp->lvalue = vp->vp_octets[0];
- break;
+ return head;
+}
+/*
+ * Start at the *data* portion of a continued attribute. search
+ * through the rest of the attributes to find a matching one, and
+ * add it's contents to our contents.
+ */
+static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int attribute,
+ int vendor,
+ int length, /* CANNOT be zero */
+ uint8_t *data, size_t packet_length,
+ int flag, DICT_ATTR *da)
+{
+ size_t tlv_length, left;
+ uint8_t *ptr;
+ uint8_t *tlv_data;
+ VALUE_PAIR *vp, *head, **tail;
+ DICT_ATTR *tlv_da;
- case PW_TYPE_SHORT:
- if (vp->length != 2) goto raw;
+ /*
+ * Ensure we have data that hasn't been split across
+ * multiple attributes.
+ */
+ if (flag) {
+ tlv_data = rad_coalesce(attribute, vendor, length,
+ data, packet_length, &tlv_length);
+ if (!tlv_data) return NULL;
+ } else {
+ tlv_data = data;
+ tlv_length = length;
+ }
- vp->lvalue = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
- break;
+ /*
+ * Non-TLV types cannot be continued across multiple
+ * attributes. This is true even of keys that are
+ * encrypted with the tunnel-password method. The spec
+ * says that they can be continued... but also that the
+ * keys are 160 bits, which means that they CANNOT be
+ * continued. <sigh>
+ *
+ * Note that we don't check "flag" here. The calling
+ * code ensures that
+ */
+ if (!da || (da->type != PW_TYPE_TLV)) {
+ not_well_formed:
+ if (tlv_data == data) { /* true if we had 'goto' */
+ tlv_data = malloc(tlv_length);
+ if (!tlv_data) return NULL;
+ memcpy(tlv_data, data, tlv_length);
+ }
+
+ vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
+ if (!vp) return NULL;
+
+ vp->type = PW_TYPE_TLV;
+ vp->flags.encrypt = FLAG_ENCRYPT_NONE;
+ vp->flags.has_tag = 0;
+ vp->flags.is_tlv = 0;
+ vp->vp_tlv = tlv_data;
+ vp->length = tlv_length;
+ return vp;
+ } /* else it WAS a TLV, go decode the sub-tlv's */
+
+ /*
+ * Now (sigh) we walk over the TLV, seeing if it is
+ * well-formed.
+ */
+ left = tlv_length;
+ for (ptr = tlv_data;
+ ptr != (tlv_data + tlv_length);
+ ptr += ptr[1]) {
+ if ((left < 2) ||
+ (ptr[1] < 2) ||
+ (ptr[1] > left)) {
+ goto not_well_formed;
+ }
- case PW_TYPE_INTEGER:
- if (vp->length != 4) goto raw;
+ left -= ptr[1];
+ }
- memcpy(&vp->lvalue, vp->vp_octets, 4);
- vp->lvalue = ntohl(vp->lvalue);
+ /*
+ * Now we walk over the TLV *again*, creating sub-tlv's.
+ */
+ head = NULL;
+ tail = &head;
- if (vp->flags.has_tag) vp->lvalue &= 0x00ffffff;
+ for (ptr = tlv_data;
+ ptr != (tlv_data + tlv_length);
+ ptr += ptr[1]) {
- /*
- * Try to get named VALUEs
- */
- {
- DICT_VALUE *dval;
- dval = dict_valbyattr(vp->attribute,
- vp->lvalue);
- if (dval) {
- strNcpy(vp->vp_strvalue,
- dval->name,
- sizeof(vp->vp_strvalue));
+ tlv_da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[1]), vendor);
+ if (tlv_da && (tlv_da->type == PW_TYPE_TLV)) {
+ vp = tlv2wimax(packet, original, secret,
+ attribute | (ptr[0] << 8),
+ vendor, ptr + 2, ptr[1] - 2, 2);
+
+ if (!vp) goto error;
+ } else {
+ vp = paircreate(attribute | (ptr[0] << fr_wimax_shift[1]), vendor,
+ PW_TYPE_OCTETS);
+ if (!vp) {
+ error:
+ pairfree(&head);
+ goto not_well_formed;
+ }
+
+ if (!data2vp(packet, original, secret,
+ ptr[1] - 2, ptr + 2, vp)) {
+ goto error;
}
}
- break;
- case PW_TYPE_DATE:
- if (vp->length != 4) goto raw;
+ *tail = vp;
- memcpy(&vp->lvalue, vp->vp_octets, 4);
- vp->lvalue = ntohl(vp->lvalue);
- break;
+ while (*tail) tail = &((*tail)->next);
+ }
+ /*
+ * TLV's MAY be continued, but sometimes they're not.
+ */
+ if (tlv_data != data) free(tlv_data);
- case PW_TYPE_IPADDR:
- if (vp->length != 4) goto raw;
+ if (head->next) rad_sortvp(&head);
- memcpy(&vp->lvalue, vp->vp_octets, 4);
- break;
+ return head;
+}
- /*
- * IPv6 interface ID is 8 octets long.
- */
- case PW_TYPE_IFID:
- if (vp->length != 8) goto raw;
- /* vp->vp_ifid == vp->vp_octets */
- break;
-
- /*
- * IPv6 addresses are 16 octets long
- */
- case PW_TYPE_IPV6ADDR:
- if (vp->length != 16) goto raw;
- /* vp->vp_ipv6addr == vp->vp_octets */
- break;
-
- /*
- * IPv6 prefixes are 2 to 18 octets long.
- *
- * RFC 3162: The first octet is unused.
- * The second is the length of the prefix
- * the rest are the prefix data.
- *
- * The prefix length can have value 0 to 128.
- */
- case PW_TYPE_IPV6PREFIX:
- if (vp->length < 2 || vp->length > 18) goto raw;
- if (vp->vp_octets[1] > 128) goto raw;
- /*
- * FIXME: double-check that
- * (vp->vp_octets[1] >> 3) matches vp->length + 2
- */
- if (vp->length < 18) {
- memset(vp->vp_octets + vp->length, 0,
- 18 - vp->length);
- }
- break;
+/*
+ * 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;
- default:
- raw:
- vp->type = PW_TYPE_OCTETS;
- vp->length = length;
- memcpy(vp->vp_octets, data, length);
-
+ if ((length < 2) || (data[1] < 2)) return NULL;
- /*
- * Ensure there's no encryption or tag stuff,
- * we just pass the attribute as-is.
- */
- memset(&vp->flags, 0, sizeof(vp->flags));
+ /*
+ * For now, only one TLV is allowed.
+ */
+ if (data[1] != length) return NULL;
+
+ attribute |= (data[0] << fr_wimax_shift[2]);
+
+ vp = paircreate(attribute, VENDORPEC_EXTENDED, PW_TYPE_OCTETS);
+ if (!vp) return NULL;
+
+ return data2vp(packet, original, secret, length - 2, data + 2, vp);
+}
+
+/*
+ * Parse a RADIUS attribute into a data structure.
+ */
+VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, int attribute, int vendor,
+ int length, const uint8_t *data)
+{
+ VALUE_PAIR *vp;
+
+ /*
+ * Hard-coded values are bad...
+ */
+ if ((vendor == 0) && (attribute >= 241) && (attribute <= 246)) {
+ DICT_ATTR *da;
+
+ da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+ if (da && (da->flags.extended || da->flags.extended_flags)) {
+
+ if (length == 0) return NULL;
+
+ attribute |= (data[0] << fr_wimax_shift[1]);
+ vendor = VENDORPEC_EXTENDED;
+
+ data++;
+ length--;
+
+ /*
+ * There may be a flag octet.
+ */
+ if (da->flags.extended_flags) {
+ if (length == 0) return NULL;
+
+ /*
+ * If there's a flag, we can't
+ * handle it.
+ */
+ if (data[0] != 0) return NULL;
+ data++;
+ length--;
+ }
+
+ /*
+ * Now look up the extended attribute, to
+ * see if it's a TLV carrying more data.
+ *
+ */
+ da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+ if (da && da->flags.has_tlv) {
+ return tlv2vp(packet, original, secret,
+ attribute, length, data);
+ }
+ }
}
+ vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
+ if (!vp) return NULL;
- return vp;
+ return data2vp(packet, original, secret, length, data, vp);
}
uint32_t vendorcode;
VALUE_PAIR **tail;
VALUE_PAIR *pair;
- uint8_t *ptr;
+ uint8_t *ptr, *vsa_ptr;
int packet_length;
int attribute;
int attrlen;
int vendorlen;
radius_packet_t *hdr;
- int vsa_tlen, vsa_llen;
+ int vsa_tlen, vsa_llen, vsa_offset;
DICT_VENDOR *dv = NULL;
+ int num_attributes = 0;
/*
* Extract attribute-value pairs
vendorcode = 0;
vendorlen = 0;
vsa_tlen = vsa_llen = 1;
+ vsa_offset = 0;
/*
* We have to read at least two bytes.
(ptr[0] == 0) || (ptr[1] < 2) ||
(ptr[1] > packet_length)) break;
- /*
- * 192 is "integer" for Ascend. So if we
- * get 12 or more bytes, it must be the
- * new extended format.
- */
- if ((ptr[0] == PW_EXTENDED_ATTRIBUTE) &&
- (ptr[1] >= 2 + 12)) {
- pair = NULL;
- attrlen = diameter2vp(packet, original, secret,
- ptr, packet_length, &pair);
- goto check_pair;
- }
-
attribute = *ptr++;
attrlen = *ptr++;
packet_length -= 2;
if (attribute != PW_VENDOR_SPECIFIC) goto create_pair;
-
+
/*
* No vendor code, or ONLY vendor code.
*/
vendorlen = 0;
}
-
+
/*
* Handle Vendor-Specific
*/
uint8_t *subptr;
int sublen;
int myvendor;
-
+
/*
* attrlen was checked above.
*/
* Zero isn't allowed.
*/
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;
dv = dict_vendorbyvalue(myvendor);
if (dv) {
vsa_tlen = dv->type;
vsa_llen = dv->length;
+ if (dv->flags) vsa_offset = 1;
}
-
+
/*
* Sweep through the list of VSA's,
* seeing if they exactly fill the
int myattr = 0;
/*
- * Don't have a type, it's bad.
+ * Not enough room for one more
+ * attribute. Die!
*/
- if (sublen < vsa_tlen) goto create_pair;
-
+ if (sublen < (vsa_tlen + vsa_llen + vsa_offset)) goto create_pair;
+
/*
* Ensure that the attribute number
* is OK.
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;
}
-
- /*
- * Not enough room for one more
- * attribute. Die!
- */
- if (sublen < vsa_tlen + vsa_llen) goto create_pair;
+
switch (vsa_llen) {
case 0:
- attribute = (myvendor << 16) | myattr;
+ attribute = 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))
+ 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 1:
attribute = ptr[0];
break;
-
+
case 2:
attribute = (ptr[0] << 8) | ptr[1];
break;
default: /* can't hit this. */
return -1;
}
- attribute |= (vendorcode << 16);
+ vsa_ptr = ptr;
ptr += vsa_tlen;
switch (vsa_llen) {
case 1:
- attrlen = ptr[0] - (vsa_tlen + vsa_llen);
+ attrlen = ptr[0] - (vsa_tlen + vsa_llen + vsa_offset);
break;
-
+
case 2:
attrlen = ptr[1] - (vsa_tlen + vsa_llen);
break;
default: /* can't hit this. */
return -1;
}
- ptr += vsa_llen;
- vendorlen -= vsa_tlen + vsa_llen + attrlen;
- if (vendorlen == 0) vendorcode = 0;
- packet_length -= (vsa_tlen + vsa_llen);
+
+ ptr += vsa_llen + vsa_offset;
+ vendorlen -= vsa_tlen + vsa_llen + vsa_offset + attrlen;
+ packet_length -= (vsa_tlen + vsa_llen + vsa_offset);
+
+ /*
+ * Ignore VSAs that have no data.
+ */
+ if (attrlen == 0) goto next;
+
+ /*
+ * WiMAX attributes of type 0 are ignored. They
+ * are a secret flag to us that the attribute has
+ * already been dealt with.
+ */
+ if ((vendorcode == VENDORPEC_WIMAX) && (attribute == 0)) {
+ goto next;
+ }
+
+ if (vsa_offset) {
+ DICT_ATTR *da;
+
+ da = dict_attrbyvalue(attribute, vendorcode);
+
+ /*
+ * 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;
+
+ /*
+ * 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, vendorcode,
+ attrlen, ptr,
+ packet_length,
+ ((vsa_ptr[2] & 0x80) != 0),
+ da);
+ goto created_pair;
+ }
/*
* 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!
*/
create_pair:
+ if (!attrlen &&
+ (attribute != PW_CHARGEABLE_USER_IDENTITY)) goto next;
+
pair = rad_attr2vp(packet, original, secret,
- attribute, attrlen, ptr);
- check_pair:
+ attribute, vendorcode, attrlen, ptr);
if (!pair) {
pairfree(&packet->vps);
- librad_log("out of memory");
+ fr_strerror_printf("out of memory");
return -1;
}
+ created_pair:
*tail = pair;
while (pair) {
+ num_attributes++;
debug_pair(pair);
tail = &pair->next;
pair = pair->next;
}
+ /*
+ * VSA's may not have been counted properly in
+ * rad_packet_ok() above, as it is hard to count
+ * then without using the dictionary. We
+ * therefore enforce the limits here, too.
+ */
+ if ((fr_max_attributes > 0) &&
+ (num_attributes > fr_max_attributes)) {
+ char host_ipaddr[128];
+
+ pairfree(&packet->vps);
+ 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,
+ host_ipaddr, sizeof(host_ipaddr)),
+ num_attributes, fr_max_attributes);
+ return -1;
+ }
+
+ next:
+ if (vendorlen == 0) vendorcode = 0;
ptr += attrlen;
packet_length -= attrlen;
}
* Merge information from the outside world into our
* random pool.
*/
- lrad_rand_seed(packet->data, AUTH_HDR_LEN);
-
+ fr_rand_seed(packet->data, AUTH_HDR_LEN);
+
return 0;
}
* int *pwlen is updated to the new length of the encrypted
* password - a multiple of 16 bytes.
*/
-int rad_pwencode(char *passwd, int *pwlen, const char *secret,
+int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
const uint8_t *vector)
{
- lrad_MD5_CTX context, old;
+ FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
int i, n, secretlen;
int len;
* Use the secret to setup the decryption digest
*/
secretlen = strlen(secret);
-
- lrad_MD5Init(&context);
- lrad_MD5Update(&context, secret, secretlen);
+
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
old = context; /* save intermediate work */
/*
*/
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n == 0) {
- lrad_MD5Update(&context, vector, AUTH_PASS_LEN);
- lrad_MD5Final(digest, &context);
+ fr_MD5Update(&context, vector, AUTH_PASS_LEN);
+ fr_MD5Final(digest, &context);
} else {
context = old;
- lrad_MD5Update(&context,
- passwd + n - AUTH_PASS_LEN,
- AUTH_PASS_LEN);
- lrad_MD5Final(digest, &context);
+ fr_MD5Update(&context,
+ (uint8_t *) passwd + n - AUTH_PASS_LEN,
+ AUTH_PASS_LEN);
+ fr_MD5Final(digest, &context);
}
-
+
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + n] ^= digest[i];
}
/*
* Decode password.
*/
-int rad_pwdecode(char *passwd, int pwlen, const char *secret,
+int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
const uint8_t *vector)
{
- lrad_MD5_CTX context, old;
+ FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
- int i, n, secretlen;
+ int i;
+ size_t n, secretlen;
/*
* The RFC's say that the maximum is 128.
* Use the secret to setup the decryption digest
*/
secretlen = strlen(secret);
-
- lrad_MD5Init(&context);
- lrad_MD5Update(&context, secret, secretlen);
+
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
old = context; /* save intermediate work */
/*
*/
for (n = 0; n < pwlen; n += AUTH_PASS_LEN) {
if (n == 0) {
- lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- lrad_MD5Final(digest, &context);
+ fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+ fr_MD5Final(digest, &context);
context = old;
- lrad_MD5Update(&context, passwd, AUTH_PASS_LEN);
+ if (pwlen > AUTH_PASS_LEN) {
+ fr_MD5Update(&context, (uint8_t *) passwd,
+ AUTH_PASS_LEN);
+ }
} else {
- lrad_MD5Final(digest, &context);
+ fr_MD5Final(digest, &context);
context = old;
- lrad_MD5Update(&context, passwd + n, AUTH_PASS_LEN);
+ if (pwlen > (n + AUTH_PASS_LEN)) {
+ fr_MD5Update(&context, (uint8_t *) passwd + n,
+ AUTH_PASS_LEN);
+ }
}
-
+
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + n] ^= digest[i];
}
* This is per RFC-2868 which adds a two char SALT to the initial intermediate
* value MD5 hash.
*/
-int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret,
+int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret,
const uint8_t *vector)
{
uint8_t buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 3];
* add in some CSPRNG data. should be OK..
*/
salt[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
- (lrad_rand() & 0x07));
- salt[1] = lrad_rand();
+ (fr_rand() & 0x07));
+ salt[1] = fr_rand();
/*
* Padd password to multiple of AUTH_PASS_LEN bytes.
if (!n2) {
memcpy(buffer + secretlen, vector, AUTH_VECTOR_LEN);
memcpy(buffer + secretlen + AUTH_VECTOR_LEN, salt, 2);
- librad_md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN + 2);
+ fr_md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN + 2);
} else {
memcpy(buffer + secretlen, passwd + n2 - AUTH_PASS_LEN, AUTH_PASS_LEN);
- librad_md5_calc(digest, buffer, secretlen + AUTH_PASS_LEN);
+ fr_md5_calc(digest, buffer, secretlen + AUTH_PASS_LEN);
}
for (i = 0; i < AUTH_PASS_LEN; i++) {
* initial intermediate value, to differentiate it from the
* above.
*/
-int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret,
+int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
const uint8_t *vector)
{
- lrad_MD5_CTX context, old;
+ FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
int secretlen;
unsigned i, n, len, reallen;
* We need at least a salt.
*/
if (len < 2) {
- librad_log("tunnel password is too short");
+ fr_strerror_printf("tunnel password is too short");
return -1;
}
*/
secretlen = strlen(secret);
- lrad_MD5Init(&context);
- lrad_MD5Update(&context, secret, secretlen);
+ fr_MD5Init(&context);
+ fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
old = context; /* save intermediate work */
/*
*
* b(1) = MD5(secret + vector + salt)
*/
- lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- lrad_MD5Update(&context, passwd, 2);
+ fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+ fr_MD5Update(&context, passwd, 2);
reallen = 0;
for (n = 0; n < len; n += AUTH_PASS_LEN) {
int base = 0;
if (n == 0) {
- lrad_MD5Final(digest, &context);
+ fr_MD5Final(digest, &context);
context = old;
*/
reallen = passwd[2] ^ digest[0];
if (reallen >= len) {
- librad_log("tunnel password is too long for the attribute");
+ fr_strerror_printf("tunnel password is too long for the attribute");
return -1;
}
- lrad_MD5Update(&context, passwd + 2, AUTH_PASS_LEN);
+ fr_MD5Update(&context, passwd + 2, AUTH_PASS_LEN);
base = 1;
} else {
- lrad_MD5Final(digest, &context);
+ fr_MD5Final(digest, &context);
context = old;
- lrad_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN);
+ fr_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN);
}
for (i = base; i < AUTH_PASS_LEN; i++) {
VALUE_PAIR *password)
{
int i;
- char *ptr;
+ uint8_t *ptr;
uint8_t string[MAX_STRING_LEN * 2 + 1];
VALUE_PAIR *challenge;
* 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;
}
*output = id;
- librad_md5_calc((uint8_t *)output + 1, (uint8_t *)string, i);
+ fr_md5_calc((uint8_t *)output + 1, (uint8_t *)string, i);
return 0;
}
*
* May be called any number of times.
*/
-void lrad_rand_seed(const void *data, size_t size)
+void fr_rand_seed(const void *data, size_t size)
{
uint32_t hash;
/*
* Ensure that the pool is initialized.
*/
- if (!lrad_rand_initialized) {
+ if (!fr_rand_initialized) {
int fd;
-
- memset(&lrad_rand_pool, 0, sizeof(lrad_rand_pool));
+
+ memset(&fr_rand_pool, 0, sizeof(fr_rand_pool));
fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
ssize_t this;
total = this = 0;
- while (total < sizeof(lrad_rand_pool.randrsl)) {
- this = read(fd, lrad_rand_pool.randrsl,
- sizeof(lrad_rand_pool.randrsl) - total);
+ while (total < sizeof(fr_rand_pool.randrsl)) {
+ this = read(fd, fr_rand_pool.randrsl,
+ sizeof(fr_rand_pool.randrsl) - total);
if ((this < 0) && (errno != EINTR)) break;
if (this > 0) total += this;
}
close(fd);
} else {
- lrad_rand_pool.randrsl[0] = fd;
- lrad_rand_pool.randrsl[1] = time(NULL);
- lrad_rand_pool.randrsl[2] = errno;
+ fr_rand_pool.randrsl[0] = fd;
+ fr_rand_pool.randrsl[1] = time(NULL);
+ fr_rand_pool.randrsl[2] = errno;
}
- lrad_randinit(&lrad_rand_pool, 1);
- lrad_rand_pool.randcnt = 0;
- lrad_rand_initialized = 1;
+ fr_randinit(&fr_rand_pool, 1);
+ fr_rand_pool.randcnt = 0;
+ fr_rand_initialized = 1;
}
if (!data) return;
/*
* Hash the user data
*/
- hash = lrad_rand();
- if (!hash) hash = lrad_rand();
- hash = lrad_hash_update(data, size, hash);
-
- lrad_rand_pool.randmem[lrad_rand_pool.randcnt] ^= hash;
+ hash = fr_rand();
+ if (!hash) hash = fr_rand();
+ hash = fr_hash_update(data, size, hash);
+
+ fr_rand_pool.randmem[fr_rand_pool.randcnt] ^= hash;
}
/*
* Return a 32-bit random number.
*/
-uint32_t lrad_rand(void)
+uint32_t fr_rand(void)
{
uint32_t num;
/*
* Ensure that the pool is initialized.
*/
- if (!lrad_rand_initialized) {
- lrad_rand_seed(NULL, 0);
+ if (!fr_rand_initialized) {
+ fr_rand_seed(NULL, 0);
}
- num = lrad_rand_pool.randrsl[lrad_rand_pool.randcnt++];
- if (lrad_rand_pool.randcnt == 256) {
- lrad_isaac(&lrad_rand_pool);
- lrad_rand_pool.randcnt = 0;
+ num = fr_rand_pool.randrsl[fr_rand_pool.randcnt++];
+ if (fr_rand_pool.randcnt >= 256) {
+ fr_rand_pool.randcnt = 0;
+ fr_isaac(&fr_rand_pool);
}
return num;
RADIUS_PACKET *rp;
if ((rp = malloc(sizeof(RADIUS_PACKET))) == NULL) {
- librad_log("out of memory");
+ fr_strerror_printf("out of memory");
return NULL;
}
memset(rp, 0, sizeof(*rp));
rp->id = -1;
- rp->verified = -1;
+ rp->offset = -1;
if (newvector) {
int i;
* Don't expose the actual contents of the random
* pool.
*/
- base = lrad_rand();
+ base = fr_rand();
for (i = 0; i < AUTH_VECTOR_LEN; i += sizeof(uint32_t)) {
- hash = lrad_rand() ^ base;
+ hash = fr_rand() ^ base;
memcpy(rp->vector + i, &hash, sizeof(hash));
}
}
- lrad_rand(); /* stir the pool again */
+ fr_rand(); /* stir the pool again */
return rp;
}
+RADIUS_PACKET *rad_alloc_reply(RADIUS_PACKET *packet)
+{
+ RADIUS_PACKET *reply;
+
+ if (!packet) return NULL;
+
+ reply = rad_alloc(0);
+ if (!reply) return NULL;
+
+ /*
+ * Initialize the fields from the request.
+ */
+ reply->sockfd = packet->sockfd;
+ reply->dst_ipaddr = packet->src_ipaddr;
+ reply->src_ipaddr = packet->dst_ipaddr;
+ reply->dst_port = packet->src_port;
+ reply->src_port = packet->dst_port;
+ reply->id = packet->id;
+ reply->code = 0; /* UNKNOWN code */
+ memcpy(reply->vector, packet->vector,
+ sizeof(reply->vector));
+ reply->vps = NULL;
+ reply->data = NULL;
+ reply->data_len = 0;
+
+ return reply;
+}
+
+
/*
* Free a RADIUS_PACKET
*/
{
RADIUS_PACKET *radius_packet;
- if (!radius_packet_ptr) return;
+ if (!radius_packet_ptr || !*radius_packet_ptr) return;
radius_packet = *radius_packet_ptr;
free(radius_packet->data);
+
pairfree(&radius_packet->vps);
free(radius_packet);