* 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 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 */
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
};
return;
}
-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 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(uint8_t const *ptr, int attrlen, int depth)
{
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;
*/
static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
#ifdef WITH_UDPFROMTO
- fr_ipaddr_t *src_ipaddr, int src_port,
+ fr_ipaddr_t *src_ipaddr, uint16_t src_port,
#else
- UNUSED fr_ipaddr_t *src_ipaddr, UNUSED int src_port,
+ UNUSED fr_ipaddr_t *src_ipaddr, UNUSED uint16_t src_port,
#endif
- fr_ipaddr_t *dst_ipaddr, int dst_port)
+ fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
{
int rcode;
struct sockaddr_storage dst;
done:
#endif
if (rcode < 0) {
- DEBUG("rad_send() failed: %s\n", strerror(errno));
+ fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
}
return rcode;
}
-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;
}
ssize_t data_len;
uint8_t header[4];
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.
* packet after "len" bytes.
*/
#ifdef WITH_UDPFROMTO
- if ((dst.ss_family == AF_INET) || (dst.ss_family == AF_INET6)) {
- data_len = recvfromto(sockfd, packet->data, 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, packet->data, len, flags,
- (struct sockaddr *)&src, &sizeof_src);
if (data_len < 0) {
return data_len;
}
FR_MD5_CTX context;
int i;
- fr_MD5Init(&context);
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Update(&context, (uint8_t const *) 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++ ) {
digest[i] ^= value[i];
}
*outlen = len;
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) 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];
}
passwd[1] = fr_rand();
passwd[2] = inlen; /* length of the password string */
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) 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];
*/
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;
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) {
+ 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);
}
{
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 = (uint8_t const *) &vp->vp_ipaddr;
- len = 4; /* just in case */
- break;
-
/*
* There are no tagged date attributes.
*/
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:
+ 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;
+ 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;
{
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;
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;
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.
size_t len, left;
VALUE_PAIR const *vp = *pvp;
+ VERIFY_VP(vp);
+
debug_pair(vp);
p = vp->vp_octets;
{
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;
{
ssize_t len;
uint32_t lvalue;
- const VALUE_PAIR *vp = *pvp;
+ VALUE_PAIR const *vp = *pvp;
+ VERIFY_VP(vp);
/*
* Double-check for WiMAX format.
*/
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;
}
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");
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.
*/
uint8_t *ptr;
uint16_t total_length;
int len;
- const VALUE_PAIR *reply;
+ VALUE_PAIR const *reply;
char const *what;
- char ip_src_buffer[128];
- char ip_dst_buffer[128];
+ 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;
while (reply) {
size_t last_len;
char const *last_name = NULL;
-
+
VERIFY_VP(reply);
/*
if (len == 0) {
if (last_len != 0) {
fr_strerror_printf("WARNING: Failed encoding attribute %s\n", last_name);
+ break;
} else {
fr_strerror_printf("WARNING: Skipping zero-length attribute %s\n", last_name);
}
* 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,
- (uint8_t const *) 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, (uint8_t const *) 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);
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.
*/
/*
* MD5(packet + secret);
*/
- fr_MD5Init(&context);
- fr_MD5Update(&context, packet->data, packet->data_len);
- fr_MD5Update(&context, (uint8_t const *) 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.
/*
* MD5(packet + secret);
*/
- fr_MD5Init(&context);
- fr_MD5Update(&context, packet->data, packet->data_len);
- fr_MD5Update(&context, (uint8_t const *) 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.
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;
* 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
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,
- (uint8_t const *) 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(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);
-
/**
* @brief convert a "concatenated" attribute to one long VP.
*/
-static ssize_t data2vp_concat(RADIUS_PACKET *packet,
+static ssize_t data2vp_concat(TALLOC_CTX *ctx,
DICT_ATTR const *da, uint8_t const *start,
size_t const packetlen, VALUE_PAIR **pvp)
{
if (ptr[0] != attr) break;
}
- vp = pairalloc(packet, da);
+ vp = pairalloc(ctx, da);
if (!vp) return -1;
vp->length = total;
/**
* @brief convert TLVs to one or more VPs
*/
-static ssize_t data2vp_tlvs(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
+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 */
}
}
- 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(RADIUS_PACKET *packet,
+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,
{
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:
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),
* But for the first fragment, we get passed a pointer to the
* "extended-attr".
*/
-static ssize_t data2vp_extended(RADIUS_PACKET *packet,
+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,
ssize_t rcode;
size_t fraglen;
uint8_t *head, *tail;
- const uint8_t *frag, *end;
+ uint8_t const *frag, *end;
uint8_t const *attr;
+ int fragments;
+ bool last_frag;
if (attrlen < 3) return -1;
fraglen = attrlen - 2;
frag = data + attrlen;
end = data + packetlen;
+ fragments = 1;
+ last_frag = false;
while (frag < end) {
- int last_frag = false;
-
if (last_frag ||
(frag[0] != attr[0]) ||
(frag[1] < 4) || /* too short for long-extended */
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.
*
*/
frag = attr;
- while (frag < end) {
+ 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(packet, original, secret, da,
+ rcode = data2vp(ctx, packet, original, secret, da,
head, fraglen, fraglen, pvp);
free(head);
if (rcode < 0) return rcode;
*
* Called ONLY for Vendor-Specific
*/
-static ssize_t data2vp_wimax(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
+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,
{
ssize_t rcode;
size_t fraglen;
+ bool last_frag;
uint8_t *head, *tail;
- const uint8_t *frag, *end;
+ 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(RADIUS_PACKET *packet,
+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,
* 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(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 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];
* 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 = (uint8_t const *) "";
- datalen = 1;
+ data = NULL;
+ datalen = 0;
goto alloc_cui; /* skip everything */
}
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 ((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;
/*
* This requires a whole lot more work.
*/
- return data2vp_extended(packet, original, secret, child,
+ return data2vp_extended(ctx, packet, original, secret, child,
start, attrlen, packetlen, pvp);
case PW_TYPE_EVS:
}
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;
fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
- tag = 0;
+ tag = TAG_NONE;
#ifndef NDEBUG
/*
* Fix for Coverity.
* information, decode the actual data.
*/
alloc_cui:
- vp = pairalloc(packet, da);
+ vp = pairalloc(ctx, da);
if (!vp) return -1;
vp->length = datalen;
break;
case PW_TYPE_OCTETS:
+ pairmemcpy(vp, data, vp->length);
+ break;
+
case PW_TYPE_ABINARY:
- vp->vp_octets = talloc_memdup(vp, 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);
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(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
+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) return -1;
/*
* Pass the entire thing to the decoding function
*/
if (da->flags.concat) {
- return data2vp_concat(packet, da, data, length, pvp);
+ return data2vp_concat(ctx, da, data, length, pvp);
}
/*
* 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,
- uint8_t const *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(VALUE_PAIR const *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;
+ }
+
+ 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;
}
/**
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
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) 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++) {
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) 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);
}
}
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) 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++) {
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;
- talloc_free(radius_packet->data); /* not really necessary... */
+ 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;
+}