* @copyright 2000-2003,2006 The FreeRADIUS server project
*/
-#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/libradius.h>
+
#include <freeradius-devel/md5.h>
#include <fcntl.h>
#include <freeradius-devel/udpfromto.h>
#endif
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-
#if 0
#define VP_TRACE if (fr_debug_flag) printf
+
+static void VP_HEXDUMP(char const *msg, uint8_t const *data, size_t len)
+{
+ size_t i;
+
+ printf("--- %s ---\n", msg);
+ for (i = 0; i < len; i++) {
+ if ((i & 0x0f) == 0) printf("%04x: ", (unsigned int) i);
+ printf("%02x ", data[i]);
+ if ((i & 0x0f) == 0x0f) printf("\n");
+ }
+ if ((len == 0x0f) || ((len & 0x0f) != 0x0f)) printf("\n");
+}
+
#else
#define VP_TRACE(_x, ...)
+#define VP_HEXDUMP(_x, _y, _z)
#endif
* is unsigned, and the attacker can use resources on the server,
* even if the end request is rejected.
*/
-int fr_max_attributes = 0;
+uint32_t fr_max_attributes = 0;
FILE *fr_log_fp = NULL;
typedef struct radius_packet_t {
static unsigned int salt_offset = 0;
static uint8_t nullvector[AUTH_VECTOR_LEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* for CoA decode */
-const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
- "",
+char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
+ "", //!< 0
"Access-Request",
"Access-Accept",
"Access-Reject",
"Password-Request",
"Password-Accept",
"Password-Reject",
- "Accounting-Message",
+ "Accounting-Message", //!< 10
"Access-Challenge",
"Status-Server",
"Status-Client",
"17",
"18",
"19",
- "20",
+ "20", //!< 20
"Resource-Free-Request",
"Resource-Free-Response",
"Resource-Query-Request",
"NAS-Reboot-Response",
"28",
"Next-Passcode",
- "New-Pin",
+ "New-Pin", //!< 30
"Terminate-Session",
"Password-Expired",
"Event-Request",
"37",
"38",
"39",
- "Disconnect-Request",
+ "Disconnect-Request", //!< 40
"Disconnect-ACK",
"Disconnect-NAK",
"CoA-Request",
"48",
"49",
"IP-Address-Allocate",
- "IP-Address-Release"
+ "IP-Address-Release", //!< 50
};
-void fr_printf_log(const char *fmt, ...)
+void fr_printf_log(char const *fmt, ...)
{
va_list ap;
return;
}
-static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
-static void print_hex_data(const uint8_t *ptr, int attrlen, int depth)
+static void print_hex_data(uint8_t const *ptr, int attrlen, int depth)
{
int i;
if (packet->data_len > 20) {
int total;
- const uint8_t *ptr;
+ uint8_t const *ptr;
fprintf(fr_log_fp, " Data:");
total = packet->data_len - 20;
* possible combinations.
*/
static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
- fr_ipaddr_t *src_ipaddr, int src_port,
- fr_ipaddr_t *dst_ipaddr, int dst_port)
+#ifdef WITH_UDPFROMTO
+ fr_ipaddr_t *src_ipaddr, uint16_t src_port,
+#else
+ UNUSED fr_ipaddr_t *src_ipaddr, UNUSED uint16_t src_port,
+#endif
+ fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
{
int rcode;
struct sockaddr_storage dst;
socklen_t sizeof_src;
fr_ipaddr2sockaddr(src_ipaddr, src_port, &src, &sizeof_src);
-#else
- src_port = src_port; /* -Wunused */
#endif
if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) {
(struct sockaddr *)&dst, sizeof_dst);
goto done;
}
-#else
- src_ipaddr = src_ipaddr; /* -Wunused */
#endif
/*
done:
#endif
if (rcode < 0) {
- DEBUG("rad_send() failed: %s\n", strerror(errno));
+ fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
}
return rcode;
struct sockaddr_storage src;
socklen_t sizeof_src = sizeof(src);
- recvfrom(sockfd, header, sizeof(header), 0,
- (struct sockaddr *)&src, &sizeof_src);
+ (void) 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 rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code)
{
ssize_t data_len, packet_len;
uint8_t header[4];
* Too little data is available, discard the packet.
*/
if (data_len < 4) {
- recvfrom(sockfd, header, sizeof(header), 0,
- (struct sockaddr *)&src, &sizeof_src);
+ rad_recv_discard(sockfd);
+
return 1;
} else { /* we got 4 bytes of data. */
* a RADIUS header length: discard it.
*/
if (packet_len < AUTH_HDR_LEN) {
- recvfrom(sockfd, header, sizeof(header), 0,
- (struct sockaddr *)&src, &sizeof_src);
+ rad_recv_discard(sockfd);
+
return 1;
/*
* Anything after 4k will be discarded.
*/
} else if (packet_len > MAX_PACKET_LEN) {
- recvfrom(sockfd, header, sizeof(header), 0,
- (struct sockaddr *)&src, &sizeof_src);
+ rad_recv_discard(sockfd);
+
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);
+ rad_recv_discard(sockfd);
+
return 1;
}
* @brief wrapper for recvfrom, which handles recvfromto, IPv6, and all
* possible combinations.
*/
-static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
+static ssize_t rad_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags,
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;
socklen_t sizeof_src = sizeof(src);
- socklen_t sizeof_dst = sizeof(dst);
+ socklen_t sizeof_dst = sizeof(dst);
ssize_t data_len;
uint8_t header[4];
- void *buf;
size_t len;
- int port;
+ uint16_t port;
memset(&src, 0, sizeof_src);
memset(&dst, 0, sizeof_dst);
/*
- * Get address family, etc. first, so we know if we
- * need to do udpfromto.
- *
- * FIXME: udpfromto also does this, but it's not
- * a critical problem.
- */
- if (getsockname(sockfd, (struct sockaddr *)&dst,
- &sizeof_dst) < 0) return -1;
-
- /*
* Read the length of the packet, from the packet.
* This lets us allocate the buffer to use for
* reading the rest of the packet.
* Too little data is available, discard the packet.
*/
if (data_len < 4) {
- recvfrom(sockfd, header, sizeof(header), flags,
- (struct sockaddr *)&src, &sizeof_src);
+ rad_recv_discard(sockfd);
+
return 0;
} else { /* we got 4 bytes of data. */
}
}
- buf = malloc(len);
- if (!buf) return -1;
+ packet->data = talloc_array(packet, uint8_t, len);
+ if (!packet->data) return -1;
/*
* Receive the packet. The OS will discard any data in the
* packet after "len" bytes.
*/
#ifdef WITH_UDPFROMTO
- if ((dst.ss_family == AF_INET) || (dst.ss_family == AF_INET6)) {
- data_len = recvfromto(sockfd, buf, len, flags,
- (struct sockaddr *)&src, &sizeof_src,
- (struct sockaddr *)&dst, &sizeof_dst);
- } else
+ data_len = recvfromto(sockfd, packet->data, len, flags,
+ (struct sockaddr *)&src, &sizeof_src,
+ (struct sockaddr *)&dst, &sizeof_dst);
+#else
+ data_len = recvfrom(sockfd, packet->data, len, flags,
+ (struct sockaddr *)&src, &sizeof_src);
+
+ /*
+ * Get the destination address, too.
+ */
+ if (getsockname(sockfd, (struct sockaddr *)&dst,
+ &sizeof_dst) < 0) return -1;
#endif
- /*
- * No udpfromto, fail gracefully.
- */
- data_len = recvfrom(sockfd, buf, len, flags,
- (struct sockaddr *)&src, &sizeof_src);
if (data_len < 0) {
- free(buf);
return data_len;
}
if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) {
- free(buf);
return -1; /* Unknown address family, Die Die Die! */
}
*src_port = port;
* Different address families should never happen.
*/
if (src.ss_family != dst.ss_family) {
- free(buf);
return -1;
}
- /*
- * Tell the caller about the data
- */
- *pbuf = buf;
-
return data_len;
}
#define AUTH_PASS_LEN (AUTH_VECTOR_LEN)
/**
* @brief Build an encrypted secret value to return in a reply packet
- *
- * The secret is hidden by xoring with a MD5 digest
- * created from the shared secret and the authentication
- * vector. We put them into MD5 in the reverse order from
- * that used when encrypting passwords to RADIUS.
+ *
+ * The secret is hidden by xoring with a MD5 digest
+ * created from the shared secret and the authentication
+ * vector. We put them into MD5 in the reverse order from
+ * that used when encrypting passwords to RADIUS.
*
*/
-static void make_secret(uint8_t *digest, const uint8_t *vector,
- const char *secret, const uint8_t *value)
+static void make_secret(uint8_t *digest, uint8_t const *vector,
+ char const *secret, uint8_t const *value)
{
FR_MD5_CTX context;
- int i;
+ int i;
- fr_MD5Init(&context);
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
- fr_MD5Final(digest, &context);
+ fr_md5_init(&context);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(digest, &context);
- for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
+ for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
digest[i] ^= value[i];
- }
+ }
}
#define MAX_PASS_LEN (128)
static void make_passwd(uint8_t *output, ssize_t *outlen,
- const uint8_t *input, size_t inlen,
- const char *secret, const uint8_t *vector)
+ uint8_t const *input, size_t inlen,
+ char const *secret, uint8_t const *vector)
{
FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
}
*outlen = len;
- fr_MD5Init(&context);
- fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
old = context;
/*
* Do first pass.
*/
- fr_MD5Update(&context, vector, AUTH_PASS_LEN);
+ fr_md5_update(&context, vector, AUTH_PASS_LEN);
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
context = old;
- fr_MD5Update(&context,
+ fr_md5_update(&context,
passwd + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
}
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + n] ^= digest[i];
}
}
static void make_tunnel_passwd(uint8_t *output, ssize_t *outlen,
- const uint8_t *input, size_t inlen, size_t room,
- const char *secret, const uint8_t *vector)
+ uint8_t const *input, size_t inlen, size_t room,
+ char const *secret, uint8_t const *vector)
{
FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
passwd[1] = fr_rand();
passwd[2] = inlen; /* length of the password string */
- fr_MD5Init(&context);
- fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
old = context;
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Update(&context, &passwd[0], 2);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(&context, &passwd[0], 2);
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
context = old;
- fr_MD5Update(&context,
+ fr_md5_update(&context,
passwd + 2 + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
}
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + 2 + n] ^= digest[i];
extern int fr_attr_shift[];
extern int fr_attr_mask[];
-static int do_next_tlv(const VALUE_PAIR *vp, const VALUE_PAIR *next, int nest)
+static int do_next_tlv(VALUE_PAIR const *vp, VALUE_PAIR const *next, int nest)
{
unsigned int tlv1, tlv2;
/*
* Not from the same vendor, skip it.
*/
- if (vp->vendor != next->vendor) return 0;
+ if (vp->da->vendor != next->da->vendor) return 0;
/*
* In a different TLV space, skip it.
*/
- tlv1 = vp->attribute;
- tlv2 = next->attribute;
-
+ tlv1 = vp->da->attr;
+ tlv2 = next->da->attr;
+
tlv1 &= ((1 << fr_attr_shift[nest]) - 1);
tlv2 &= ((1 << fr_attr_shift[nest]) - 1);
-
+
if (tlv1 != tlv2) return 0;
return 1;
}
-static ssize_t vp2data_any(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, int nest,
- const VALUE_PAIR **pvp,
+static ssize_t vp2data_any(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, int nest,
+ VALUE_PAIR const **pvp,
uint8_t *start, size_t room);
-static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp,
+static ssize_t vp2attr_rfc(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp,
unsigned int attribute, uint8_t *ptr, size_t room);
/**
* the *data* portion of the TLV, and assumes that the encapsulating
* attribute has already been encoded.
*/
-static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, int nest,
- const VALUE_PAIR **pvp,
+static ssize_t vp2data_tlvs(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, int nest,
+ VALUE_PAIR const **pvp,
uint8_t *start, size_t room)
{
ssize_t len;
size_t my_room;
uint8_t *ptr = start;
- const VALUE_PAIR *vp = *pvp;
- const VALUE_PAIR *svp = vp;
+ VALUE_PAIR const *vp = *pvp;
+ VALUE_PAIR const *svp = vp;
if (!svp) return 0;
#endif
while (vp) {
- if (room < 2) return ptr - start;
-
- ptr[0] = (vp->attribute >> fr_attr_shift[nest]) & fr_attr_mask[nest];
+ VERIFY_VP(vp);
+
+ if (room <= 2) return ptr - start;
+
+ ptr[0] = (vp->da->attr >> fr_attr_shift[nest]) & fr_attr_mask[nest];
ptr[1] = 2;
-
+
my_room = room;
if (room > 255) my_room = 255;
room -= ptr[1];
ptr += ptr[1];
*pvp = vp;
-
+
if (!do_next_tlv(svp, vp, nest)) break;
}
#ifndef NDEBUG
if ((fr_debug_flag > 3) && fr_log_fp) {
- DICT_ATTR *da;
-
- da = dict_attrbyvalue(svp->attribute & ((1 << fr_attr_shift[nest ]) - 1), svp->vendor);
+ DICT_ATTR const *da;
+
+ da = dict_attrbyvalue(svp->da->attr & ((1 << fr_attr_shift[nest ]) - 1), svp->da->vendor);
if (da) fprintf(fr_log_fp, "\t%s = ...\n", da->name);
}
#endif
* @brief Encodes the data portion of an attribute.
* @return -1 on error, or the length of the data portion.
*/
-static ssize_t vp2data_any(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, int nest,
- const VALUE_PAIR **pvp,
+static ssize_t vp2data_any(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, int nest,
+ VALUE_PAIR const **pvp,
uint8_t *start, size_t room)
{
uint32_t lvalue;
ssize_t len;
- const uint8_t *data;
+ uint8_t const *data;
uint8_t *ptr = start;
uint8_t array[4];
uint64_t lvalue64;
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
+
+ VERIFY_VP(vp);
/*
* See if we need to encode a TLV. The low portion of
*
* If we cared about the stack, we could unroll the loop.
*/
- if (vp->flags.is_tlv && (nest < fr_attr_max_tlv) &&
- ((vp->attribute >> fr_attr_shift[nest + 1]) != 0)) {
+ if (vp->da->flags.is_tlv && (nest < fr_attr_max_tlv) &&
+ ((vp->da->attr >> fr_attr_shift[nest + 1]) != 0)) {
return vp2data_tlvs(packet, original, secret, nest + 1, pvp,
start, room);
}
/*
* Set up the default sources for the data.
*/
- data = vp->vp_octets;
len = vp->length;
- /*
- * Short-circuit it for long attributes. They can't be
- * encrypted, tagged, etc.
- */
- if ((vp->type & PW_FLAG_LONG) != 0) goto do_tlv;
-
- switch(vp->type) {
+ switch(vp->da->type) {
case PW_TYPE_STRING:
case PW_TYPE_OCTETS:
+ case PW_TYPE_TLV:
+ data = vp->data.ptr;
+ if (!data) {
+ fr_strerror_printf("ERROR: Cannot encode NULL data");
+ return -1;
+ }
+ break;
+
case PW_TYPE_IFID:
- case PW_TYPE_IPV6ADDR:
- case PW_TYPE_IPV6PREFIX:
- case PW_TYPE_IPV4PREFIX:
+ case PW_TYPE_IPV4_ADDR:
+ case PW_TYPE_IPV6_ADDR:
+ case PW_TYPE_IPV6_PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
case PW_TYPE_ABINARY:
- /* nothing more to do */
+ case PW_TYPE_ETHERNET: /* just in case */
+ data = (uint8_t const *) &vp->data;
break;
case PW_TYPE_BYTE:
len = 1; /* just in case */
- array[0] = vp->vp_integer & 0xff;
+ array[0] = vp->vp_byte;
data = array;
break;
case PW_TYPE_SHORT:
len = 2; /* just in case */
- array[0] = (vp->vp_integer >> 8) & 0xff;
- array[1] = vp->vp_integer & 0xff;
+ array[0] = (vp->vp_short >> 8) & 0xff;
+ array[1] = vp->vp_short & 0xff;
data = array;
break;
data = (uint8_t *) &lvalue64;
break;
- case PW_TYPE_IPADDR:
- 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->vp_date);
- data = (const uint8_t *) &lvalue;
+ data = (uint8_t const *) &lvalue;
len = 4; /* just in case */
break;
len = 4; /* just in case */
slvalue = htonl(vp->vp_signed);
memcpy(array, &slvalue, sizeof(slvalue));
+ data = array;
break;
}
- case PW_TYPE_TLV:
- do_tlv:
- data = vp->vp_tlv;
- if (!data) {
- fr_strerror_printf("ERROR: Cannot encode NULL TLV");
- return -1;
- }
- break;
-
default: /* unknown type: ignore it */
- fr_strerror_printf("ERROR: Unknown attribute type %d", vp->type);
+ fr_strerror_printf("ERROR: Unknown attribute type %d", vp->da->type);
return -1;
}
* Attributes with encrypted values MUST be less than
* 128 bytes long.
*/
- switch (vp->flags.encrypt) {
+ switch (vp->da->flags.encrypt) {
case FLAG_ENCRYPT_USER_PASSWORD:
make_passwd(ptr, &len, data, len,
secret, packet->vector);
case FLAG_ENCRYPT_TUNNEL_PASSWORD:
lvalue = 0;
- if (vp->flags.has_tag) lvalue = 1;
+ if (vp->da->flags.has_tag) lvalue = 1;
/*
* Check if there's enough room. If there isn't,
*/
if (room < (18 + lvalue)) return 0;
- switch (packet->code) {
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
- default:
+ switch (packet->code) {
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ default:
if (!original) {
- fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name);
+ fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->da->name);
return -1;
}
- if (lvalue) ptr[0] = vp->flags.tag;
+ if (lvalue) ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
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;
+ break;
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_COA_REQUEST:
+ ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
secret, packet->vector);
- break;
- }
+ break;
+ }
break;
/*
* always fits.
*/
case FLAG_ENCRYPT_ASCEND_SECRET:
+ if (len != 16) return 0;
make_secret(ptr, packet->vector, secret, data);
len = AUTH_VECTOR_LEN;
break;
default:
- if (vp->flags.has_tag && TAG_VALID(vp->flags.tag)) {
- if (vp->type == PW_TYPE_STRING) {
+ if (vp->da->flags.has_tag && TAG_VALID(vp->tag)) {
+ if (vp->da->type == PW_TYPE_STRING) {
if (len > ((ssize_t) (room - 1))) len = room - 1;
- ptr[0] = vp->flags.tag;
+ ptr[0] = vp->tag;
ptr++;
- } else if (vp->type == PW_TYPE_INTEGER) {
- array[0] = vp->flags.tag;
+ } else if (vp->da->type == PW_TYPE_INTEGER) {
+ array[0] = vp->tag;
} /* else it can't be any other type */
}
memcpy(ptr, data, len);
return len + (ptr - start);
}
-static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
+static ssize_t attr_shift(uint8_t const *start, uint8_t const *end,
uint8_t *ptr, int hdr_len, ssize_t len,
int flag_offset, int vsa_offset)
{
int check_len = len - ptr[1];
int total = len + hdr_len;
-
+
/*
* Pass 1: Check if the addition of the headers
* overflows the available room. If so, return
* what we were capable of encoding.
*/
-
+
while (check_len > (255 - hdr_len)) {
total += hdr_len;
check_len -= (255 - hdr_len);
if ((ptr + ptr[1] + total) > end) {
return (ptr + ptr[1]) - start;
}
-
+
/*
* Pass 2: Now that we know there's enough room,
* re-arrange the data to form a set of valid
*/
while (1) {
int sublen = 255 - ptr[1];
-
+
if (len <= sublen) {
break;
}
-
+
len -= sublen;
memmove(ptr + 255 + hdr_len, ptr + 255, sublen);
memcpy(ptr + 255, ptr, hdr_len);
ptr[1] += sublen;
if (vsa_offset) ptr[vsa_offset] += sublen;
ptr[flag_offset] |= 0x80;
-
+
ptr += 255;
ptr[1] = hdr_len;
if (vsa_offset) ptr[vsa_offset] = 3;
/**
* @brief Encode an "extended" attribute.
*/
-int rad_vp2extended(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp,
+int rad_vp2extended(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp,
uint8_t *ptr, size_t room)
{
int len;
int hdr_len;
uint8_t *start = ptr;
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
- if (!vp->flags.extended) {
+ VERIFY_VP(vp);
+
+ if (!vp->da->flags.extended) {
fr_strerror_printf("rad_vp2extended called for non-extended attribute");
return -1;
}
* The attribute number is encoded into the upper 8 bits
* of the vendor ID.
*/
- ptr[0] = (vp->vendor / FR_MAX_VENDOR) & 0xff;
+ ptr[0] = (vp->da->vendor / FR_MAX_VENDOR) & 0xff;
- if (!vp->flags.long_extended) {
+ if (!vp->da->flags.long_extended) {
if (room < 3) return 0;
-
+
ptr[1] = 3;
- ptr[2] = vp->attribute & fr_attr_mask[0];
+ ptr[2] = vp->da->attr & fr_attr_mask[0];
} else {
if (room < 4) return 0;
ptr[1] = 4;
- ptr[2] = vp->attribute & fr_attr_mask[0];
+ ptr[2] = vp->da->attr & fr_attr_mask[0];
ptr[3] = 0;
}
* Only "flagged" attributes can be longer than one
* attribute.
*/
- if (!vp->flags.long_extended && (room > 255)) {
+ if (!vp->da->flags.long_extended && (room > 255)) {
room = 255;
}
/*
* Handle EVS VSAs.
*/
- if (vp->flags.evs) {
+ if (vp->da->flags.evs) {
uint8_t *evs = ptr + ptr[1];
if (room < (size_t) (ptr[1] + 5)) return 0;
ptr[2] = 26;
evs[0] = 0; /* always zero */
- evs[1] = (vp->vendor >> 16) & 0xff;
- evs[2] = (vp->vendor >> 8) & 0xff;
- evs[3] = vp->vendor & 0xff;
- evs[4] = vp->attribute & fr_attr_mask[0];
+ evs[1] = (vp->da->vendor >> 16) & 0xff;
+ evs[2] = (vp->da->vendor >> 8) & 0xff;
+ evs[3] = vp->da->vendor & 0xff;
+ evs[4] = vp->da->attr & fr_attr_mask[0];
ptr[1] += 5;
}
* and copy the existing header over. Set the "M" flag ONLY
* after copying the rest of the data.
*/
- if (vp->flags.long_extended && (len > (255 - ptr[1]))) {
+ if (vp->da->flags.long_extended && (len > (255 - ptr[1]))) {
return attr_shift(start, start + room, ptr, 4, len, 3, 0);
}
ptr[1] += len;
-
+
#ifndef NDEBUG
if ((fr_debug_flag > 3) && fr_log_fp) {
int jump = 3;
fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
- if (!vp->flags.long_extended) {
+ if (!vp->da->flags.long_extended) {
fprintf(fr_log_fp, "%02x ", ptr[2]);
-
+
} else {
fprintf(fr_log_fp, "%02x %02x ", ptr[2], ptr[3]);
jump = 4;
}
- if (vp->flags.evs) {
+ if (vp->da->flags.evs) {
fprintf(fr_log_fp, "%02x%02x%02x%02x (%u) %02x ",
ptr[jump], ptr[jump + 1],
ptr[jump + 2], ptr[jump + 3],
/**
* @brief Encode a WiMAX attribute.
*/
-int rad_vp2wimax(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp,
+int rad_vp2wimax(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp,
uint8_t *ptr, size_t room)
{
int len;
uint32_t lvalue;
int hdr_len;
uint8_t *start = ptr;
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
+
+ VERIFY_VP(vp);
/*
* Double-check for WiMAX format.
*/
- if (!vp->flags.wimax) {
+ if (!vp->da->flags.wimax) {
fr_strerror_printf("rad_vp2wimax called for non-WIMAX VSA");
return -1;
}
ptr = start;
ptr[0] = PW_VENDOR_SPECIFIC;
ptr[1] = 9;
- lvalue = htonl(vp->vendor);
+ lvalue = htonl(vp->da->vendor);
memcpy(ptr + 2, &lvalue, 4);
- ptr[6] = (vp->attribute & fr_attr_mask[1]);
+ ptr[6] = (vp->da->attr & fr_attr_mask[1]);
ptr[7] = 3;
ptr[8] = 0; /* continuation byte */
}
/**
+ * @brief Encode an RFC format attribute, with the "concat" flag set.
+ *
+ * If there isn't enough room in the packet, the data is
+ * truncated to fit.
+ */
+static ssize_t vp2attr_concat(UNUSED RADIUS_PACKET const *packet,
+ UNUSED RADIUS_PACKET const *original,
+ UNUSED char const *secret, VALUE_PAIR const **pvp,
+ unsigned int attribute, uint8_t *start, size_t room)
+{
+ uint8_t *ptr = start;
+ uint8_t const *p;
+ size_t len, left;
+ VALUE_PAIR const *vp = *pvp;
+
+ VERIFY_VP(vp);
+
+ debug_pair(vp);
+
+ p = vp->vp_octets;
+ len = vp->length;
+
+ while (len > 0) {
+ if (room <= 2) break;
+
+ ptr[0] = attribute;
+ ptr[1] = 2;
+
+ left = len;
+
+ /* no more than 253 octets */
+ if (left > 253) left = 253;
+
+ /* no more than "room" octets */
+ if (room < (left + 2)) left = room - 2;
+
+ memcpy(ptr + 2, p, left);
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
+ print_hex_data(ptr + 2, len, 3);
+ }
+#endif
+ ptr[1] += left;
+ ptr += ptr[1];
+ p += left;
+ room -= left;
+ len -= left;
+ }
+
+ *pvp = vp->next;
+ return ptr - start;
+}
+
+/**
* @brief Encode an RFC format TLV.
*
* This could be a standard attribute,
* or a TLV data type. If it's a standard attribute, then
- * vp->attribute == attribute. Otherwise, attribute may be
+ * vp->da->attr == attribute. Otherwise, attribute may be
* something else.
*/
-static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp,
+static ssize_t vp2attr_rfc(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp,
unsigned int attribute, uint8_t *ptr, size_t room)
{
ssize_t len;
- if (room < 2) return 0;
+ if (room <= 2) return 0;
ptr[0] = attribute & 0xff;
ptr[1] = 2;
* @brief Encode a VSA which is a TLV. If it's in the RFC format, call
* vp2attr_rfc. Otherwise, encode it here.
*/
-static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp,
+static ssize_t vp2attr_vsa(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp,
unsigned int attribute, unsigned int vendor,
uint8_t *ptr, size_t room)
{
ssize_t len;
DICT_VENDOR *dv;
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
+ VERIFY_VP(vp);
/*
* Unknown vendor: RFC format.
* Known vendor and RFC format: go do that.
*/
dv = dict_vendorbyvalue(vendor);
if (!dv ||
- (!vp->flags.is_tlv && (dv->type == 1) && (dv->length == 1))) {
+ (!vp->da->flags.is_tlv && (dv->type == 1) && (dv->length == 1))) {
return vp2attr_rfc(packet, original, secret, pvp,
attribute, ptr, room);
}
fprintf(fr_log_fp, "\t\t%02x%02x%02x%02x ",
ptr[0], ptr[1], ptr[2], ptr[3]);
break;
-
+
case 2:
if ((fr_debug_flag > 3) && fr_log_fp)
fprintf(fr_log_fp, "\t\t%02x%02x ",
ptr[0], ptr[1]);
break;
-
+
case 1:
if ((fr_debug_flag > 3) && fr_log_fp)
fprintf(fr_log_fp, "\t\t%02x ", ptr[0]);
break;
}
-
+
switch (dv->length) {
default:
break;
/**
* @brief Encode a Vendor-Specific attribute.
*/
-int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp, uint8_t *ptr,
+int rad_vp2vsa(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp, uint8_t *ptr,
size_t room)
{
ssize_t len;
uint32_t lvalue;
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
+ VERIFY_VP(vp);
/*
* Double-check for WiMAX format.
*/
- if (vp->flags.wimax) {
+ if (vp->da->flags.wimax) {
return rad_vp2wimax(packet, original, secret, pvp,
ptr, room);
}
- if (vp->vendor > FR_MAX_VENDOR) {
+ if (vp->da->vendor > FR_MAX_VENDOR) {
fr_strerror_printf("rad_vp2vsa: Invalid arguments");
return -1;
}
*/
ptr[0] = PW_VENDOR_SPECIFIC;
ptr[1] = 6;
- lvalue = htonl(vp->vendor);
+ lvalue = htonl(vp->da->vendor);
memcpy(ptr + 2, &lvalue, 4);
if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
len = vp2attr_vsa(packet, original, secret, pvp,
- vp->attribute, vp->vendor,
+ vp->da->attr, vp->da->vendor,
ptr + ptr[1], room);
if (len < 0) return len;
/**
* @brief Encode an RFC standard attribute 1..255
*/
-int rad_vp2rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp,
+int rad_vp2rfc(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp,
uint8_t *ptr, size_t room)
{
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
+
+ VERIFY_VP(vp);
- if (vp->vendor != 0) {
+ if (vp->da->vendor != 0) {
fr_strerror_printf("rad_vp2rfc called with VSA");
return -1;
}
- if ((vp->attribute == 0) || (vp->attribute > 255)) {
- fr_strerror_printf("rad_vp2rfc called with non-standard attribute %u", vp->attribute);
+ if ((vp->da->attr == 0) || (vp->da->attr > 255)) {
+ fr_strerror_printf("rad_vp2rfc called with non-standard attribute %u", vp->da->attr);
return -1;
}
* Thank you, WiMAX!
*/
if ((vp->length == 0) &&
- (vp->attribute == PW_CHARGEABLE_USER_IDENTITY)) {
+ (vp->da->attr == PW_CHARGEABLE_USER_IDENTITY)) {
ptr[0] = PW_CHARGEABLE_USER_IDENTITY;
ptr[1] = 2;
/*
* Message-Authenticator is hard-coded.
*/
- if (vp->attribute == PW_MESSAGE_AUTHENTICATOR) {
+ if (vp->da->attr == PW_MESSAGE_AUTHENTICATOR) {
if (room < 18) return -1;
-
+
debug_pair(vp);
ptr[0] = PW_MESSAGE_AUTHENTICATOR;
ptr[1] = 18;
fprintf(fr_log_fp, "\t\t50 12 ...\n");
}
#endif
-
+
*pvp = (*pvp)->next;
return 18;
}
- return vp2attr_rfc(packet, original, secret, pvp, vp->attribute,
+ /*
+ * EAP-Message is special.
+ */
+ if (vp->da->flags.concat && (vp->length > 253)) {
+ return vp2attr_concat(packet, original, secret, pvp, vp->da->attr,
+ ptr, room);
+ }
+
+ return vp2attr_rfc(packet, original, secret, pvp, vp->da->attr,
ptr, room);
}
-static ssize_t rad_vp2rfctlv(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp,
+static ssize_t rad_vp2rfctlv(RADIUS_PACKET const *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp,
uint8_t *start, size_t room)
{
ssize_t len;
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
- if (!vp->flags.is_tlv) {
+ VERIFY_VP(vp);
+
+ if (!vp->da->flags.is_tlv) {
fr_strerror_printf("rad_vp2rfctlv: attr is not a TLV");
return -1;
}
- if ((vp->vendor & (FR_MAX_VENDOR - 1)) != 0) {
+ if ((vp->da->vendor & (FR_MAX_VENDOR - 1)) != 0) {
fr_strerror_printf("rad_vp2rfctlv: attr is not an RFC TLV");
return -1;
}
/*
* Encode the first level of TLVs
*/
- start[0] = (vp->vendor / FR_MAX_VENDOR) & 0xff;
+ start[0] = (vp->da->vendor / FR_MAX_VENDOR) & 0xff;
start[1] = 4;
- start[2] = vp->attribute & fr_attr_mask[0];
+ start[2] = vp->da->attr & fr_attr_mask[0];
start[3] = 2;
len = vp2data_any(packet, original, secret, 0, pvp,
if (len <= 0) return len;
if (len > 253) {
- fprintf(stderr, "SHIT!\n");
return -1;
}
/**
* @brief Parse a data structure into a RADIUS attribute.
*/
-int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, const VALUE_PAIR **pvp, uint8_t *start,
+int rad_vp2attr(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
+ char const *secret, VALUE_PAIR const **pvp, uint8_t *start,
size_t room)
{
- const VALUE_PAIR *vp;
+ VALUE_PAIR const *vp;
if (!pvp || !*pvp || !start || (room <= 2)) return -1;
vp = *pvp;
+ VERIFY_VP(vp);
+
/*
* RFC format attributes take the fast path.
*/
- if (!vp->vendor) {
- if (vp->attribute > 255) return 0;
+ if (!vp->da->vendor) {
+ if (vp->da->attr > 255) return 0;
return rad_vp2rfc(packet, original, secret, pvp,
start, room);
}
- if (vp->flags.extended) {
+ if (vp->da->flags.extended) {
return rad_vp2extended(packet, original, secret, pvp,
start, room);
}
* The upper 8 bits of the vendor number are the standard
* space attribute which is a TLV.
*/
- if ((vp->vendor & (FR_MAX_VENDOR - 1)) == 0) {
+ if ((vp->da->vendor & (FR_MAX_VENDOR - 1)) == 0) {
return rad_vp2rfctlv(packet, original, secret, pvp,
start, room);
}
- if (vp->flags.wimax) {
+ if (vp->da->flags.wimax) {
return rad_vp2wimax(packet, original, secret, pvp,
start, room);
}
/**
* @brief Encode a packet.
*/
-int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret)
+int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret)
{
- radius_packet_t *hdr;
- uint8_t *ptr;
- uint16_t total_length;
- int len;
- const VALUE_PAIR *reply;
- const char *what;
- char ip_src_buffer[128];
- char ip_dst_buffer[128];
+ radius_packet_t *hdr;
+ uint8_t *ptr;
+ uint16_t total_length;
+ int len;
+ VALUE_PAIR const *reply;
+ char const *what;
+ char ip_src_buffer[INET6_ADDRSTRLEN];
+ char ip_dst_buffer[INET6_ADDRSTRLEN];
/*
* A 4K packet, aligned on 64-bits.
*/
uint64_t data[MAX_PACKET_LEN / sizeof(uint64_t)];
- if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+ if (is_radius_code(packet->code)) {
what = fr_packet_codes[packet->code];
} else {
what = "Reply";
}
- DEBUG("Sending %s of id %d from %s port %u to %s port %u\n",
+ DEBUG("Sending %s Id %d from %s:%u to %s:%u\n",
what, packet->id,
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
* Double-check some things based on packet code.
*/
switch (packet->code) {
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
if (!original) {
- fr_strerror_printf("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_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_COA_REQUEST:
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_COA_REQUEST:
memset(packet->vector, 0, sizeof(packet->vector));
break;
reply = packet->vps;
while (reply) {
size_t last_len;
- const char *last_name = NULL;
+ char const *last_name = NULL;
+
+ VERIFY_VP(reply);
/*
* Ignore non-wire attributes, but allow extended
* attributes.
*/
- if ((reply->vendor == 0) &&
- ((reply->attribute & 0xFFFF) >= 256) &&
- !reply->flags.extended && !reply->flags.long_extended) {
+ if ((reply->da->vendor == 0) &&
+ ((reply->da->attr & 0xFFFF) >= 256) &&
+ !reply->da->flags.extended && !reply->da->flags.long_extended) {
#ifndef NDEBUG
/*
* Permit the admin to send BADLY formatted
* attributes with a debug build.
*/
- if (reply->attribute == PW_RAW_ATTRIBUTE) {
+ if (reply->da->attr == PW_RAW_ATTRIBUTE) {
memcpy(ptr, reply->vp_octets, reply->length);
len = reply->length;
reply = reply->next;
* Set the Message-Authenticator to the correct
* length and initial value.
*/
- if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
+ if (reply->da->attr == PW_MESSAGE_AUTHENTICATOR) {
/*
* Cache the offset to the
* Message-Authenticator
} else {
last_len = reply->length;
}
- last_name = reply->name;
+ last_name = reply->da->name;
len = rad_vp2attr(packet, original, secret, &reply, ptr,
((uint8_t *) data) + sizeof(data) - ptr);
*/
if (len == 0) {
if (last_len != 0) {
- DEBUG("WARNING: Failed encoding attribute %s\n", last_name);
+ fr_strerror_printf("WARNING: Failed encoding attribute %s\n", last_name);
+ break;
} else {
- DEBUG("WARNING: Skipping zero-length attribute %s\n", last_name);
+ fr_strerror_printf("WARNING: Skipping zero-length attribute %s\n", last_name);
}
}
* memory for a request.
*/
packet->data_len = total_length;
- packet->data = (uint8_t *) malloc(packet->data_len);
+ packet->data = talloc_array(packet, uint8_t, packet->data_len);
if (!packet->data) {
fr_strerror_printf("Out of memory");
return -1;
/**
* @brief Sign a previously encoded packet.
*/
-int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret)
+int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret)
{
radius_packet_t *hdr = (radius_packet_t *)packet->data;
* It wasn't assigned an Id, this is bad!
*/
if (packet->id < 0) {
- fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id.");
+ fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id");
return -1;
}
uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
switch (packet->code) {
- case PW_ACCOUNTING_RESPONSE:
- if (original && original->code == PW_STATUS_SERVER) {
+ case PW_CODE_ACCOUNTING_RESPONSE:
+ if (original && original->code == PW_CODE_STATUS_SERVER) {
goto do_ack;
}
- case PW_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_DISCONNECT_ACK:
- case PW_DISCONNECT_NAK:
- case PW_COA_REQUEST:
- case PW_COA_ACK:
- case PW_COA_NAK:
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ case PW_CODE_COA_REQUEST:
+ case PW_CODE_COA_ACK:
memset(hdr->vector, 0, AUTH_VECTOR_LEN);
break;
do_ack:
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
if (!original) {
- fr_strerror_printf("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,
* into the Message-Authenticator
* attribute.
*/
- fr_hmac_md5(packet->data, packet->data_len,
- (const uint8_t *) secret, strlen(secret),
- calc_auth_vector);
+ fr_hmac_md5(calc_auth_vector, packet->data, packet->data_len,
+ (uint8_t const *) secret, strlen(secret));
memcpy(packet->data + packet->offset + 2,
calc_auth_vector, AUTH_VECTOR_LEN);
* Request packets are not signed, bur
* have a random authentication vector.
*/
- case PW_AUTHENTICATION_REQUEST:
- case PW_STATUS_SERVER:
+ case PW_CODE_ACCESS_REQUEST:
+ case PW_CODE_STATUS_SERVER:
break;
/*
uint8_t digest[16];
FR_MD5_CTX context;
- fr_MD5Init(&context);
- fr_MD5Update(&context, packet->data, packet->data_len);
- fr_MD5Update(&context, (const uint8_t *) secret,
+ fr_md5_init(&context);
+ fr_md5_update(&context, packet->data, packet->data_len);
+ fr_md5_update(&context, (uint8_t const *) secret,
strlen(secret));
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
memcpy(hdr->vector, digest, AUTH_VECTOR_LEN);
memcpy(packet->vector, digest, AUTH_VECTOR_LEN);
* @brief Reply to the request. Also attach
* reply attribute value pairs and any user message provided.
*/
-int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret)
+int rad_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret)
{
VALUE_PAIR *reply;
- const char *what;
+ char const *what;
char ip_src_buffer[128];
char ip_dst_buffer[128];
return 0;
}
- if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+ if (is_radius_code(packet->code)) {
what = fr_packet_codes[packet->code];
} else {
what = "Reply";
* the VP list again only for debugging.
*/
} else if (fr_debug_flag) {
- DEBUG("Sending %s of id %d from %s port %u to %s port %u\n", what,
- packet->id,
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
+ DEBUG("Sending %s Id %d from %s:%u to %s:%u\n", what,
+ packet->id,
+ inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
ip_src_buffer, sizeof(ip_src_buffer)),
packet->src_port,
- inet_ntop(packet->dst_ipaddr.af,
- &packet->dst_ipaddr.ipaddr,
+ inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr,
ip_dst_buffer, sizeof(ip_dst_buffer)),
packet->dst_port);
for (reply = packet->vps; reply; reply = reply->next) {
- if ((reply->vendor == 0) &&
- ((reply->attribute & 0xFFFF) > 0xff)) continue;
+ if ((reply->da->vendor == 0) &&
+ ((reply->da->attr & 0xFFFF) > 0xff)) continue;
debug_pair(reply);
}
}
if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
#endif
+#ifdef WITH_TCP
+ /*
+ * If the socket is TCP, call write(). Calling sendto()
+ * is allowed on some platforms, but it's not nice. Even
+ * worse, if UDPFROMTO is defined, we *can't* use it on
+ * TCP sockets. So... just call write().
+ */
+ if (packet->proto == IPPROTO_TCP) {
+ ssize_t rcode;
+
+ rcode = write(packet->sockfd, packet->data, packet->data_len);
+ if (rcode >= 0) return rcode;
+
+ fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
+ return -1;
+ }
+#endif
+
/*
* And send it on it's way.
*/
*
* http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf
*/
-int rad_digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
+int rad_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
{
int result = 0;
size_t i;
* @brief Validates the requesting client NAS. Calculates the
* Request Authenticator based on the clients private key.
*/
-static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
+static int calc_acctdigest(RADIUS_PACKET *packet, char const *secret)
{
uint8_t digest[AUTH_VECTOR_LEN];
FR_MD5_CTX context;
/*
* MD5(packet + secret);
*/
- fr_MD5Init(&context);
- fr_MD5Update(&context, packet->data, packet->data_len);
- fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
- fr_MD5Final(digest, &context);
+ fr_md5_init(&context);
+ fr_md5_update(&context, packet->data, packet->data_len);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(digest, &context);
/*
* Return 0 if OK, 2 if not OK.
* Response Authenticator based on the clients private key.
*/
static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
- const char *secret)
+ char const *secret)
{
uint8_t calc_digest[AUTH_VECTOR_LEN];
FR_MD5_CTX context;
/*
* MD5(packet + secret);
*/
- 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);
+ fr_md5_init(&context);
+ fr_md5_update(&context, packet->data, packet->data_len);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(calc_digest, &context);
/*
* Copy the packet's vector back to the packet.
/**
* @brief Check if a set of RADIUS formatted TLVs are OK.
*/
-int rad_tlv_ok(const uint8_t *data, size_t length,
+int rad_tlv_ok(uint8_t const *data, size_t length,
size_t dv_type, size_t dv_length)
{
- const uint8_t *end = data + length;
+ uint8_t const *end = data + length;
if ((dv_length > 2) || (dv_type == 0) || (dv_type > 4)) {
fr_strerror_printf("rad_tlv_ok: Invalid arguments");
break;
case 2:
- if ((data[1] == 0) && (data[1] == 0)) goto zero;
+ if ((data[0] == 0) && (data[1] == 0)) goto zero;
break;
case 1:
}
-/**
- * @brief See if the data pointed to by PTR is a valid RADIUS packet.
+/** See if the data pointed to by PTR is a valid RADIUS packet.
+ *
+ * Packet is not 'const * const' because we may update data_len, if there's more data
+ * in the UDP packet than in the RADIUS packet.
*
- * 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.
+ * @param packet to check
+ * @param flags to control decoding
+ * @param reason if not NULL, will have the failure reason written to where it points.
+ * @return bool, true on success, false on failure.
*/
-int rad_packet_ok(RADIUS_PACKET *packet, int flags)
+bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
{
uint8_t *attr;
size_t totallen;
int count;
radius_packet_t *hdr;
char host_ipaddr[128];
- int require_ma = 0;
- int seen_ma = 0;
- int num_attributes;
+ bool require_ma = false;
+ bool seen_ma = false;
+ uint32_t num_attributes;
+ decode_fail_t failure = DECODE_FAIL_NONE;
/*
* Check for packets smaller than the packet header.
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
packet->data_len, AUTH_HDR_LEN);
- return 0;
+ failure = DECODE_FAIL_MIN_LENGTH_PACKET;
+ goto finish;
}
*/
if ((hdr->code == 0) ||
(hdr->code >= FR_MAX_PACKET_CODE)) {
- fr_strerror_printf("WARNING: Bad RADIUS packet from host %s: unknown packet code%d ",
+ 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)),
hdr->code);
- return 0;
+ failure = DECODE_FAIL_UNKNOWN_PACKET_CODE;
+ goto finish;
}
/*
* Message-Authenticator is required in Status-Server
* packets, otherwise they can be trivially forged.
*/
- if (hdr->code == PW_STATUS_SERVER) require_ma = 1;
+ if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true;
/*
* It's also required if the caller asks for it.
*/
- if (flags) require_ma = 1;
+ if (flags) require_ma = true;
/*
* Repeat the length checks. This time, instead of
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
totallen, AUTH_HDR_LEN);
- return 0;
+ failure = DECODE_FAIL_MIN_LENGTH_FIELD;
+ goto finish;
}
/*
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
packet->data_len, totallen);
- return 0;
+ failure = DECODE_FAIL_MIN_LENGTH_MISMATCH;
+ goto finish;
}
/*
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
- return 0;
+ failure = DECODE_FAIL_HEADER_OVERFLOW;
+ goto finish;
}
/*
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
- return 0;
+ failure = DECODE_FAIL_INVALID_ATTRIBUTE;
+ goto finish;
}
/*
* Attributes are at LEAST as long as the ID & length
* fields. Anything shorter is an invalid attribute.
*/
- if (attr[1] < 2) {
+ if (attr[1] < 2) {
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)),
attr[0]);
- return 0;
+ failure = DECODE_FAIL_ATTRIBUTE_TOO_SHORT;
+ goto finish;
}
/*
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
attr[0]);
- return 0;
+ failure = DECODE_FAIL_ATTRIBUTE_OVERFLOW;
+ goto finish;
}
/*
* a Message-Authenticator.
*/
case PW_EAP_MESSAGE:
- require_ma = 1;
+ require_ma = true;
break;
case PW_MESSAGE_AUTHENTICATOR:
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
attr[1] - 2);
- return 0;
+ failure = DECODE_FAIL_MA_INVALID_LENGTH;
+ goto finish;
}
- seen_ma = 1;
+ seen_ma = true;
break;
}
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
- return 0;
+ failure = DECODE_FAIL_ATTRIBUTE_UNDERFLOW;
+ goto finish;
}
/*
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
num_attributes, fr_max_attributes);
- return 0;
+ failure = DECODE_FAIL_TOO_MANY_ATTRIBUTES;
+ goto finish;
}
/*
* Similarly, Status-Server packets MUST contain
* Message-Authenticator attributes.
*/
- if (require_ma && ! seen_ma) {
+ 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)));
- return 0;
+ failure = DECODE_FAIL_MA_MISSING;
+ goto finish;
}
/*
packet->id = hdr->id;
memcpy(packet->vector, hdr->vector, AUTH_VECTOR_LEN);
- return 1;
+
+ finish:
+
+ if (reason) {
+ *reason = failure;
+ }
+ return (failure == DECODE_FAIL_NONE);
}
/*
* Allocate the new request data structure
*/
- if ((packet = malloc(sizeof(*packet))) == NULL) {
+ packet = rad_alloc(NULL, false);
+ if (!packet) {
fr_strerror_printf("out of memory");
return NULL;
}
- memset(packet, 0, sizeof(*packet));
if (flags & 0x02) {
sock_flags = MSG_PEEK;
flags &= ~0x02;
}
- data_len = rad_recvfrom(fd, &packet->data, sock_flags,
- &packet->src_ipaddr, &packet->src_port,
- &packet->dst_ipaddr, &packet->dst_port);
+ data_len = rad_recvfrom(fd, packet, sock_flags,
+ &packet->src_ipaddr, &packet->src_port,
+ &packet->dst_ipaddr, &packet->dst_port);
/*
* Check for socket errors.
*/
if (data_len < 0) {
- fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+ fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
/* packet->data is NULL */
- free(packet);
+ rad_free(&packet);
return NULL;
}
packet->data_len = data_len; /* unsigned vs signed */
* packet.
*/
if (packet->data_len > MAX_PACKET_LEN) {
- fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+ fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
/* packet->data is NULL */
- free(packet);
+ rad_free(&packet);
return NULL;
}
* packet->data == NULL
*/
if ((packet->data_len == 0) || !packet->data) {
- fr_strerror_printf("Empty packet: Socket is not ready.");
- free(packet);
+ fr_strerror_printf("Empty packet: Socket is not ready");
+ rad_free(&packet);
return NULL;
}
/*
* See if it's a well-formed RADIUS packet.
*/
- if (!rad_packet_ok(packet, flags)) {
+ if (!rad_packet_ok(packet, flags, NULL)) {
rad_free(&packet);
return NULL;
}
packet->vps = NULL;
if (fr_debug_flag) {
- char host_ipaddr[128];
+ char src_ipaddr[128];
+ char dst_ipaddr[128];
- if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
- DEBUG("rad_recv: %s packet from host %s port %d",
+ if (is_radius_code(packet->code)) {
+ DEBUG("Received %s Id %d from %s:%d to %s:%d length %d\n",
fr_packet_codes[packet->code],
+ packet->id,
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
- host_ipaddr, sizeof(host_ipaddr)),
- packet->src_port);
+ src_ipaddr, sizeof(src_ipaddr)),
+ packet->src_port,
+ inet_ntop(packet->dst_ipaddr.af,
+ &packet->dst_ipaddr.ipaddr,
+ dst_ipaddr, sizeof(dst_ipaddr)),
+ packet->dst_port,
+ (int) packet->data_len);
} else {
- DEBUG("rad_recv: Packet from host %s port %d code=%d",
+ DEBUG("Received code %d Id %d from %s:%d to %s:%d length %d\n",
+ packet->code,
+ packet->id,
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
- host_ipaddr, sizeof(host_ipaddr)),
+ src_ipaddr, sizeof(src_ipaddr)),
packet->src_port,
- packet->code);
+ inet_ntop(packet->dst_ipaddr.af,
+ &packet->dst_ipaddr.ipaddr,
+ dst_ipaddr, sizeof(dst_ipaddr)),
+ packet->dst_port,
+ (int) packet->data_len);
}
- DEBUG(", id=%d, length=%d\n",
- packet->id, (int) packet->data_len);
}
#ifndef NDEBUG
* (and Message-Authenticator if present) of a packet.
*/
int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
- const char *secret)
+ char const *secret)
{
uint8_t *ptr;
int length;
default:
break;
- case PW_ACCOUNTING_RESPONSE:
+ case PW_CODE_ACCOUNTING_RESPONSE:
if (original &&
- (original->code == PW_STATUS_SERVER)) {
+ (original->code == PW_CODE_STATUS_SERVER)) {
goto do_ack;
}
- case PW_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_DISCONNECT_ACK:
- case PW_DISCONNECT_NAK:
- case PW_COA_REQUEST:
- case PW_COA_ACK:
- case PW_COA_NAK:
- memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_COA_REQUEST:
+ memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
break;
do_ack:
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ case PW_CODE_COA_ACK:
+ case PW_CODE_COA_NAK:
if (!original) {
- fr_strerror_printf("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;
}
- fr_hmac_md5(packet->data, packet->data_len,
- (const uint8_t *) secret, strlen(secret),
- calc_auth_vector);
+ fr_hmac_md5(calc_auth_vector, packet->data, packet->data_len,
+ (uint8_t const *) secret, strlen(secret));
if (rad_digest_cmp(calc_auth_vector, msg_auth_vector,
sizeof(calc_auth_vector)) != 0) {
char buffer[32];
/*
* Calculate and/or verify Request or Response Authenticator.
*/
- switch(packet->code) {
+ switch (packet->code) {
int rcode;
char buffer[32];
- case PW_AUTHENTICATION_REQUEST:
- case PW_STATUS_SERVER:
+ case PW_CODE_ACCESS_REQUEST:
+ case PW_CODE_STATUS_SERVER:
/*
* The authentication vector is random
* nonsense, invented by the client.
*/
break;
- case PW_COA_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_ACCOUNTING_REQUEST:
+ case PW_CODE_COA_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_ACCOUNTING_REQUEST:
if (calc_acctdigest(packet, secret) > 1) {
fr_strerror_printf("Received %s packet "
"from client %s with invalid Request Authenticator! (Shared secret is incorrect.)",
break;
/* Verify the reply digest */
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
- case PW_ACCOUNTING_RESPONSE:
- case PW_DISCONNECT_ACK:
- case PW_DISCONNECT_NAK:
- case PW_COA_ACK:
- case PW_COA_NAK:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ case PW_CODE_ACCOUNTING_RESPONSE:
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ case PW_CODE_COA_ACK:
+ case PW_CODE_COA_NAK:
rcode = calc_replydigest(packet, original, secret);
if (rcode > 1) {
fr_strerror_printf("Received %s packet "
/**
- * @brief Create a "raw" attribute from the attribute contents.
+ * @brief convert a "concatenated" attribute to one long VP.
*/
-static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
- UNUSED const RADIUS_PACKET *original,
- UNUSED const char *secret,
- unsigned int attribute, unsigned int vendor,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
+static ssize_t data2vp_concat(TALLOC_CTX *ctx,
+ DICT_ATTR const *da, uint8_t const *start,
+ size_t const packetlen, VALUE_PAIR **pvp)
{
+ size_t total;
+ uint8_t attr;
+ uint8_t const *ptr = start;
+ uint8_t const *end = start + packetlen;
+ uint8_t *p;
VALUE_PAIR *vp;
+ total = 0;
+ attr = ptr[0];
+
/*
- * Keep the next function happy.
+ * The packet has already been sanity checked, so we
+ * don't care about walking off of the end of it.
*/
- vp = pairalloc(NULL);
- vp = paircreate_raw(attribute, vendor, PW_TYPE_OCTETS, vp);
- if (!vp) {
- fr_strerror_printf("data2vp_raw: Failed creating attribute");
- return -1;
+ while (ptr < end) {
+ total += ptr[1] - 2;
+
+ ptr += ptr[1];
+
+ /*
+ * Attributes MUST be consecutive.
+ */
+ if (ptr[0] != attr) break;
}
- vp->length = length;
+ vp = pairalloc(ctx, da);
+ if (!vp) return -1;
- /*
- * If it's short, put it into the array. If it's too
- * long, flag it as such, and put it somewhere else;
- */
- if (length <= sizeof(vp->vp_octets)) {
- memcpy(vp->vp_octets, data, length);
- } else {
- vp->type |= PW_FLAG_LONG;
- vp->vp_tlv = malloc(length);
- if (!vp->vp_tlv) {
- pairfree(&vp);
- return -1;
- }
- memcpy(vp->vp_tlv, data, length);
+ vp->length = total;
+ vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
+ if (!p) {
+ pairfree(&vp);
+ return -1;
}
- *pvp = vp;
+ total = 0;
+ ptr = start;
+ while (total < vp->length) {
+ memcpy(p, ptr + 2, ptr[1] - 2);
+ p += ptr[1] - 2;
+ total += ptr[1] - 2;
+ ptr += ptr[1];
+ }
- return length;
+ *pvp = vp;
+ return ptr - start;
}
-static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- unsigned int attribute, unsigned int vendor,
- int nest,
- const uint8_t *start, size_t length,
- VALUE_PAIR **pvp);
-
/**
- * @brief Create any kind of VP from the attribute contents.
- * @return -1 on error, or "length".
+ * @brief convert TLVs to one or more VPs
*/
-static ssize_t data2vp_any(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, int nest,
- unsigned int attribute, unsigned int vendor,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
+static ssize_t data2vp_tlvs(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret, DICT_ATTR const *da,
+ uint8_t const *start, size_t length,
+ VALUE_PAIR **pvp)
{
- int data_offset = 0;
- DICT_ATTR *da;
- VALUE_PAIR *vp = NULL;
- uint8_t buffer[256];
-
- if (length == 0) {
- /*
- * Hacks for CUI. The WiMAX spec says that it
- * can be zero length, even though this is
- * forbidden by the RADIUS specs. So... we make
- * a special case for it.
- */
- if ((vendor == 0) &&
- (attribute == PW_CHARGEABLE_USER_IDENTITY)) {
- data = (const uint8_t *) "";
- length = 1;
- } else {
- *pvp = NULL;
- return 0;
- }
- }
-
- da = dict_attrbyvalue(attribute, vendor);
+ uint8_t const *data = start;
+ DICT_ATTR const *child;
+ VALUE_PAIR *head, **tail;
- /*
- * Unknown attribute. Create it as a "raw" attribute.
- */
- if (!da) {
- VP_TRACE("Not found %08x.%08x\n", vendor, attribute);
- raw:
- if (vp) pairfree(&vp);
- return data2vp_raw(packet, original, secret,
- attribute, vendor, data, length, pvp);
- }
+ if (length < 3) return -1; /* type, length, value */
- /*
- * TLVs are handled first. They can't be tagged, and
- * they can't be encrypted.
- */
- if (da->type == PW_TYPE_TLV) {
- VP_TRACE("Found TLV %08x.%08x\n", vendor, attribute);
- return data2vp_tlvs(packet, original, secret,
- attribute, vendor, nest,
- data, length, pvp);
- }
+ VP_HEXDUMP("tlvs", data, length);
- /*
- * The data is very long.
- */
- if (length > sizeof(vp->vp_octets)) {
- /*
- * Long encrypted attributes are forbidden.
- */
- if (da->flags.encrypt != FLAG_ENCRYPT_NONE) goto raw;
+ if (rad_tlv_ok(data, length, 1, 1) < 0) return -1;
-#ifndef NDEBUG
- /*
- * Catch programming errors.
- */
- if ((da->type != PW_TYPE_STRING) &&
- (da->type != PW_TYPE_OCTETS)) goto raw;
+ head = NULL;
+ tail = &head;
-#endif
+ while (data < (start + length)) {
+ ssize_t tlv_len;
- /*
- * FIXME: Figure out how to deal with long
- * strings and binary data!
- */
- goto raw;
- }
+ child = dict_attrbyparent(da, data[0], da->vendor);
+ if (!child) {
+ unsigned int my_attr, my_vendor;
- /*
- * The attribute is known, and well formed. We can now
- * create it. The main failure from here on in is being
- * out of memory.
- */
- vp = pairalloc(da);
- if (!vp) return -1;
+ VP_TRACE("Failed to find child %u of TLV %s\n",
+ data[0], da->name);
- /*
- * Handle tags.
- */
- 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.
+ * Get child attr/vendor so that
+ * we can call unknown attr.
*/
- vp->flags.tag = data[0];
+ my_attr = data[0];
+ my_vendor = da->vendor;
- if ((vp->type == PW_TYPE_STRING) ||
- (vp->type == PW_TYPE_OCTETS)) {
- if (length == 0) goto raw;
- data_offset = 1;
+ if (!dict_attr_child(da, &my_attr, &my_vendor)) {
+ pairfree(&head);
+ return -1;
}
- }
- }
-
- /*
- * Copy the data to be decrypted
- */
- vp->length = length - data_offset;
- memcpy(buffer, data + data_offset, vp->length);
- /*
- * Decrypt the attribute.
- */
- if (secret && packet) switch (vp->flags.encrypt) {
- /*
- * User-Password
- */
- case FLAG_ENCRYPT_USER_PASSWORD:
- if (original) {
- rad_pwdecode((char *) buffer,
- vp->length, secret,
- original->vector);
- } else {
- rad_pwdecode((char *) buffer,
- vp->length, secret,
- packet->vector);
- }
- buffer[253] = '\0';
- if (vp->attribute == PW_USER_PASSWORD) {
- vp->length = strlen((char *) buffer);
+ child = dict_attrunknown(my_attr, my_vendor, true);
+ if (!child) {
+ pairfree(&head);
+ return -1;
+ }
}
- break;
- /*
- * Tunnel-Password's may go ONLY
- * in response packets.
- */
- case FLAG_ENCRYPT_TUNNEL_PASSWORD:
- if (rad_tunnel_pwdecode(buffer, &vp->length, secret,
- original ? original->vector : nullvector) < 0)
- goto raw;
- break;
-
- /*
- * 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(buffer, my_digest,
- AUTH_VECTOR_LEN );
- buffer[AUTH_VECTOR_LEN] = '\0';
- vp->length = strlen((char *) buffer);
- }
- break;
-
- default:
- break;
- } /* switch over encryption flags */
-
-
- switch (vp->type) {
- case PW_TYPE_STRING:
- memcpy(vp->vp_strvalue, buffer, vp->length);
- vp->vp_strvalue[vp->length] = '\0';
- break;
-
- case PW_TYPE_OCTETS:
- case PW_TYPE_ABINARY:
- memcpy(vp->vp_octets, buffer, vp->length);
- break;
-
- case PW_TYPE_BYTE:
- if (vp->length != 1) goto raw;
-
- vp->vp_integer = buffer[0];
- break;
-
-
- case PW_TYPE_SHORT:
- if (vp->length != 2) goto raw;
-
- vp->vp_integer = (buffer[0] << 8) | buffer[1];
- break;
-
- case PW_TYPE_INTEGER:
- if (vp->length != 4) goto raw;
-
- memcpy(&vp->vp_integer, buffer, 4);
- vp->vp_integer = ntohl(vp->vp_integer);
-
- if (vp->flags.has_tag) vp->vp_integer &= 0x00ffffff;
- break;
-
- case PW_TYPE_INTEGER64:
- if (vp->length != 8) goto raw;
-
- /* vp_integer64 is a union with vp_octets */
- memcpy(&vp->vp_integer64, buffer, 8);
- vp->vp_integer64 = ntohll(vp->vp_integer64);
- break;
-
- case PW_TYPE_DATE:
- if (vp->length != 4) goto raw;
-
- memcpy(&vp->vp_date, buffer, 4);
- vp->vp_date = ntohl(vp->vp_date);
- break;
-
-
- case PW_TYPE_IPADDR:
- if (vp->length != 4) goto raw;
-
- memcpy(&vp->vp_ipaddr, buffer, 4);
- break;
-
- /*
- * IPv6 interface ID is 8 octets long.
- */
- case PW_TYPE_IFID:
- if (vp->length != 8) goto raw;
- memcpy(&vp->vp_ifid, buffer, 8);
- break;
-
- /*
- * IPv6 addresses are 16 octets long
- */
- case PW_TYPE_IPV6ADDR:
- if (vp->length != 16) goto raw;
- memcpy(&vp->vp_ipv6addr, buffer, 16);
- 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 (buffer[1] > 128) goto raw;
-
- /*
- * FIXME: double-check that
- * (vp->vp_octets[1] >> 3) matches vp->length + 2
- */
- memcpy(&vp->vp_ipv6prefix, buffer, vp->length);
- if (vp->length < 18) {
- memset(((uint8_t *)vp->vp_ipv6prefix) + vp->length, 0,
- 18 - vp->length);
- }
- break;
-
- case PW_TYPE_IPV4PREFIX:
- if (vp->length != sizeof(vp->vp_ipv4prefix)) goto raw;
-
- if ((buffer[1] & 0x3f) > 32) goto raw;
-
- memcpy(&vp->vp_ipv4prefix, buffer, sizeof(vp->vp_ipv4prefix));
-
- /*
- * /32 means "keep all bits". Otherwise, mask
- * them out.
- */
- if ((buffer[1] & 0x3f) > 32) {
- uint32_t addr, mask;
-
- memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
- mask = 1;
- mask <<= (32 - (buffer[1] & 0x3f));
- mask--;
- mask = ~mask;
- mask = htonl(mask);
- addr &= mask;
- memcpy(vp->vp_octets + 2, &addr, sizeof(addr));
- }
- break;
-
- case PW_TYPE_SIGNED:
- if (vp->length != 4) goto raw;
-
- /*
- * Overload vp_integer for ntohl, which takes
- * uint32_t, not int32_t
- */
- memcpy(&vp->vp_integer, buffer, 4);
- vp->vp_integer = ntohl(vp->vp_integer);
- break;
-
- case PW_TYPE_TLV:
- pairfree(&vp);
- fr_strerror_printf("data2vp_any: Internal sanity check failed");
- return -1;
-
- case PW_TYPE_COMBO_IP:
- if (vp->length == 4) {
- vp->type = PW_TYPE_IPADDR;
- memcpy(&vp->vp_ipaddr, buffer, 4);
- break;
-
- } else if (vp->length == 16) {
- vp->type = PW_TYPE_IPV6ADDR;
- memcpy(&vp->vp_ipv6addr, buffer, 16);
- break;
-
- }
- /* FALL-THROUGH */
-
- default:
- goto raw;
- }
-
- *pvp = vp;
+ tlv_len = data2vp(ctx, packet, original, secret, child,
+ data + 2, data[1] - 2, data[1] - 2, tail);
+ if (tlv_len < 0) {
+ pairfree(&head);
+ return -1;
+ }
+ tail = &((*tail)->next);
+ data += data[1];
+ }
+ *pvp = head;
return length;
}
-
/**
* @brief Convert a top-level VSA to a VP.
+ *
+ * "length" can be LONGER than just this sub-vsa
*/
-static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, unsigned int vendor,
- size_t dv_type, size_t dv_length,
- const uint8_t *data, size_t length,
+static ssize_t data2vp_vsa(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, DICT_VENDOR *dv,
+ uint8_t const *data, size_t length,
VALUE_PAIR **pvp)
{
unsigned int attribute;
ssize_t attrlen, my_len;
+ DICT_ATTR const *da;
#ifndef NDEBUG
- if (length <= (dv_type + dv_length)) {
- fr_strerror_printf("attr2vp_vsa: Failure to call rad_tlv_ok");
+ if (length <= (dv->type + dv->length)) {
+ fr_strerror_printf("data2vp_vsa: Failure to call rad_tlv_ok");
return -1;
}
-#endif
+#endif
- switch (dv_type) {
+ switch (dv->type) {
case 4:
/* data[0] must be zero */
attribute = data[1] << 16;
break;
default:
- fr_strerror_printf("attr2vp_vsa: Internal sanity check failed");
+ fr_strerror_printf("data2vp_vsa: Internal sanity check failed");
return -1;
}
- switch (dv_length) {
+ switch (dv->length) {
case 2:
- /* data[dv_type] must be zero */
- attrlen = data[dv_type + 1];
+ /* data[dv->type] must be zero, from rad_tlv_ok() */
+ attrlen = data[dv->type + 1];
break;
case 1:
- attrlen = data[dv_type];
+ attrlen = data[dv->type];
break;
case 0:
break;
default:
- fr_strerror_printf("attr2vp_vsa: Internal sanity check failed");
+ fr_strerror_printf("data2vp_vsa: Internal sanity check failed");
return -1;
}
#ifndef NDEBUG
- if (attrlen <= (ssize_t) (dv_type + dv_length)) {
- fr_strerror_printf("attr2vp_vsa: Failure to call rad_tlv_ok");
+ if (attrlen <= (ssize_t) (dv->type + dv->length)) {
+ fr_strerror_printf("data2vp_vsa: Failure to call rad_tlv_ok");
return -1;
}
#endif
- attrlen -= (dv_type + dv_length);
-
- my_len = data2vp_any(packet, original, secret, 0,
- attribute, vendor,
- data + dv_type + dv_length, attrlen, pvp);
- if (my_len < 0) return my_len;
+ /*
+ * See if the VSA is known.
+ */
+ da = dict_attrbyvalue(attribute, dv->vendorpec);
+ if (!da) da = dict_attrunknown(attribute, dv->vendorpec, true);
+ if (!da) return -1;
-#ifndef NDEBUG
- if (my_len != attrlen) {
- pairfree(pvp);
- fr_strerror_printf("attr2vp_vsa: Incomplete decode %d != %d",
- (int) my_len, (int) attrlen);
- return -1;
- }
-#endif
+ my_len = data2vp(ctx, packet, original, secret, da,
+ data + dv->type + dv->length,
+ attrlen - (dv->type + dv->length),
+ attrlen - (dv->type + dv->length),
+ pvp);
+ if (my_len < 0) return my_len;
- return dv_type + dv_length + attrlen;
+ return attrlen;
}
+
/**
- * @brief Convert one or more TLVs to VALUE_PAIRs. This function can
- * be called recursively...
+ * @brief Convert a fragmented extended attr to a VP
+ *
+ * Format is:
+ *
+ * attr
+ * length
+ * extended-attr
+ * flag
+ * data...
+ *
+ * But for the first fragment, we get passed a pointer to the
+ * "extended-attr".
*/
-static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- unsigned int attribute, unsigned int vendor,
- int nest,
- const uint8_t *start, size_t length,
- VALUE_PAIR **pvp)
+static ssize_t data2vp_extended(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, DICT_ATTR const *da,
+ uint8_t const *data,
+ size_t attrlen, size_t packetlen,
+ VALUE_PAIR **pvp)
{
- size_t dv_type, dv_length;
- const uint8_t *data, *end;
- const DICT_ATTR *parent, *da;
- VALUE_PAIR *head, **last, *vp;
- DICT_VENDOR *dv;
-
- data = start;
+ ssize_t rcode;
+ size_t fraglen;
+ uint8_t *head, *tail;
+ uint8_t const *frag, *end;
+ uint8_t const *attr;
+ int fragments;
+ bool last_frag;
+
+ if (attrlen < 3) return -1;
+
+ /*
+ * Calculate the length of all of the fragments. For
+ * now, they MUST be contiguous in the packet, and they
+ * MUST be all of the same TYPE and EXTENDED-TYPE
+ */
+ attr = data - 2;
+ fraglen = attrlen - 2;
+ frag = data + attrlen;
+ end = data + packetlen;
+ fragments = 1;
+ last_frag = false;
+
+ while (frag < end) {
+ if (last_frag ||
+ (frag[0] != attr[0]) ||
+ (frag[1] < 4) || /* too short for long-extended */
+ (frag[2] != attr[2]) ||
+ ((frag + frag[1]) > end)) { /* overflow */
+ end = frag;
+ break;
+ }
- /*
- * The default format for a VSA is the RFC recommended
- * format.
- */
- dv_type = 1;
- dv_length = 1;
+ last_frag = ((frag[3] & 0x80) == 0);
- parent = dict_attrbyvalue(attribute, vendor);
- if (!parent) {
- raw:
- return data2vp_raw(packet, original, secret,
- attribute, vendor, data, length, pvp);
+ fraglen += frag[1] - 4;
+ frag += frag[1];
+ fragments++;
}
- /*
- * This works because vendors with non-standard VSA
- * format don't have sub-TLVs.
- */
- dv = dict_vendorbyvalue(vendor);
- if (dv) {
- dv_type = dv->type;
- dv_length = dv->length;
- /* dict.c enforces sane values on the above fields */
- }
+ head = tail = malloc(fraglen);
+ if (!head) return -1;
+
+ VP_TRACE("Fragments %d, total length %d\n", fragments, (int) fraglen);
/*
- * The VSAs do not exactly fill the data,
- * The *entire* TLV is malformed.
+ * And again, but faster and looser.
+ *
+ * We copy the first fragment, followed by the rest of
+ * the fragments.
*/
- if (rad_tlv_ok(data, length, dv_type, dv_length) < 0) {
- VP_TRACE("TLV malformed %08x.%08x\n", vendor, attribute);
- goto raw;
+ frag = attr;
+
+ while (fragments > 0) {
+ memcpy(tail, frag + 4, frag[1] - 4);
+ tail += frag[1] - 4;
+ frag += frag[1];
+ fragments--;
}
- end = data + length;
- head = NULL;
- last = &head;
+ VP_HEXDUMP("long-extended fragments", head, fraglen);
- while (data < end) {
- unsigned int my_attr;
- unsigned int my_len;
+ rcode = data2vp(ctx, packet, original, secret, da,
+ head, fraglen, fraglen, pvp);
+ free(head);
+ if (rcode < 0) return rcode;
-#ifndef NDEBUG
- if ((data + dv_type + dv_length) > end) {
- fr_strerror_printf("data2vp_tlvs: Internal sanity check failed in tlvs: Insufficient data");
- pairfree(&head);
- return -1;
- }
-#endif
+ return end - data;
+}
- switch (dv_type) {
- case 1:
- da = dict_attrbyparent(parent, data[0]);
- if (!da) {
- VP_TRACE("No parent for %u\n", data[0]);
- goto raw;
- }
- my_attr = da->attr;
- break;
- case 2:
- my_attr = (data[0] << 8) | data[1];
- break;
+/**
+ * @brief Convert a Vendor-Specific WIMAX to vps
+ *
+ * Called ONLY for Vendor-Specific
+ */
+static ssize_t data2vp_wimax(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret, uint32_t vendor,
+ uint8_t const *data,
+ size_t attrlen, size_t packetlen,
+ VALUE_PAIR **pvp)
+{
+ ssize_t rcode;
+ size_t fraglen;
+ bool last_frag;
+ uint8_t *head, *tail;
+ uint8_t const *frag, *end;
+ DICT_ATTR const *child;
- case 4:
- my_attr = (data[1] << 16) | (data[1] << 8) | data[3];
- break;
+ if (attrlen < 8) return -1;
- default:
- fr_strerror_printf("data2vp_tlvs: Internal sanity check failed");
- return -1;
- }
+ if (((size_t) (data[5] + 4)) != attrlen) return -1;
- switch (dv_length) {
- case 0:
- my_len = length;
- break;
+ child = dict_attrbyvalue(data[4], vendor);
+ if (!child) return -1;
- case 1:
- case 2:
- my_len = data[dv_type + dv_length - 1];
- break;
+ if ((data[6] & 0x80) == 0) {
+ rcode = data2vp(ctx, packet, original, secret, child,
+ data + 7, data[5] - 3, data[5] - 3,
+ pvp);
+ if (rcode < 0) return -1;
+ return 7 + rcode;
+ }
- default:
- fr_strerror_printf("data2vp_tlvs: Internal sanity check failed");
- return -1;
- }
-
-#ifndef NDEBUG
- if (my_len < (dv_type + dv_length)) {
- fr_strerror_printf("data2vp_tlvs: Internal sanity check failed in tlvs: underflow");
- pairfree(&head);
- return -1;
+ /*
+ * Calculate the length of all of the fragments. For
+ * now, they MUST be contiguous in the packet, and they
+ * MUST be all of the same VSA, WiMAX, and WiMAX-attr.
+ *
+ * The first fragment doesn't have a RADIUS attribute
+ * header, so it needs to be treated a little special.
+ */
+ fraglen = data[5] - 3;
+ frag = data + attrlen;
+ end = data + packetlen;
+ last_frag = false;
+
+ while (frag < end) {
+ if (last_frag ||
+ (frag[0] != PW_VENDOR_SPECIFIC) ||
+ (frag[1] < 9) || /* too short for wimax */
+ ((frag + frag[1]) > end) || /* overflow */
+ (memcmp(frag + 2, data, 4) != 0) || /* not wimax */
+ (frag[6] != data[4]) || /* not the same wimax attr */
+ ((frag[7] + 6) != frag[1])) { /* doesn't fill the attr */
+ end = frag;
+ break;
}
- if ((data + my_len) > end) {
- fr_strerror_printf("data2vp_tlvs: Internal sanity check failed in tlvs: overflow");
- pairfree(&head);
- return -1;
- }
-#endif
+ last_frag = ((frag[8] & 0x80) == 0);
- my_len -= dv_type + dv_length;
+ fraglen += frag[7] - 3;
+ frag += frag[1];
+ }
- /*
- * If this returns > 0, it returns "my_len"
- */
- if (data2vp_any(packet, original, secret, nest + 1,
- my_attr, vendor,
- data + dv_type + dv_length, my_len, &vp) < 0) {
- pairfree(&head);
- return -1;
- }
+ head = tail = malloc(fraglen);
+ if (!head) return -1;
- data += my_len + dv_type + dv_length;
- *last = vp;
+ /*
+ * And again, but faster and looser.
+ *
+ * We copy the first fragment, followed by the rest of
+ * the fragments.
+ */
+ frag = data;
- while (vp) {
- last = &(vp->next);
- vp = vp->next;
- }
- }
+ memcpy(tail, frag + 4 + 3, frag[4 + 1] - 3);
+ tail += frag[4 + 1] - 3;
+ frag += attrlen; /* should be frag[1] - 7 */
- *pvp = head;
- return data - start;
+ /*
+ * frag now points to RADIUS attributes
+ */
+ do {
+ memcpy(tail, frag + 2 + 4 + 3, frag[2 + 4 + 1] - 3);
+ tail += frag[2 + 4 + 1] - 3;
+ frag += frag[1];
+ } while (frag < end);
+
+ VP_HEXDUMP("wimax fragments", head, fraglen);
+
+ rcode = data2vp(ctx, packet, original, secret, child,
+ head, fraglen, fraglen, pvp);
+ free(head);
+ if (rcode < 0) return rcode;
+
+ return end - data;
}
/**
- * @brief Group "continued" attributes together, and create VPs from them.
- *
- * The caller ensures that the RADIUS packet is OK, and that the
- * continuations have all been checked.
+ * @brief Convert a top-level VSA to one or more VPs
*/
-static ssize_t data2vp_continued(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const uint8_t *start, size_t length,
- VALUE_PAIR **pvp, int nest,
- unsigned int attribute, unsigned int vendor,
- int first_offset, int later_offset,
- ssize_t attrlen)
+static ssize_t data2vp_vsas(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
+ RADIUS_PACKET const *original,
+ char const *secret, uint8_t const *data,
+ size_t attrlen, size_t packetlen,
+ VALUE_PAIR **pvp)
{
- ssize_t left;
- uint8_t *attr, *ptr;
- const uint8_t *data;
+ size_t total;
+ ssize_t rcode;
+ uint32_t vendor;
+ DICT_VENDOR *dv;
+ VALUE_PAIR *head, **tail;
- attr = malloc(attrlen);
- if (!attr) {
- fr_strerror_printf("Out of memory");
- return -1;
+ if (attrlen > packetlen) return -1;
+ if (attrlen < 5) return -1; /* vid, value */
+ if (data[0] != 0) return -1; /* we require 24-bit VIDs */
+
+ memcpy(&vendor, data, 4);
+ vendor = ntohl(vendor);
+ dv = dict_vendorbyvalue(vendor);
+ if (!dv) return -1;
+
+ /*
+ * WiMAX craziness
+ */
+ if ((vendor == VENDORPEC_WIMAX) && dv->flags) {
+ rcode = data2vp_wimax(ctx, packet, original, secret, vendor,
+ data, attrlen, packetlen, pvp);
+ return rcode;
}
- left = attrlen;
- ptr = attr;
- data = start;
+ /*
+ * VSAs should normally be in TLV format.
+ */
+ if (rad_tlv_ok(data + 4, attrlen - 4,
+ dv->type, dv->length) < 0) return -1;
/*
- * Do the first one.
+ * There may be more than one VSA in the
+ * Vendor-Specific. If so, loop over them all.
*/
- memcpy(ptr, data + first_offset, data[1] - first_offset);
- ptr += data[1] - first_offset;
- left -= data[1] - first_offset;
- data += data[1];
+ data += 4;
+ attrlen -= 4;
+ packetlen -= 4;
+ total = 4;
+ head = NULL;
+ tail = &head;
- while (left > 0) {
-#ifndef NDEBUG
- if (data >= (start + length)) {
- free(attr);
- fr_strerror_printf("data2vp_continued: Internal sanity check failed");
+ while (attrlen > 0) {
+ ssize_t vsa_len;
+
+ vsa_len = data2vp_vsa(ctx, packet, original, secret, dv,
+ data, attrlen, tail);
+ if (vsa_len < 0) {
+ pairfree(&head);
+ fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
-#endif
- memcpy(ptr, data + later_offset, data[1] - later_offset);
- ptr += data[1] - later_offset;
- left -= data[1] - later_offset;
- data += data[1];
+ tail = &((*tail)->next);
+ data += vsa_len;
+ attrlen -= vsa_len;
+ packetlen -= vsa_len;
+ total += vsa_len;
}
- left = data2vp_any(packet, original, secret, nest,
- attribute, vendor,
- attr, attrlen, pvp);
- free(attr);
- if (left < 0) return left;
-
- return data - start;
+ *pvp = head;
+ return total;
}
/**
- * @brief Create a "raw" VALUE_PAIR from a RADIUS attribute.
+ * @brief Create any kind of VP from the attribute contents.
+ *
+ * "length" is AT LEAST the length of this attribute, as we
+ * expect the caller to have verified the data with
+ * rad_packet_ok(). "length" may be up to the length of the
+ * packet.
+ *
+ * @return -1 on error, or "length".
*/
-ssize_t rad_attr2vp_raw(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
+ssize_t data2vp(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret,
+ DICT_ATTR const *da, uint8_t const *start,
+ size_t const attrlen, size_t const packetlen,
+ VALUE_PAIR **pvp)
{
- ssize_t my_len;
+ int8_t tag = TAG_NONE;
+ size_t datalen;
+ ssize_t rcode;
+ uint32_t vendor;
+ DICT_ATTR const *child;
+ DICT_VENDOR *dv;
+ VALUE_PAIR *vp;
+ uint8_t const *data = start;
+ char *p;
+ uint8_t buffer[256];
- if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
- fr_strerror_printf("rad_attr2vp_raw: Invalid length");
+ /*
+ * FIXME: Attrlen can be larger than 253 for extended attrs!
+ */
+ if (!da || (attrlen > packetlen) ||
+ ((attrlen > 253) && (attrlen != packetlen)) ||
+ (attrlen > 128*1024)) {
+ fr_strerror_printf("data2vp: invalid arguments");
return -1;
}
- my_len = data2vp_raw(packet, original, secret, data[0], 0,
- data + 2, data[1] - 2, pvp);
- if (my_len < 0) return my_len;
-
- return data[1];
-}
-
+ VP_HEXDUMP("data2vp", start, attrlen);
-/**
- * @brief Get the length of the data portion of all of the contiguous
- * continued attributes.
- * @return
- * 0 for "no continuation"
- * -1 on malformed packets (continuation followed by non-wimax, etc.)
- */
-static ssize_t wimax_attrlen(uint32_t vendor,
- const uint8_t *start, const uint8_t *end)
-{
- ssize_t total;
- const uint8_t *data = start;
+ VP_TRACE("parent %s len %zu ... %zu\n", da->name, attrlen, packetlen);
- if ((data[8] & 0x80) == 0) return 0;
- total = data[7] - 3;
- data += data[1];
+ datalen = attrlen;
- while (data < end) {
-
- if ((data + 9) > end) return -1;
+ /*
+ * Hacks for CUI. The WiMAX spec says that it can be
+ * zero length, even though this is forbidden by the
+ * RADIUS specs. So... we make a special case for it.
+ */
+ if (attrlen == 0) {
+ if (!((da->vendor == 0) &&
+ (da->attr == PW_CHARGEABLE_USER_IDENTITY))) {
+ *pvp = NULL;
+ return 0;
+ }
- if ((data[0] != PW_VENDOR_SPECIFIC) ||
- (data[1] < 9) ||
- (memcmp(data + 2, &vendor, 4) != 0) ||
- (data[6] != start[6]) ||
- ((data[7] + 6) != data[1])) return -1;
+#ifndef NDEBUG
+ /*
+ * Hacks for Coverity. Editing the dictionary
+ * will break assumptions about CUI. We know
+ * this, but Coverity doesn't.
+ */
+ if (da->type != PW_TYPE_OCTETS) return -1;
+#endif
- total += data[7] - 3;
- if ((data[8] & 0x80) == 0) break;
- data += data[1];
+ data = NULL;
+ datalen = 0;
+ goto alloc_cui; /* skip everything */
}
- return total;
-}
-
+ /*
+ * Hacks for tags. If the attribute is capable of
+ * encoding a tag, and there's room for the tag, and
+ * there is a tag, or it's encrypted with Tunnel-Password,
+ * then decode the tag.
+ */
+ if (da->flags.has_tag && (datalen > 1) &&
+ ((data[0] < 0x20) ||
+ (da->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD))) {
+ /*
+ * Only "short" attributes can be encrypted.
+ */
+ if (datalen >= sizeof(buffer)) return -1;
-/**
- * @brief Get the length of the data portion of all of the contiguous
- * continued attributes.
- *
- * @return
- * 0 for "no continuation"
- * -1 on malformed packets (continuation followed by non-wimax, etc.)
- */
-static ssize_t extended_attrlen(const uint8_t *start, const uint8_t *end)
-{
- ssize_t total;
- const uint8_t *data = start;
+ if (da->type == PW_TYPE_STRING) {
+ memcpy(buffer, data + 1, datalen - 1);
+ tag = data[0];
+ datalen -= 1;
- if ((data[3] & 0x80) == 0) return 0;
- total = data[1] - 4;
- data += data[1];
-
- while (data < end) {
- if ((data + 4) > end) return -1;
+ } else if (da->type == PW_TYPE_INTEGER) {
+ memcpy(buffer, data, attrlen);
+ tag = buffer[0];
+ buffer[0] = 0;
- if ((data[0] != start[0]) ||
- (data[1] < 4) ||
- (data[2] != start[2])) return -1;
+ } else {
+ return -1; /* only string and integer can have tags */
+ }
- total += data[1] - 4;
- if ((data[3] & 0x80) == 0) break;
- data += data[1];
+ data = buffer;
}
- return total;
-}
+ /*
+ * Decrypt the attribute.
+ */
+ if (secret && packet && (da->flags.encrypt != FLAG_ENCRYPT_NONE)) {
+ /*
+ * Encrypted attributes can only exist for the
+ * old-style format. Extended attributes CANNOT
+ * be encrypted.
+ */
+ if (attrlen > 253) {
+ return -1;
+ }
+ if (data == start) {
+ memcpy(buffer, data, attrlen);
+ }
+ data = buffer;
-/**
- * @brief Create WiMAX VALUE_PAIRs from a RADIUS attribute.
- */
-ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- ssize_t my_len;
- unsigned int attribute;
- uint32_t lvalue;
+ switch (da->flags.encrypt) { /* can't be tagged */
+ /*
+ * User-Password
+ */
+ case FLAG_ENCRYPT_USER_PASSWORD:
+ if (original) {
+ rad_pwdecode((char *) buffer,
+ attrlen, secret,
+ original->vector);
+ } else {
+ rad_pwdecode((char *) buffer,
+ attrlen, secret,
+ packet->vector);
+ }
+ buffer[253] = '\0';
+ datalen = strlen((char *) buffer);
+ break;
- if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
- fr_strerror_printf("rad_attr2vp_wimax: Invalid length");
- return -1;
- }
+ /*
+ * Tunnel-Password's may go ONLY in response
+ * packets. They can have a tag, so datalen is
+ * not the same as attrlen.
+ */
+ case FLAG_ENCRYPT_TUNNEL_PASSWORD:
+ if (rad_tunnel_pwdecode(buffer, &datalen, secret,
+ original ? original->vector : nullvector) < 0) {
+ goto raw;
+ }
+ break;
- if (data[0] != PW_VENDOR_SPECIFIC) {
- fr_strerror_printf("rad_attr2vp_wimax: Invalid attribute");
- return -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(buffer, my_digest,
+ AUTH_VECTOR_LEN );
+ buffer[AUTH_VECTOR_LEN] = '\0';
+ datalen = strlen((char *) buffer);
+ }
+ break;
+
+ default:
+ break;
+ } /* switch over encryption flags */
}
/*
- * Not enough room for a Vendor-Id. + WiMAX header
+ * Double-check the length after decrypting the
+ * attribute.
*/
- if (data[1] < 9) {
- return rad_attr2vp_raw(packet, original, secret,
- data, length, pvp);
- }
+ switch (da->type) {
+ case PW_TYPE_STRING:
+ case PW_TYPE_OCTETS:
+ break;
- memcpy(&lvalue, data + 2, 4);
- lvalue = ntohl(lvalue);
+ case PW_TYPE_ABINARY:
+ if (datalen > sizeof(vp->vp_filter)) goto raw;
+ break;
- /*
- * Not WiMAX format.
- */
- if (lvalue != VENDORPEC_WIMAX) {
- DICT_VENDOR *dv;
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_IPV4_ADDR:
+ case PW_TYPE_DATE:
+ case PW_TYPE_SIGNED:
+ if (datalen != 4) goto raw;
+ break;
- dv = dict_vendorbyvalue(lvalue);
- if (!dv || !dv->flags) {
- fr_strerror_printf("rad_attr2vp_wimax: Not a WiMAX attribute");
- return -1;
+ case PW_TYPE_INTEGER64:
+ case PW_TYPE_IFID:
+ if (datalen != 8) goto raw;
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ if (datalen != 16) goto raw;
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
+ if ((datalen < 2) || (datalen > 18)) goto raw;
+ if (data[1] > 128) goto raw;
+ break;
+
+ case PW_TYPE_BYTE:
+ if (datalen != 1) goto raw;
+ break;
+
+ case PW_TYPE_SHORT:
+ if (datalen != 2) goto raw;
+ break;
+
+ case PW_TYPE_ETHERNET:
+ if (datalen != 6) goto raw;
+ break;
+
+ case PW_TYPE_IP_ADDR:
+ if (datalen == 4) {
+ child = dict_attrbytype(da->attr, da->vendor,
+ PW_TYPE_IPV4_ADDR);
+ } else if (datalen == 16) {
+ child = dict_attrbytype(da->attr, da->vendor,
+ PW_TYPE_IPV6_ADDR);
+ } else {
+ goto raw;
}
- }
+ if (!child) goto raw;
+ da = child; /* re-write it */
+ break;
+
+ case PW_TYPE_IPV4_PREFIX:
+ if (datalen != 6) goto raw;
+ if ((data[1] & 0x3f) > 32) goto raw;
+ break;
+
+ /*
+ * The rest of the data types can cause
+ * recursion! Ask yourself, "is recursion OK?"
+ */
- /*
- * The WiMAX attribute is encapsulated in a VSA. If the
- * WiMAX length disagrees with the VSA length, it's malformed.
- */
- if ((data[7] + 6) != data[1]) {
- return rad_attr2vp_raw(packet, original, secret,
- data, length, pvp);
- }
+ case PW_TYPE_EXTENDED:
+ if (datalen < 2) goto raw; /* etype, value */
- attribute = data[6];
+ child = dict_attrbyparent(da, data[0], 0);
+ if (!child) goto raw;
- /*
- * Attribute is continued. Do some more work.
- */
- if (data[8] != 0) {
- my_len = wimax_attrlen(htonl(lvalue), data, data + length);
- if (my_len < 0) {
- return rad_attr2vp_raw(packet, original, secret,
- data, length, pvp);
- }
+ /*
+ * Recurse to decode the contents, which could be
+ * a TLV, IPaddr, etc. Note that we decode only
+ * the current attribute, and we ignore any extra
+ * data after it.
+ */
+ rcode = data2vp(ctx, packet, original, secret, child,
+ data + 1, attrlen - 1, attrlen - 1, pvp);
+ if (rcode < 0) goto raw;
+ return 1 + rcode;
+
+ case PW_TYPE_LONG_EXTENDED:
+ if (datalen < 3) goto raw; /* etype, flags, value */
+
+ child = dict_attrbyparent(da, data[0], 0);
+ if (!child) {
+ if ((data[0] != PW_VENDOR_SPECIFIC) ||
+ (datalen < (3 + 4 + 1))) {
+ /* da->attr < 255, da->vendor == 0 */
+ child = dict_attrunknown(data[0], da->attr * FR_MAX_VENDOR, true);
+ } else {
+ /*
+ * Try to find the VSA.
+ */
+ memcpy(&vendor, data + 3, 4);
+ vendor = ntohl(vendor);
- return data2vp_continued(packet, original, secret,
- data, length, pvp, 0,
- data[6], lvalue,
- 9, 9, my_len);
- }
+ if (vendor == 0) goto raw;
- my_len = data2vp_any(packet, original, secret, 0, attribute, lvalue,
- data + 9, data[1] - 9, pvp);
- if (my_len < 0) return my_len;
+ child = dict_attrunknown(data[7], vendor | (da->attr * FR_MAX_VENDOR), true);
+ }
- return data[1];
-}
+ if (!child) {
+ fr_strerror_printf("Internal sanity check %d", __LINE__);
+ return -1;
+ }
+ }
-/**
- * @brief Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
- */
-ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- size_t dv_type, dv_length;
- ssize_t my_len;
- uint32_t lvalue;
- DICT_VENDOR *dv;
+ /*
+ * If there no more fragments, then the contents
+ * have to be a well-known data type.
+ *
+ */
+ if ((data[1] & 0x80) == 0) {
+ rcode = data2vp(ctx, packet, original, secret, child,
+ data + 2, attrlen - 2, attrlen - 2,
+ pvp);
+ if (rcode < 0) goto raw;
+ return 2 + rcode;
+ }
- if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
- fr_strerror_printf("rad_attr2vp_vsa: Invalid length");
- return -1;
- }
+ /*
+ * This requires a whole lot more work.
+ */
+ return data2vp_extended(ctx, packet, original, secret, child,
+ start, attrlen, packetlen, pvp);
- if (data[0] != PW_VENDOR_SPECIFIC) {
- fr_strerror_printf("rad_attr2vp_vsa: Invalid attribute");
- return -1;
- }
+ case PW_TYPE_EVS:
+ if (datalen < 6) goto raw; /* vid, vtype, value */
- /*
- * Not enough room for a Vendor-Id.
- * Or the high octet of the Vendor-Id is set.
- */
- if ((data[1] < 6) || (data[2] != 0)) {
- return rad_attr2vp_raw(packet, original, secret,
- data, length, pvp);
- }
+ if (data[0] != 0) goto raw; /* we require 24-bit VIDs */
- memcpy(&lvalue, data + 2, 4);
- lvalue = ntohl(lvalue);
+ memcpy(&vendor, data, 4);
+ vendor = ntohl(vendor);
+ dv = dict_vendorbyvalue(vendor);
+ if (!dv) {
+ child = dict_attrunknown(data[4], da->vendor | vendor, true);
+ } else {
+ child = dict_attrbyparent(da, data[4], vendor);
+ if (!child) {
+ child = dict_attrunknown(data[4], da->vendor | vendor, true);
+ }
+ }
+ if (!child) goto raw;
- /*
- * WiMAX gets its own set of magic.
- */
- if (lvalue == VENDORPEC_WIMAX) {
- wimax:
- return rad_attr2vp_wimax(packet, original, secret,
- data, length, pvp);
- }
+ rcode = data2vp(ctx, packet, original, secret, child,
+ data + 5, attrlen - 5, attrlen - 5, pvp);
+ if (rcode < 0) goto raw;
+ return 5 + rcode;
- dv_type = dv_length = 1;
- dv = dict_vendorbyvalue(lvalue);
- if (dv) {
- dv_type = dv->type;
- dv_length = dv->length;
+ case PW_TYPE_TLV:
+ /*
+ * We presume that the TLVs all fit into one
+ * attribute, OR they've already been grouped
+ * into a contiguous memory buffer.
+ */
+ rcode = data2vp_tlvs(ctx, packet, original, secret, da,
+ data, attrlen, pvp);
+ if (rcode < 0) goto raw;
+ return rcode;
- if (dv->flags) goto wimax;
- }
+ case PW_TYPE_VSA:
+ /*
+ * VSAs can be WiMAX, in which case they don't
+ * fit into one attribute.
+ */
+ rcode = data2vp_vsas(ctx, packet, original, secret,
+ data, attrlen, packetlen, pvp);
+ if (rcode < 0) goto raw;
+ return rcode;
- /*
- * Attribute is not in the correct form.
- */
- if (rad_tlv_ok(data + 6, data[1] - 6, dv_type, dv_length) < 0) {
- return rad_attr2vp_raw(packet, original, secret,
- data, length, pvp);
+ default:
+ raw:
+ /*
+ * Re-write the attribute to be "raw". It is
+ * therefore of type "octets", and will be
+ * handled below.
+ */
+ da = dict_attrunknown(da->attr, da->vendor, true);
+ if (!da) {
+ fr_strerror_printf("Internal sanity check %d", __LINE__);
+ return -1;
+ }
+ tag = TAG_NONE;
+#ifndef NDEBUG
+ /*
+ * Fix for Coverity.
+ */
+ if (da->type != PW_TYPE_OCTETS) {
+ dict_attr_free(&da);
+ return -1;
+ }
+#endif
+ break;
}
- my_len = attr2vp_vsa(packet, original, secret,
- lvalue, dv_type, dv_length,
- data + 6, data[1] - 6, pvp);
- if (my_len < 0) return my_len;
-
/*
- * Incomplete decode means that something is wrong
- * with the attribute. Back up, and make it "raw".
+ * And now that we've verified the basic type
+ * information, decode the actual data.
*/
- if (my_len != (data[1] - 6)) {
- pairfree(pvp);
- return rad_attr2vp_raw(packet, original, secret,
- data, length, pvp);
- }
+ alloc_cui:
+ vp = pairalloc(ctx, da);
+ if (!vp) return -1;
- return data[1];
-}
+ vp->length = datalen;
+ vp->tag = tag;
-/**
- * @brief Create an "extended" VALUE_PAIR from a RADIUS attribute.
- */
-ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const uint8_t *start, size_t length,
- VALUE_PAIR **pvp)
-{
- unsigned int attribute;
- int continued = 0;
- unsigned int vendor = 0;
- size_t data_len = length;
- const uint8_t *data;
- DICT_ATTR *da;
+ switch (da->type) {
+ case PW_TYPE_STRING:
+ p = talloc_array(vp, char, vp->length + 1);
+ memcpy(p, data, vp->length);
+ p[vp->length] = '\0';
+ vp->vp_strvalue = p;
+ break;
- data = start;
+ case PW_TYPE_OCTETS:
+ pairmemcpy(vp, data, vp->length);
+ break;
- if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
- fr_strerror_printf("rad_attr2vp_extended: Invalid length");
- return -1;
- }
+ case PW_TYPE_ABINARY:
+ if (vp->length > sizeof(vp->vp_filter)) {
+ vp->length = sizeof(vp->vp_filter);
+ }
+ memcpy(vp->vp_filter, data, vp->length);
+ break;
- da = dict_attrbyvalue(data[0], 0);
- if (!da || !da->flags.extended) {
- fr_strerror_printf("rad_attr2vp_extended: Attribute is not extended format");
- return -1;
- }
+ case PW_TYPE_BYTE:
+ vp->vp_byte = data[0];
+ break;
- data = start;
+ case PW_TYPE_SHORT:
+ vp->vp_short = (data[0] << 8) | data[1];
+ break;
- /*
- * No Extended-Type. It's a raw attribute.
- * Also, if there's no data following the Extended-Type,
- * it's a raw attribute.
- */
- if (data[1] <= 3) {
- raw:
- return rad_attr2vp_raw(packet, original, secret, start,
- length, pvp);
- }
+ case PW_TYPE_INTEGER:
+ memcpy(&vp->vp_integer, data, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
+ break;
- /*
- * The attribute is "241.1", for example. Go look that
- * up to see what type it is.
- */
- vendor = data[0] * FR_MAX_VENDOR;
- attribute = data[2];
+ case PW_TYPE_INTEGER64:
+ memcpy(&vp->vp_integer64, data, 8);
+ vp->vp_integer64 = ntohll(vp->vp_integer64);
+ break;
- da = dict_attrbyvalue(attribute, vendor);
- if (!da) goto raw;
+ case PW_TYPE_DATE:
+ memcpy(&vp->vp_date, data, 4);
+ vp->vp_date = ntohl(vp->vp_date);
+ break;
- data_len = length;
- if (data[1] < length) data_len = data[1];
+ case PW_TYPE_ETHERNET:
+ memcpy(&vp->vp_ether, data, 6);
+ break;
- data += 3;
- data_len -= 3;
+ case PW_TYPE_IPV4_ADDR:
+ memcpy(&vp->vp_ipaddr, data, 4);
+ break;
- /*
- * If there's supposed to be a flag octet. If not, it's
- * a raw attribute. If the flag is set, it's supposed to
- * be continued.
- */
- if (da->flags.long_extended) {
- if (data_len == 0) goto raw;
+ case PW_TYPE_IFID:
+ memcpy(&vp->vp_ifid, data, 8);
+ break;
- continued = ((data[0] & 0x80) != 0);
- data++;
- data_len--;
- }
-
- /*
- * Extended VSAs have 4 octets of
- * Vendor-Id followed by one octet of
- * Vendor-Type.
- */
- if (da->flags.evs) {
- if (data_len < 5) goto raw;
-
- /*
- * Vendor Ids can only be 24-bit.
- */
- if (data[0] != 0) goto raw;
-
- vendor |= ((data[1] << 16) |
- (data[2] << 8) |
- data[3]);
-
+ case PW_TYPE_IPV6_ADDR:
+ memcpy(&vp->vp_ipv6addr, data, 16);
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
/*
- * Over-write the attribute with the
- * VSA.
+ * FIXME: double-check that
+ * (vp->vp_octets[1] >> 3) matches vp->length + 2
*/
- attribute = data[4];
- data += 5;
- data_len -= 5;
- }
-
- if (continued) {
- int first_offset = 4;
- ssize_t my_len;
+ memcpy(&vp->vp_ipv6prefix, data, vp->length);
+ if (vp->length < 18) {
+ memset(((uint8_t *)vp->vp_ipv6prefix) + vp->length, 0,
+ 18 - vp->length);
+ }
+ break;
- if (da->flags.evs) first_offset += 5;
+ case PW_TYPE_IPV4_PREFIX:
+ /* FIXME: do the same double-check as for IPv6Prefix */
+ memcpy(&vp->vp_ipv4prefix, data, vp->length);
- my_len = extended_attrlen(start, start + length);
- if (my_len < 0) goto raw;
+ /*
+ * /32 means "keep all bits". Otherwise, mask
+ * them out.
+ */
+ if ((data[1] & 0x3f) > 32) {
+ uint32_t addr, mask;
- if (da->flags.evs) my_len -= 5;
+ memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
+ mask = 1;
+ mask <<= (32 - (data[1] & 0x3f));
+ mask--;
+ mask = ~mask;
+ mask = htonl(mask);
+ addr &= mask;
+ memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr));
+ }
+ break;
- return data2vp_continued(packet, original, secret,
- start, length, pvp, 0,
- attribute, vendor,
- first_offset, 4, my_len);
- }
+ case PW_TYPE_SIGNED: /* overloaded with vp_integer */
+ memcpy(&vp->vp_integer, buffer, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
+ break;
- if (data2vp_any(packet, original, secret, 0,
- attribute, vendor, data, data_len, pvp) < 0) {
+ default:
+ pairfree(&vp);
+ fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
+ vp->type = VT_DATA;
+ *pvp = vp;
- return (data + data_len) - start;
+ return attrlen;
}
/**
- * @brief Create a "standard" RFC VALUE_PAIR from the given data.
- */
-ssize_t rad_attr2vp_rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
- fr_strerror_printf("rad_attr2vp_rfc: Insufficient data");
- return -1;
- }
-
- if (data2vp_any(packet, original, secret, 0,
- data[0], 0, data + 2, data[1] - 2, pvp) < 0) {
- return -1;
- }
-
- return data[1];
-}
-
-/**
* @brief Create a "normal" VALUE_PAIR from the given data.
*/
-ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const uint8_t *data, size_t length,
+ssize_t rad_attr2vp(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret,
+ uint8_t const *data, size_t length,
VALUE_PAIR **pvp)
{
- DICT_ATTR *da;
+ ssize_t rcode;
+
+ DICT_ATTR const *da;
if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
fr_strerror_printf("rad_attr2vp: Insufficient data");
return -1;
}
+ da = dict_attrbyvalue(data[0], 0);
+ if (!da) da = dict_attrunknown(data[0], 0, true);
+ if (!da) return -1;
+
/*
- * VSAs get their own handler.
+ * Pass the entire thing to the decoding function
*/
- if (data[0] == PW_VENDOR_SPECIFIC) {
- return rad_attr2vp_vsa(packet, original, secret,
- data, length, pvp);
+ if (da->flags.concat) {
+ return data2vp_concat(ctx, da, data, length, pvp);
}
/*
- * Extended attribute format gets their own handler.
+ * Note that we pass the entire length, not just the
+ * length of this attribute. The Extended or WiMAX
+ * attributes may have the "continuation" bit set, and
+ * will thus be more than one attribute in length.
*/
- da = dict_attrbyvalue(data[0], 0);
- if (da && da->flags.extended) {
- return rad_attr2vp_extended(packet, original, secret,
- data, length, pvp);
- }
-
- return rad_attr2vp_rfc(packet, original, secret, data, length, pvp);
-}
-
-
-/**
- * @brief Converts data in network byte order to a VP
- * @return -1 on error, or the length of the data read
- */
-ssize_t rad_data2vp(unsigned int attribute, unsigned int vendor,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- if (!data || (length == 0) || !pvp) return -1;
+ rcode = data2vp(ctx, packet, original, secret, da,
+ data + 2, data[1] - 2, length - 2, pvp);
+ if (rcode < 0) return rcode;
- return data2vp_any(NULL, NULL, NULL, 0,
- attribute, vendor, data, length, pvp);
+ return 2 + rcode;
}
+fr_thread_local_setup(uint8_t *, rad_vp2data_buff);
-/**
- * @brief Converts vp_data to network byte order
+/** Converts vp_data to network byte order
+ *
+ * Provide a pointer to a buffer which contains the value of the VALUE_PAIR
+ * in an architecture independent format.
+ *
+ * The pointer is only guaranteed to be valid between calls to rad_vp2data, and so long
+ * as the source VALUE_PAIR is not freed.
+ *
+ * @param out where to write the pointer to the value.
+ * @param vp to get the value from.
* @return -1 on error, or the length of the value
*/
-ssize_t rad_vp2data(const VALUE_PAIR *vp, uint8_t *out, size_t outlen)
+ssize_t rad_vp2data(uint8_t const **out, VALUE_PAIR const *vp)
{
- size_t len = 0;
+ uint8_t *buffer;
uint32_t lvalue;
uint64_t lvalue64;
- len = vp->length;
- if (outlen < len) {
- fr_strerror_printf("ERROR: rad_vp2data buffer passed too small");
- return -1;
+ *out = NULL;
+
+ buffer = fr_thread_local_init(rad_vp2data_buff, free);
+ if (!buffer) {
+ int ret;
+
+ buffer = malloc(sizeof(uint8_t) * sizeof(value_data_t));
+ if (!buffer) {
+ fr_strerror_printf("Failed allocating memory for rad_vp2data buffer");
+ return -1;
+ }
+
+ ret = fr_thread_local_set(rad_vp2data_buff, buffer);
+ if (ret != 0) {
+ fr_strerror_printf("Failed setting up TLS for rad_vp2data buffer: %s", strerror(errno));
+ free(buffer);
+ return -1;
+ }
}
-
+
+ VERIFY_VP(vp);
+
+ switch(vp->da->type) {
+ case PW_TYPE_STRING:
+ case PW_TYPE_OCTETS:
+ case PW_TYPE_TLV:
+ memcpy(out, &vp->data.ptr, sizeof(*out));
+ break;
+
/*
- * Short-circuit it for long attributes.
+ * All of these values are at the same location.
*/
- if ((vp->type & PW_FLAG_LONG) != 0) goto do_raw;
+ case PW_TYPE_IFID:
+ case PW_TYPE_IPV4_ADDR:
+ case PW_TYPE_IPV6_ADDR:
+ case PW_TYPE_IPV6_PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
+ case PW_TYPE_ABINARY:
+ case PW_TYPE_ETHERNET:
+ case PW_TYPE_IP_ADDR:
+ case PW_TYPE_IP_PREFIX:
+ {
+ void const *p = &vp->data;
+ memcpy(out, &p, sizeof(*out));
+ break;
+ }
- switch(vp->type) {
- case PW_TYPE_STRING:
- case PW_TYPE_OCTETS:
- case PW_TYPE_IFID:
- case PW_TYPE_IPADDR:
- case PW_TYPE_IPV6ADDR:
- case PW_TYPE_IPV6PREFIX:
- case PW_TYPE_IPV4PREFIX:
- case PW_TYPE_ABINARY:
- case PW_TYPE_TLV:
- do_raw:
- memcpy(out, vp->vp_octets, len);
- break;
- case PW_TYPE_BYTE:
- out[0] = vp->vp_integer & 0xff;
- break;
-
- case PW_TYPE_SHORT:
- out[0] = (vp->vp_integer >> 8) & 0xff;
- out[1] = vp->vp_integer & 0xff;
- break;
-
- case PW_TYPE_INTEGER:
- lvalue = htonl(vp->vp_integer);
- memcpy(out, &lvalue, sizeof(lvalue));
- break;
-
- case PW_TYPE_INTEGER64:
- lvalue64 = htonll(vp->vp_integer64);
- memcpy(out, &lvalue64, sizeof(lvalue64));
- break;
+ case PW_TYPE_BOOLEAN:
+ buffer[0] = vp->vp_integer & 0x01;
+ *out = buffer;
+ break;
- case PW_TYPE_DATE:
- lvalue = htonl(vp->vp_date);
- memcpy(out, &lvalue, sizeof(lvalue));
- break;
-
- case PW_TYPE_SIGNED:
- {
- int32_t slvalue;
-
- slvalue = htonl(vp->vp_signed);
- memcpy(out, &slvalue, sizeof(slvalue));
- break;
- }
- /* unknown type: ignore it */
- default:
- fr_strerror_printf("ERROR: Unknown attribute type %d",
- vp->type);
- return -1;
+ case PW_TYPE_BYTE:
+ buffer[0] = vp->vp_integer & 0xff;
+ *out = buffer;
+ break;
+
+ case PW_TYPE_SHORT:
+ buffer[0] = (vp->vp_integer >> 8) & 0xff;
+ buffer[1] = vp->vp_integer & 0xff;
+ *out = buffer;
+ break;
+
+ case PW_TYPE_INTEGER:
+ lvalue = htonl(vp->vp_integer);
+ memcpy(buffer, &lvalue, sizeof(lvalue));
+ *out = buffer;
+ break;
+
+ case PW_TYPE_INTEGER64:
+ lvalue64 = htonll(vp->vp_integer64);
+ memcpy(buffer, &lvalue64, sizeof(lvalue64));
+ *out = buffer;
+ break;
+
+ case PW_TYPE_DATE:
+ lvalue = htonl(vp->vp_date);
+ memcpy(buffer, &lvalue, sizeof(lvalue));
+ *out = buffer;
+ break;
+
+ case PW_TYPE_SIGNED:
+ {
+ int32_t slvalue = htonl(vp->vp_signed);
+ memcpy(buffer, &slvalue, sizeof(slvalue));
+ *out = buffer;
+ break;
+ }
+
+ case PW_TYPE_INVALID:
+ case PW_TYPE_EXTENDED:
+ case PW_TYPE_LONG_EXTENDED:
+ case PW_TYPE_EVS:
+ case PW_TYPE_VSA:
+ case PW_TYPE_TIMEVAL:
+ case PW_TYPE_MAX:
+ fr_strerror_printf("Cannot get data for VALUE_PAIR type %i", vp->da->type);
+ return -1;
+
+ /* Don't add default */
}
-
- return len;
+
+ return vp->length;
}
/**
* @return -1 on decoding error, 0 on success
*/
int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
- const char *secret)
+ char const *secret)
{
int packet_length;
- int num_attributes;
+ uint32_t num_attributes;
uint8_t *ptr;
radius_packet_t *hdr;
VALUE_PAIR *head, **tail, *vp;
/*
* This may return many VPs
*/
- my_len = rad_attr2vp(packet, original, secret,
+ my_len = rad_attr2vp(packet, packet, original, secret,
ptr, packet_length, &vp);
if (my_len < 0) {
pairfree(&head);
* random pool.
*/
fr_rand_seed(packet->data, AUTH_HDR_LEN);
-
+
/*
* There may be VP's already in the packet. Don't
* destroy them. Instead, add the decoded attributes to
* int *pwlen is updated to the new length of the encrypted
* password - a multiple of 16 bytes.
*/
-int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
- const uint8_t *vector)
+int rad_pwencode(char *passwd, size_t *pwlen, char const *secret,
+ uint8_t const *vector)
{
FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, secretlen);
old = context; /* save intermediate work */
/*
*/
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n == 0) {
- fr_MD5Update(&context, vector, AUTH_PASS_LEN);
- fr_MD5Final(digest, &context);
+ fr_md5_update(&context, vector, AUTH_PASS_LEN);
+ fr_md5_final(digest, &context);
} else {
context = old;
- fr_MD5Update(&context,
+ fr_md5_update(&context,
(uint8_t *) passwd + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
}
for (i = 0; i < AUTH_PASS_LEN; i++) {
/**
* @brief Decode password.
*/
-int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
- const uint8_t *vector)
+int rad_pwdecode(char *passwd, size_t pwlen, char const *secret,
+ uint8_t const *vector)
{
FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, secretlen);
old = context; /* save intermediate work */
/*
*/
for (n = 0; n < pwlen; n += AUTH_PASS_LEN) {
if (n == 0) {
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Final(digest, &context);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_final(digest, &context);
context = old;
if (pwlen > AUTH_PASS_LEN) {
- fr_MD5Update(&context, (uint8_t *) passwd,
+ fr_md5_update(&context, (uint8_t *) passwd,
AUTH_PASS_LEN);
}
} else {
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
context = old;
if (pwlen > (n + AUTH_PASS_LEN)) {
- fr_MD5Update(&context, (uint8_t *) passwd + n,
+ fr_md5_update(&context, (uint8_t *) passwd + n,
AUTH_PASS_LEN);
}
}
* This is per RFC-2868 which adds a two char SALT to the initial intermediate
* value MD5 hash.
*/
-int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret,
- const uint8_t *vector)
+int rad_tunnel_pwencode(char *passwd, size_t *pwlen, char const *secret,
+ uint8_t const *vector)
{
uint8_t buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 3];
unsigned char digest[AUTH_VECTOR_LEN];
* initial intermediate value, to differentiate it from the
* above.
*/
-int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
- const uint8_t *vector)
+int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret,
+ uint8_t const *vector)
{
FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, secretlen);
old = context; /* save intermediate work */
/*
*
* b(1) = MD5(secret + vector + salt)
*/
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Update(&context, passwd, 2);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(&context, passwd, 2);
reallen = 0;
for (n = 0; n < len; n += AUTH_PASS_LEN) {
int base = 0;
if (n == 0) {
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
context = old;
return -1;
}
- fr_MD5Update(&context, passwd + 2, AUTH_PASS_LEN);
+ fr_md5_update(&context, passwd + 2, AUTH_PASS_LEN);
base = 1;
} else {
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
context = old;
- fr_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN);
+ fr_md5_update(&context, passwd + n + 2, AUTH_PASS_LEN);
}
for (i = base; i < AUTH_PASS_LEN; i++) {
*
* May be called any number of times.
*/
-void fr_rand_seed(const void *data, size_t size)
+void fr_rand_seed(void const *data, size_t size)
{
uint32_t hash;
sizeof(fr_rand_pool.randrsl) - total);
if ((this < 0) && (errno != EINTR)) break;
if (this > 0) total += this;
- }
+ }
close(fd);
} else {
fr_rand_pool.randrsl[0] = fd;
}
-/**
- * @brief Allocate a new RADIUS_PACKET
+/** Allocate a new RADIUS_PACKET
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ * the packet is not associated with a REQUEST.
+ * @param new_vector if true a new request authenticator will be generated.
+ * @return a new RADIUS_PACKET or NULL on error.
*/
-RADIUS_PACKET *rad_alloc(int newvector)
+RADIUS_PACKET *rad_alloc(TALLOC_CTX *ctx, bool new_vector)
{
RADIUS_PACKET *rp;
- if ((rp = malloc(sizeof(RADIUS_PACKET))) == NULL) {
+ rp = talloc_zero(ctx, RADIUS_PACKET);
+ if (!rp) {
fr_strerror_printf("out of memory");
return NULL;
}
- memset(rp, 0, sizeof(*rp));
rp->id = -1;
rp->offset = -1;
- if (newvector) {
+ if (new_vector) {
int i;
uint32_t hash, base;
return rp;
}
-RADIUS_PACKET *rad_alloc_reply(RADIUS_PACKET *packet)
+/** Allocate a new RADIUS_PACKET response
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ * the packet is not associated with a REQUEST.
+ * @param packet The request packet.
+ * @return a new RADIUS_PACKET or NULL on error.
+ */
+RADIUS_PACKET *rad_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet)
{
RADIUS_PACKET *reply;
if (!packet) return NULL;
- reply = rad_alloc(0);
+ reply = rad_alloc(ctx, false);
if (!reply) return NULL;
/*
reply->data = NULL;
reply->data_len = 0;
+#ifdef WITH_TCP
+ reply->proto = packet->proto;
+#endif
return reply;
}
if (!radius_packet_ptr || !*radius_packet_ptr) return;
radius_packet = *radius_packet_ptr;
- free(radius_packet->data);
+ VERIFY_PACKET(radius_packet);
pairfree(&radius_packet->vps);
- free(radius_packet);
-
+ talloc_free(radius_packet);
*radius_packet_ptr = NULL;
}
+
+/** Duplicate a RADIUS_PACKET
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ * the packet is not associated with a REQUEST.
+ * @param in The packet to copy
+ * @return a new RADIUS_PACKET or NULL on error.
+ */
+RADIUS_PACKET *rad_copy_packet(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
+{
+ RADIUS_PACKET *out;
+
+ out = rad_alloc(ctx, false);
+ if (!out) return NULL;
+
+ /*
+ * Bootstrap by copying everything.
+ */
+ memcpy(out, in, sizeof(*out));
+
+ /*
+ * Then reset necessary fields
+ */
+ out->sockfd = -1;
+
+ out->data = NULL;
+ out->data_len = 0;
+
+ out->vps = paircopy(out, in->vps);
+ out->offset = 0;
+
+ return out;
+}