* @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(const char *msg, const uint8_t *data, size_t len)
+static void VP_HEXDUMP(char const *msg, uint8_t const *data, size_t len)
{
size_t i;
* 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;
socklen_t sizeof_src = sizeof(src);
(void) recvfrom(sockfd, header, sizeof(header), 0,
- (struct sockaddr *)&src, &sizeof_src);
+ (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];
*/
if (data_len < 4) {
rad_recv_discard(sockfd);
-
+
return 1;
} else { /* we got 4 bytes of data. */
*/
if (packet_len < AUTH_HDR_LEN) {
rad_recv_discard(sockfd);
-
+
return 1;
/*
*/
} else if (packet_len > MAX_PACKET_LEN) {
rad_recv_discard(sockfd);
-
+
return 1;
}
}
*/
if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
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.
}
}
- 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;
*/
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;
-
+ 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) {
- const DICT_ATTR *da;
-
+ 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);
}
* @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
/*
* 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->da->type & PW_FLAG_LONG) != 0) goto do_tlv;
-
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->da->type);
+ fr_strerror_printf("ERROR: Unknown attribute type %d", vp->da->type);
return -1;
}
*/
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->da->name);
return -1;
}
- if (lvalue) ptr[0] = vp->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->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;
/*
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;
+
+ VERIFY_VP(vp);
if (!vp->da->flags.extended) {
fr_strerror_printf("rad_vp2extended called for non-extended attribute");
if (!vp->da->flags.long_extended) {
if (room < 3) return 0;
-
+
ptr[1] = 3;
ptr[2] = vp->da->attr & 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];
+ evs[4] = vp->da->attr & fr_attr_mask[0];
ptr[1] += 5;
}
}
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->da->flags.long_extended) {
fprintf(fr_log_fp, "%02x ", ptr[2]);
-
+
} else {
fprintf(fr_log_fp, "%02x %02x ", ptr[2], ptr[3]);
jump = 4;
/**
* @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.
}
/**
+ * @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,
* 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.
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.
*/
/**
* @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->da->vendor != 0) {
fr_strerror_printf("rad_vp2rfc called with VSA");
*/
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;
}
+ /*
+ * 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;
+
+ VERIFY_VP(vp);
if (!vp->da->flags.is_tlv) {
fr_strerror_printf("rad_vp2rfctlv: attr is not a TLV");
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.
*/
/**
* @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
*/
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);
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");
}
-/**
- * @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;
}
&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
*/
- packet = rad_alloc(NULL, 0);
+ packet = rad_alloc(NULL, false);
if (!packet) {
fr_strerror_printf("out of memory");
return NULL;
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 */
rad_free(&packet);
return NULL;
* 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 */
rad_free(&packet);
return NULL;
* packet->data == NULL
*/
if ((packet->data_len == 0) || !packet->data) {
- fr_strerror_printf("Empty packet: Socket is not ready.");
+ 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 "
}
-static ssize_t data2vp(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const DICT_ATTR *da, const uint8_t *start,
- const size_t attrlen, const size_t packetlen,
- VALUE_PAIR **pvp);
+/**
+ * @brief convert a "concatenated" attribute to one long VP.
+ */
+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];
+
+ /*
+ * The packet has already been sanity checked, so we
+ * don't care about walking off of the end of it.
+ */
+ while (ptr < end) {
+ total += ptr[1] - 2;
+
+ ptr += ptr[1];
+
+ /*
+ * Attributes MUST be consecutive.
+ */
+ if (ptr[0] != attr) break;
+ }
+
+ vp = pairalloc(ctx, da);
+ if (!vp) return -1;
+
+ vp->length = total;
+ vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
+ if (!p) {
+ pairfree(&vp);
+ return -1;
+ }
+
+ 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];
+ }
+
+ *pvp = vp;
+ return ptr - start;
+}
+
/**
* @brief convert TLVs to one or more VPs
*/
-static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const DICT_ATTR *da,
- const uint8_t *start, size_t length,
+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)
{
- const uint8_t *data = start;
- const DICT_ATTR *child;
+ uint8_t const *data = start;
+ DICT_ATTR const *child;
VALUE_PAIR *head, **tail;
if (length < 3) return -1; /* type, length, value */
return -1;
}
- child = dict_attrunknown(my_attr, my_vendor, TRUE);
+ child = dict_attrunknown(my_attr, my_vendor, true);
if (!child) {
pairfree(&head);
return -1;
}
}
- tlv_len = data2vp(packet, original, secret, child,
+ tlv_len = data2vp(ctx, packet, original, secret, child,
data + 2, data[1] - 2, data[1] - 2, tail);
if (tlv_len < 0) {
pairfree(&head);
*
* "length" can be LONGER than just this sub-vsa
*/
-static ssize_t data2vp_vsa(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, DICT_VENDOR *dv,
- 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;
- const DICT_ATTR *da;
+ DICT_ATTR const *da;
#ifndef NDEBUG
if (length <= (dv->type + dv->length)) {
fr_strerror_printf("data2vp_vsa: Failure to call rad_tlv_ok");
return -1;
}
-#endif
+#endif
switch (dv->type) {
case 4:
* See if the VSA is known.
*/
da = dict_attrbyvalue(attribute, dv->vendorpec);
- if (!da) da = dict_attrunknown(attribute, dv->vendorpec, TRUE);
+ if (!da) da = dict_attrunknown(attribute, dv->vendorpec, true);
if (!da) return -1;
- my_len = data2vp(packet, original, secret, da,
+ my_len = data2vp(ctx, packet, original, secret, da,
data + dv->type + dv->length,
attrlen - (dv->type + dv->length),
attrlen - (dv->type + dv->length),
/**
+ * @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_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)
+{
+ 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;
+ }
+
+ last_frag = ((frag[3] & 0x80) == 0);
+
+ fraglen += frag[1] - 4;
+ frag += frag[1];
+ fragments++;
+ }
+
+ head = tail = malloc(fraglen);
+ if (!head) return -1;
+
+ VP_TRACE("Fragments %d, total length %d\n", fragments, (int) fraglen);
+
+ /*
+ * And again, but faster and looser.
+ *
+ * We copy the first fragment, followed by the rest of
+ * the fragments.
+ */
+ frag = attr;
+
+ while (fragments > 0) {
+ memcpy(tail, frag + 4, frag[1] - 4);
+ tail += frag[1] - 4;
+ frag += frag[1];
+ fragments--;
+ }
+
+ VP_HEXDUMP("long-extended fragments", head, fraglen);
+
+ rcode = data2vp(ctx, packet, original, secret, da,
+ head, fraglen, fraglen, pvp);
+ free(head);
+ if (rcode < 0) return rcode;
+
+ return end - data;
+}
+
+/**
* @brief Convert a Vendor-Specific WIMAX to vps
*
* Called ONLY for Vendor-Specific
*/
-static ssize_t data2vp_wimax(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, uint32_t vendor,
- const uint8_t *data,
+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;
- const uint8_t *frag, *end;
- const DICT_ATTR *child;
+ uint8_t const *frag, *end;
+ DICT_ATTR const *child;
if (attrlen < 8) return -1;
if (!child) return -1;
if ((data[6] & 0x80) == 0) {
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 7, data[5] - 3, data[5] - 3,
pvp);
if (rcode < 0) return -1;
fraglen = data[5] - 3;
frag = data + attrlen;
end = data + packetlen;
+ last_frag = false;
while (frag < end) {
- int last_frag = FALSE;
-
if (last_frag ||
(frag[0] != PW_VENDOR_SPECIFIC) ||
(frag[1] < 9) || /* too short for wimax */
VP_HEXDUMP("wimax fragments", head, fraglen);
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
head, fraglen, fraglen, pvp);
free(head);
if (rcode < 0) return rcode;
/**
* @brief Convert a top-level VSA to one or more VPs
*/
-static ssize_t data2vp_vsas(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret, const uint8_t *data,
+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)
{
* WiMAX craziness
*/
if ((vendor == VENDORPEC_WIMAX) && dv->flags) {
- rcode = data2vp_wimax(packet, original, secret, vendor,
+ rcode = data2vp_wimax(ctx, packet, original, secret, vendor,
data, attrlen, packetlen, pvp);
return rcode;
}
-
+
/*
* VSAs should normally be in TLV format.
*/
while (attrlen > 0) {
ssize_t vsa_len;
-
- vsa_len = data2vp_vsa(packet, original, secret, dv,
+
+ vsa_len = data2vp_vsa(ctx, packet, original, secret, dv,
data, attrlen, tail);
if (vsa_len < 0) {
pairfree(&head);
*
* @return -1 on error, or "length".
*/
-static ssize_t data2vp(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const char *secret,
- const DICT_ATTR *da, const uint8_t *start,
- const size_t attrlen, const size_t packetlen,
- 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)
{
- int tag = 0;
+ int8_t tag = TAG_NONE;
size_t datalen;
ssize_t rcode;
uint32_t vendor;
- const DICT_ATTR *child;
+ DICT_ATTR const *child;
DICT_VENDOR *dv;
VALUE_PAIR *vp;
- const uint8_t *data = start;
+ uint8_t const *data = start;
+ char *p;
uint8_t buffer[256];
- if (!da || (attrlen > 253) || (attrlen > packetlen) ||
+ /*
+ * 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;
* will break assumptions about CUI. We know
* this, but Coverity doesn't.
*/
- if (da->type != PW_TYPE_STRING) return -1;
+ if (da->type != PW_TYPE_OCTETS) return -1;
#endif
- data = (const uint8_t *) "";
- datalen = 1;
+ data = NULL;
+ datalen = 0;
goto alloc_cui; /* skip everything */
}
/*
* 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 encryted with Tunnel-Password,
+ * 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;
if (da->type == PW_TYPE_STRING) {
memcpy(buffer, data + 1, datalen - 1);
* Decrypt the attribute.
*/
if (secret && packet && (da->flags.encrypt != FLAG_ENCRYPT_NONE)) {
- if (data == start) memcpy(buffer, data, attrlen);
+ /*
+ * 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;
switch (da->flags.encrypt) { /* can't be tagged */
switch (da->type) {
case PW_TYPE_STRING:
case PW_TYPE_OCTETS:
+ break;
+
case PW_TYPE_ABINARY:
+ if (datalen > sizeof(vp->vp_filter)) goto raw;
break;
case PW_TYPE_INTEGER:
- case PW_TYPE_IPADDR:
+ case PW_TYPE_IPV4_ADDR:
case PW_TYPE_DATE:
case PW_TYPE_SIGNED:
if (datalen != 4) goto raw;
if (datalen != 8) goto raw;
break;
- case PW_TYPE_IPV6ADDR:
+ case PW_TYPE_IPV6_ADDR:
if (datalen != 16) goto raw;
break;
- case PW_TYPE_IPV6PREFIX:
+ case PW_TYPE_IPV6_PREFIX:
if ((datalen < 2) || (datalen > 18)) goto raw;
if (data[1] > 128) goto raw;
break;
if (datalen != 6) goto raw;
break;
- case PW_TYPE_COMBO_IP:
+ case PW_TYPE_IP_ADDR:
if (datalen == 4) {
child = dict_attrbytype(da->attr, da->vendor,
- PW_TYPE_IPADDR);
+ PW_TYPE_IPV4_ADDR);
} else if (datalen == 16) {
child = dict_attrbytype(da->attr, da->vendor,
- PW_TYPE_IPV6ADDR);
+ PW_TYPE_IPV6_ADDR);
} else {
goto raw;
}
da = child; /* re-write it */
break;
- case PW_TYPE_IPV4PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
if (datalen != 6) goto raw;
if ((data[1] & 0x3f) > 32) goto raw;
break;
* the current attribute, and we ignore any extra
* data after it.
*/
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 1, attrlen - 1, attrlen - 1, pvp);
if (rcode < 0) goto raw;
return 1 + rcode;
if (datalen < 3) goto raw; /* etype, flags, value */
child = dict_attrbyparent(da, data[0], 0);
- if (!child) goto raw;
+ 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);
+
+ if (vendor == 0) goto raw;
+
+ child = dict_attrunknown(data[7], vendor | (da->attr * FR_MAX_VENDOR), true);
+ }
+
+ if (!child) {
+ fr_strerror_printf("Internal sanity check %d", __LINE__);
+ return -1;
+ }
+ }
/*
* If there no more fragments, then the contents
*
*/
if ((data[1] & 0x80) == 0) {
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 2, attrlen - 2, attrlen - 2,
pvp);
if (rcode < 0) goto raw;
return 2 + rcode;
}
- fr_strerror_printf("Internal sanity check %d", __LINE__);
- return -1; /* TODO: fixme! */
+ /*
+ * This requires a whole lot more work.
+ */
+ return data2vp_extended(ctx, packet, original, secret, child,
+ start, attrlen, packetlen, pvp);
case PW_TYPE_EVS:
if (datalen < 6) goto raw; /* vid, vtype, value */
memcpy(&vendor, data, 4);
vendor = ntohl(vendor);
dv = dict_vendorbyvalue(vendor);
- if (!dv) goto raw;
-
- child = dict_attrbyparent(da, data[5], 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;
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 5, attrlen - 5, attrlen - 5, pvp);
if (rcode < 0) goto raw;
return 5 + rcode;
* attribute, OR they've already been grouped
* into a contiguous memory buffer.
*/
- rcode = data2vp_tlvs(packet, original, secret, da,
+ rcode = data2vp_tlvs(ctx, packet, original, secret, da,
data, attrlen, pvp);
if (rcode < 0) goto raw;
return rcode;
* VSAs can be WiMAX, in which case they don't
* fit into one attribute.
*/
- rcode = data2vp_vsas(packet, original, secret,
+ rcode = data2vp_vsas(ctx, packet, original, secret,
data, attrlen, packetlen, pvp);
if (rcode < 0) goto raw;
return rcode;
* therefore of type "octets", and will be
* handled below.
*/
- da = dict_attrunknown(da->attr, da->vendor, TRUE);
+ da = dict_attrunknown(da->attr, da->vendor, true);
if (!da) {
fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
- tag = 0;
+ tag = TAG_NONE;
+#ifndef NDEBUG
+ /*
+ * Fix for Coverity.
+ */
+ if (da->type != PW_TYPE_OCTETS) {
+ dict_attr_free(&da);
+ return -1;
+ }
+#endif
break;
}
* information, decode the actual data.
*/
alloc_cui:
- vp = pairalloc(NULL, da);
+ vp = pairalloc(ctx, da);
if (!vp) return -1;
vp->length = datalen;
switch (da->type) {
case PW_TYPE_STRING:
- memcpy(vp->vp_strvalue, data, vp->length);
- vp->vp_strvalue[vp->length] = '\0';
+ p = talloc_array(vp, char, vp->length + 1);
+ memcpy(p, data, vp->length);
+ p[vp->length] = '\0';
+ vp->vp_strvalue = p;
break;
case PW_TYPE_OCTETS:
+ pairmemcpy(vp, data, vp->length);
+ break;
+
case PW_TYPE_ABINARY:
- memcpy(vp->vp_octets, data, vp->length);
+ if (vp->length > sizeof(vp->vp_filter)) {
+ vp->length = sizeof(vp->vp_filter);
+ }
+ memcpy(vp->vp_filter, data, vp->length);
break;
case PW_TYPE_BYTE:
- vp->vp_integer = data[0];
+ vp->vp_byte = data[0];
break;
-
case PW_TYPE_SHORT:
- vp->vp_integer = (data[0] << 8) | data[1];
+ vp->vp_short = (data[0] << 8) | data[1];
break;
case PW_TYPE_INTEGER:
vp->vp_date = ntohl(vp->vp_date);
break;
+ case PW_TYPE_ETHERNET:
+ memcpy(&vp->vp_ether, data, 6);
+ break;
- case PW_TYPE_IPADDR:
+ case PW_TYPE_IPV4_ADDR:
memcpy(&vp->vp_ipaddr, data, 4);
break;
memcpy(&vp->vp_ifid, data, 8);
break;
- case PW_TYPE_IPV6ADDR:
+ case PW_TYPE_IPV6_ADDR:
memcpy(&vp->vp_ipv6addr, data, 16);
break;
- case PW_TYPE_IPV6PREFIX:
+ case PW_TYPE_IPV6_PREFIX:
/*
* FIXME: double-check that
* (vp->vp_octets[1] >> 3) matches vp->length + 2
}
break;
- case PW_TYPE_IPV4PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
/* FIXME: do the same double-check as for IPv6Prefix */
- memcpy(&vp->vp_ipv4prefix, buffer, sizeof(vp->vp_ipv4prefix));
+ memcpy(&vp->vp_ipv4prefix, data, vp->length);
/*
* /32 means "keep all bits". Otherwise, mask
memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
mask = 1;
- mask <<= (32 - (buffer[1] & 0x3f));
+ mask <<= (32 - (data[1] & 0x3f));
mask--;
mask = ~mask;
mask = htonl(mask);
addr &= mask;
- memcpy(vp->vp_octets + 2, &addr, sizeof(addr));
+ memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr));
}
break;
fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
-
+ vp->type = VT_DATA;
*pvp = vp;
return attrlen;
/**
* @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)
{
ssize_t rcode;
- const DICT_ATTR *da;
+ DICT_ATTR const *da;
if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
fr_strerror_printf("rad_attr2vp: Insufficient data");
}
da = dict_attrbyvalue(data[0], 0);
- if (!da) da = dict_attrunknown(data[0], 0, TRUE);
+ if (!da) da = dict_attrunknown(data[0], 0, true);
+ if (!da) return -1;
+
+ /*
+ * Pass the entire thing to the decoding function
+ */
+ if (da->flags.concat) {
+ return data2vp_concat(ctx, da, data, length, pvp);
+ }
/*
* Note that we pass the entire length, not just the
* attributes may have the "continuation" bit set, and
* will thus be more than one attribute in length.
*/
- rcode = data2vp(packet, original, secret, da,
+ rcode = data2vp(ctx, packet, original, secret, da,
data + 2, data[1] - 2, length - 2, pvp);
if (rcode < 0) return rcode;
return 2 + rcode;
}
+fr_thread_local_setup(uint8_t *, rad_vp2data_buff);
-/**
- * @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)
-{
- const DICT_ATTR *da;
-
- if (!data || (length == 0) || !pvp) return -1;
-
- da = dict_attrbyvalue(attribute, vendor);
- if (!da) da = dict_attrunknown(attribute, vendor, TRUE);
- if (!da) return -1;
-
- return data2vp(NULL, NULL, NULL, da,
- data, length, length, pvp);
-}
-
-/**
- * @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->da->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->da->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->da->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;
}
-
- return len;
+
+ 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 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;
*
* @param ctx the context in which the packet is allocated. May be NULL if
* the packet is not associated with a REQUEST.
- * @param newvector if TRUE a new request authenticator will be generated.
+ * @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(TALLOC_CTX *ctx, int newvector)
+RADIUS_PACKET *rad_alloc(TALLOC_CTX *ctx, bool new_vector)
{
RADIUS_PACKET *rp;
rp->id = -1;
rp->offset = -1;
- if (newvector) {
+ if (new_vector) {
int i;
uint32_t hash, base;
if (!packet) return NULL;
- reply = rad_alloc(ctx, 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);
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;
+}