-/*
- * radius.c Functions to send/receive radius packets.
+/**
+ * @file radius.c
+ * @brief Functions to send/receive radius packets.
*
* Version: $Id$
*
#include <malloc.h>
#endif
+#if 0
+#define VP_TRACE if (fr_debug_flag) printf
+#else
+#define VP_TRACE(_x, ...)
+#endif
+
+
/*
* The RFC says 4096 octets max, and most packets are less than 256.
*/
return;
}
-#if 0
-static void print_hex(RADIUS_PACKET *packet)
+static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+
+static void print_hex_data(const uint8_t *ptr, int attrlen, int depth)
+{
+ int i;
+
+ for (i = 0; i < attrlen; i++) {
+ if ((i > 0) && ((i & 0x0f) == 0x00))
+ fprintf(fr_log_fp, "%.*s", depth, tabs);
+ fprintf(fr_log_fp, "%02x ", ptr[i]);
+ if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
+ }
+ if ((i & 0x0f) != 0) fprintf(fr_log_fp, "\n");
+}
+
+
+void rad_print_hex(RADIUS_PACKET *packet)
{
int i;
- if (!packet->data) return;
+ if (!packet->data || !fr_log_fp) return;
- printf(" Code:\t\t%u\n", packet->data[0]);
- printf(" Id:\t\t%u\n", packet->data[1]);
- printf(" Length:\t%u\n", ((packet->data[2] << 8) |
+ fprintf(fr_log_fp, " Code:\t\t%u\n", packet->data[0]);
+ fprintf(fr_log_fp, " Id:\t\t%u\n", packet->data[1]);
+ fprintf(fr_log_fp, " Length:\t%u\n", ((packet->data[2] << 8) |
(packet->data[3])));
- printf(" Vector:\t");
+ fprintf(fr_log_fp, " Vector:\t");
for (i = 4; i < 20; i++) {
- printf("%02x", packet->data[i]);
+ fprintf(fr_log_fp, "%02x", packet->data[i]);
}
- printf("\n");
+ fprintf(fr_log_fp, "\n");
if (packet->data_len > 20) {
int total;
const uint8_t *ptr;
- printf(" Data:");
+ fprintf(fr_log_fp, " Data:");
total = packet->data_len - 20;
ptr = packet->data + 20;
while (total > 0) {
int attrlen;
+ unsigned int vendor = 0;
- printf("\t\t");
+ fprintf(fr_log_fp, "\t\t");
if (total < 2) { /* too short */
- printf("%02x\n", *ptr);
+ fprintf(fr_log_fp, "%02x\n", *ptr);
break;
}
if (ptr[1] > total) { /* too long */
for (i = 0; i < total; i++) {
- printf("%02x ", ptr[i]);
+ fprintf(fr_log_fp, "%02x ", ptr[i]);
}
break;
}
- printf("%02x %02x ", ptr[0], ptr[1]);
+ fprintf(fr_log_fp, "%02x %02x ", ptr[0], ptr[1]);
attrlen = ptr[1] - 2;
- ptr += 2;
- total -= 2;
-
- for (i = 0; i < attrlen; i++) {
- if ((i > 0) && ((i & 0x0f) == 0x00))
- printf("\t\t\t");
- printf("%02x ", ptr[i]);
- if ((i & 0x0f) == 0x0f) printf("\n");
+
+ if ((ptr[0] == PW_VENDOR_SPECIFIC) &&
+ (attrlen > 4)) {
+ vendor = (ptr[3] << 16) | (ptr[4] << 8) | ptr[5];
+ fprintf(fr_log_fp, "%02x%02x%02x%02x (%u) ",
+ ptr[2], ptr[3], ptr[4], ptr[5], vendor);
+ attrlen -= 4;
+ ptr += 6;
+ total -= 6;
+
+ } else {
+ ptr += 2;
+ total -= 2;
}
- if ((attrlen & 0x0f) != 0x00) printf("\n");
+ print_hex_data(ptr, attrlen, 3);
ptr += attrlen;
total -= attrlen;
}
fflush(stdout);
}
-#endif
-/*
- * Wrapper for sendto which handles sendfromto, IPv6, and all
+/**
+ * @brief Wrapper for sendto which handles sendfromto, IPv6, and all
* possible combinations.
*/
static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
fr_ipaddr_t *src_ipaddr, int src_port,
fr_ipaddr_t *dst_ipaddr, int dst_port)
{
+ int rcode;
struct sockaddr_storage dst;
socklen_t sizeof_dst;
#ifdef WITH_UDPFROMTO
/*
- * Only IPv4 is supported for udpfromto.
- *
* And if they don't specify a source IP address, don't
* use udpfromto.
*/
- if ((dst_ipaddr->af == AF_INET) &&
- (src_ipaddr->af != AF_UNSPEC)) {
- return sendfromto(sockfd, data, data_len, flags,
- (struct sockaddr *)&src, sizeof_src,
- (struct sockaddr *)&dst, sizeof_dst);
+ if (((dst_ipaddr->af == AF_INET) || (dst_ipaddr->af == AF_INET6)) &&
+ (src_ipaddr->af != AF_UNSPEC) &&
+ !fr_inaddr_any(src_ipaddr)) {
+ rcode = sendfromto(sockfd, data, data_len, flags,
+ (struct sockaddr *)&src, sizeof_src,
+ (struct sockaddr *)&dst, sizeof_dst);
+ goto done;
}
#else
src_ipaddr = src_ipaddr; /* -Wunused */
#endif
/*
- * No udpfromto, OR an IPv6 socket, fail gracefully.
+ * No udpfromto, fail gracefully.
*/
- return sendto(sockfd, data, data_len, flags,
- (struct sockaddr *) &dst, sizeof_dst);
+ rcode = sendto(sockfd, data, data_len, flags,
+ (struct sockaddr *) &dst, sizeof_dst);
+#ifdef WITH_UDPFROMTO
+done:
+#endif
+ if (rcode < 0) {
+ DEBUG("rad_send() failed: %s\n", strerror(errno));
+ }
+
+ return rcode;
}
}
-/*
- * wrapper for recvfrom, which handles recvfromto, IPv6, and all
+/**
+ * @brief wrapper for recvfrom, which handles recvfromto, IPv6, and all
* possible combinations.
*/
static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
* packet after "len" bytes.
*/
#ifdef WITH_UDPFROMTO
- if (dst.ss_family == AF_INET) {
+ if ((dst.ss_family == AF_INET) || (dst.ss_family == AF_INET6)) {
data_len = recvfromto(sockfd, buf, len, flags,
(struct sockaddr *)&src, &sizeof_src,
(struct sockaddr *)&dst, &sizeof_dst);
} else
#endif
/*
- * No udpfromto, OR an IPv6 socket. Fail gracefully.
+ * No udpfromto, fail gracefully.
*/
data_len = recvfrom(sockfd, buf, len, flags,
(struct sockaddr *)&src, &sizeof_src);
#define AUTH_PASS_LEN (AUTH_VECTOR_LEN)
-/*************************************************************************
- *
- * Function: make_secret
- *
- * Purpose: Build an encrypted secret value to return in a reply
- * packet. The secret is hidden by xoring with a MD5 digest
+/**
+ * @brief Build an encrypted secret value to return in a reply packet
+ *
+ * The secret is hidden by xoring with a MD5 digest
* created from the shared secret and the authentication
* vector. We put them into MD5 in the reverse order from
* that used when encrypting passwords to RADIUS.
*
- *************************************************************************/
+ */
static void make_secret(uint8_t *digest, const uint8_t *vector,
const char *secret, const uint8_t *value)
{
extern int fr_attr_shift[];
extern int fr_attr_mask[];
-static int do_next_tlv(const VALUE_PAIR *vp, int nest)
+static int do_next_tlv(const VALUE_PAIR *vp, const VALUE_PAIR *next, int nest)
{
unsigned int tlv1, tlv2;
if (nest > fr_attr_max_tlv) return 0;
+ if (!vp) return 0;
+
/*
* Keep encoding TLVs which have the same scope.
* e.g. two attributes of:
/*
* Nothing to follow, we're done.
*/
- if (!vp->next) return 0;
+ if (!next) return 0;
/*
* Not from the same vendor, skip it.
*/
- if (vp->vendor != vp->next->vendor) return 0;
-
- /*
- * The next one has already been done. Maybe by
- * another level of recursion. Skip it.
- */
- if (vp->next->flags.encoded) return 0;
+ if (vp->vendor != next->vendor) return 0;
/*
* In a different TLV space, skip it.
*/
tlv1 = vp->attribute;
- tlv2 = vp->next->attribute;
+ tlv2 = next->attribute;
tlv1 &= ((1 << fr_attr_shift[nest]) - 1);
tlv2 &= ((1 << fr_attr_shift[nest]) - 1);
static ssize_t vp2data_any(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
- const char *secret, int nest, VALUE_PAIR *vps,
+ const char *secret, int nest,
+ const VALUE_PAIR **pvp,
uint8_t *start, size_t room);
+static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
+ const RADIUS_PACKET *original,
+ const char *secret, const VALUE_PAIR **pvp,
+ unsigned int attribute, uint8_t *ptr, size_t room);
+
+/**
+ * @brief This is really a sub-function of vp2data_any(). It encodes
+ * the *data* portion of the TLV, and assumes that the encapsulating
+ * attribute has already been encoded.
+ */
static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
- const char *secret, int nest, VALUE_PAIR *vps,
+ const char *secret, int nest,
+ const VALUE_PAIR **pvp,
uint8_t *start, size_t room)
{
ssize_t len;
+ size_t my_room;
uint8_t *ptr = start;
- uint8_t *end = start + room;
- VALUE_PAIR *vp = vps;
+ const VALUE_PAIR *vp = *pvp;
+ const VALUE_PAIR *svp = vp;
+
+ if (!svp) return 0;
#ifndef NDEBUG
if (nest > fr_attr_max_tlv) {
}
#endif
- while (1) {
+ while (vp) {
+ if (room < 2) return ptr - start;
+
ptr[0] = (vp->attribute >> fr_attr_shift[nest]) & fr_attr_mask[nest];
ptr[1] = 2;
- len = vp2data_any(packet, original, secret, nest + 1, vp,
- ptr + ptr[1], end - ptr);
- if (len < 0) {
- if (vp != vps) break;
- return len;
- }
-
+ my_room = room;
+ if (room > 255) my_room = 255;
+
+ len = vp2data_any(packet, original, secret, nest,
+ &vp, ptr + 2, my_room - 2);
+ if (len < 0) return len;
+ if (len == 0) return ptr - start;
+ /* len can NEVER be more than 253 */
+
ptr[1] += len;
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
+ print_hex_data(ptr + 2, len, 3);
+ }
+#endif
+
+ room -= ptr[1];
ptr += ptr[1];
- vp->flags.encoded = 1;
+ *pvp = vp;
- if (!do_next_tlv(vp, nest)) break;
- vp = vp->next;
+ if (!do_next_tlv(svp, vp, nest)) break;
}
-
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ DICT_ATTR *da;
+
+ da = dict_attrbyvalue(svp->attribute & ((1 << fr_attr_shift[nest ]) - 1), svp->vendor);
+ if (da) fprintf(fr_log_fp, "\t%s = ...\n", da->name);
+ }
+#endif
+
return ptr - start;
}
-/*
- * Encodes the data portion of an attribute.
- * Returns -1 on error, or the length of the data portion.
+
+/**
+ * @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, VALUE_PAIR *vp,
+ const char *secret, int nest,
+ const VALUE_PAIR **pvp,
uint8_t *start, size_t room)
{
uint32_t lvalue;
const uint8_t *data;
uint8_t *ptr = start;
uint8_t array[4];
+ uint64_t lvalue64;
+ const VALUE_PAIR *vp = *pvp;
/*
* See if we need to encode a TLV. The low portion of
*
* If we cared about the stack, we could unroll the loop.
*/
- if ((nest > 0) && (nest <= fr_attr_max_tlv) &&
- ((vp->attribute >> fr_attr_shift[nest]) != 0)) {
- return vp2data_tlvs(packet, original, secret, nest, vp,
+ if (vp->flags.is_tlv && (nest < fr_attr_max_tlv) &&
+ ((vp->attribute >> fr_attr_shift[nest + 1]) != 0)) {
+ return vp2data_tlvs(packet, original, secret, nest + 1, pvp,
start, room);
}
+ debug_pair(vp);
+
/*
* Set up the default sources for the data.
*/
data = vp->vp_octets;
len = vp->length;
+ /*
+ * Short-circuit it for long attributes. They can't be
+ * encrypted, tagged, etc.
+ */
+ if ((vp->type & PW_FLAG_LONG) != 0) goto do_tlv;
+
switch(vp->type) {
case PW_TYPE_STRING:
case PW_TYPE_OCTETS:
data = array;
break;
+ case PW_TYPE_INTEGER64:
+ len = 8; /* just in case */
+ lvalue64 = htonll(vp->vp_integer64);
+ data = (uint8_t *) &lvalue64;
+ break;
+
case PW_TYPE_IPADDR:
data = (const uint8_t *) &vp->vp_ipaddr;
len = 4; /* just in case */
}
case PW_TYPE_TLV:
+ do_tlv:
data = vp->vp_tlv;
if (!data) {
fr_strerror_printf("ERROR: Cannot encode NULL TLV");
return -1;
}
- if (vp->length > room) return 0; /* can't chop TLVs to fit */
break;
default: /* unknown type: ignore it */
}
/*
+ * No data: skip it.
+ */
+ if (len == 0) {
+ *pvp = vp->next;
+ return 0;
+ }
+
+ /*
* Bound the data to the calling size
*/
if (len > (ssize_t) room) len = room;
break;
} /* switch over encryption flags */
- vp->flags.encoded = 1;
- return len + (ptr - start);;
+ *pvp = vp->next;
+ return len + (ptr - start);
}
static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
}
-/*
- * Encode an "extended" attribute.
+/**
+ * @brief Encode an "extended" attribute.
*/
int rad_vp2extended(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
- const char *secret, VALUE_PAIR *vp,
+ const char *secret, const VALUE_PAIR **pvp,
uint8_t *ptr, size_t room)
{
int len;
int hdr_len;
- int nest = 2;
+ int nest = 1;
uint8_t *start = ptr;
+ const VALUE_PAIR *vp = *pvp;
if (vp->vendor < VENDORPEC_EXTENDED) {
fr_strerror_printf("rad_vp2extended called for non-extended attribute");
evs[4] = vp->attribute & 0xff;
ptr[1] += 5;
- nest = 1;
+ nest = 0;
}
hdr_len = ptr[1];
len = vp2data_any(packet, original, secret, nest,
- vp, ptr + ptr[1], room - hdr_len);
- if (len < 0) return len;
+ pvp, ptr + ptr[1], room - hdr_len);
+ if (len <= 0) return len;
/*
* There may be more than 252 octets of data encoded in
}
ptr[1] += len;
+
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ int jump = 3;
+
+ fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
+ if (!vp->flags.extended_flags) {
+ fprintf(fr_log_fp, "%02x ", ptr[2]);
+
+ } else {
+ fprintf(fr_log_fp, "%02x %02x ", ptr[2], ptr[3]);
+ jump = 4;
+ }
+
+ if (vp->flags.evs) {
+ fprintf(fr_log_fp, "%02x%02x%02x%02x (%u) %02x ",
+ ptr[jump], ptr[jump + 1],
+ ptr[jump + 2], ptr[jump + 3],
+ ((ptr[jump + 1] << 16) |
+ (ptr[jump + 2] << 8) |
+ ptr[jump + 3]),
+ ptr[jump + 4]);
+ jump += 5;
+ }
+
+ print_hex_data(ptr + jump, len, 3);
+ }
+#endif
return (ptr + ptr[1]) - start;
}
-/*
- * Encode a WiMAX attribute.
+/**
+ * @brief Encode a WiMAX attribute.
*/
int rad_vp2wimax(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
- const char *secret, VALUE_PAIR *vp,
+ const char *secret, const VALUE_PAIR **pvp,
uint8_t *ptr, size_t room)
{
int len;
uint32_t lvalue;
int hdr_len;
uint8_t *start = ptr;
+ const VALUE_PAIR *vp = *pvp;
/*
- * Double-check for WiMAX
+ * Double-check for WiMAX format.
*/
- if (vp->vendor != VENDORPEC_WIMAX) {
+ if (!vp->flags.wimax) {
fr_strerror_printf("rad_vp2wimax called for non-WIMAX VSA");
return -1;
}
hdr_len = 9;
- len = vp2data_any(packet, original, secret, 1, vp, ptr + ptr[1],
+ len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1],
room - hdr_len);
if (len <= 0) return len;
ptr[1] += len;
ptr[7] += len;
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x %02x%02x%02x%02x (%u) %02x %02x %02x ",
+ ptr[0], ptr[1],
+ ptr[2], ptr[3], ptr[4], ptr[5],
+ (ptr[3] << 16) | (ptr[4] << 8) | ptr[5],
+ ptr[6], ptr[7], ptr[8]);
+ print_hex_data(ptr + 9, len, 3);
+ }
+#endif
+
return (ptr + ptr[1]) - start;
}
-/*
- * Encode an RFC format TLV. This could be a standard attribute,
+/**
+ * @brief Encode an RFC format TLV.
+ *
+ * This could be a standard attribute,
* or a TLV data type. If it's a standard attribute, then
* vp->attribute == attribute. Otherwise, attribute may be
* something else.
*/
static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
- const char *secret, VALUE_PAIR *vp,
+ const char *secret, const VALUE_PAIR **pvp,
unsigned int attribute, uint8_t *ptr, size_t room)
{
ssize_t len;
if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
- len = vp2data_any(packet, original, secret, 0, vp, ptr + ptr[1], room);
- if (len < 0) return len;
+ len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1], room);
+ if (len <= 0) return len;
ptr[1] += len;
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
+ print_hex_data(ptr + 2, len, 3);
+ }
+#endif
+
return ptr[1];
}
-/*
- * Encode a VSA which is a TLV. If it's in the RFC format, call
+/**
+ * @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, VALUE_PAIR *vp,
+ const char *secret, const VALUE_PAIR **pvp,
unsigned int attribute, unsigned int vendor,
uint8_t *ptr, size_t room)
{
ssize_t len;
DICT_VENDOR *dv;
+ const VALUE_PAIR *vp = *pvp;
/*
* Unknown vendor: RFC format.
* Known vendor and RFC format: go do that.
*/
dv = dict_vendorbyvalue(vendor);
- if (!dv || ((dv->type == 1) && (dv->length == 1))) {
- return vp2attr_rfc(packet, original, secret, vp,
+ if (!dv ||
+ (!vp->flags.is_tlv && (dv->type == 1) && (dv->length == 1))) {
+ return vp2attr_rfc(packet, original, secret, pvp,
attribute, ptr, room);
}
case 2:
ptr[dv->type] = 0;
- /* FALL-THROUGH */
+ ptr[dv->type + 1] = dv->type + 2;
+ break;
case 1:
- ptr[dv->type + dv->length - 1] = dv->type + dv->length;
+ ptr[dv->type] = dv->type + 1;
break;
}
room = 255 - (dv->type + dv->length);
}
- len = vp2data_any(packet, original, secret, 0, vp,
+ len = vp2data_any(packet, original, secret, 0, pvp,
ptr + dv->type + dv->length, room);
- if (len < 0) return len;
+ if (len <= 0) return len;
if (dv->length) ptr[dv->type + dv->length - 1] += len;
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ switch (dv->type) {
+ default:
+ break;
+
+ case 4:
+ if ((fr_debug_flag > 3) && fr_log_fp)
+ fprintf(fr_log_fp, "\t\t%02x%02x%02x%02x ",
+ ptr[0], ptr[1], ptr[2], ptr[3]);
+ break;
+
+ case 2:
+ if ((fr_debug_flag > 3) && fr_log_fp)
+ fprintf(fr_log_fp, "\t\t%02x%02x ",
+ ptr[0], ptr[1]);
+ break;
+
+ case 1:
+ if ((fr_debug_flag > 3) && fr_log_fp)
+ fprintf(fr_log_fp, "\t\t%02x ", ptr[0]);
+ break;
+ }
+
+ switch (dv->length) {
+ default:
+ break;
+
+ case 0:
+ fprintf(fr_log_fp, " ");
+ break;
+
+ case 1:
+ fprintf(fr_log_fp, "%02x ",
+ ptr[dv->type]);
+ break;
+
+ case 2:
+ fprintf(fr_log_fp, "%02x%02x ",
+ ptr[dv->type], ptr[dv->type] + 1);
+ break;
+ }
+
+ print_hex_data(ptr + dv->type + dv->length, len, 3);
+ }
+#endif
+
return dv->type + dv->length + len;
}
-/*
- * Encode a Vendor-Specific attribute.
+/**
+ * @brief Encode a Vendor-Specific attribute.
*/
int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, VALUE_PAIR *vp, uint8_t *ptr,
+ const char *secret, const VALUE_PAIR **pvp, uint8_t *ptr,
size_t room)
{
ssize_t len;
uint32_t lvalue;
+ const VALUE_PAIR *vp = *pvp;
/*
- * Double-check for WiMAX
+ * Double-check for WiMAX format.
*/
- if (vp->vendor == VENDORPEC_WIMAX) {
- return rad_vp2wimax(packet, original, secret, vp,
+ if (vp->flags.wimax) {
+ return rad_vp2wimax(packet, original, secret, pvp,
ptr, room);
}
if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
- len = vp2attr_vsa(packet, original, secret, vp,
+ len = vp2attr_vsa(packet, original, secret, pvp,
vp->attribute, vp->vendor,
ptr + ptr[1], room);
if (len < 0) return len;
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t%02x %02x %02x%02x%02x%02x (%u) ",
+ ptr[0], ptr[1],
+ ptr[2], ptr[3], ptr[4], ptr[5],
+ (ptr[3] << 16) | (ptr[4] << 8) | ptr[5]);
+ print_hex_data(ptr + 6, len, 3);
+ }
+#endif
+
ptr[1] += len;
return ptr[1];
}
-/*
- * Encode an RFC standard attribute 1..255
+/**
+ * @brief Encode an RFC standard attribute 1..255
*/
int rad_vp2rfc(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
- const char *secret, VALUE_PAIR *vp,
+ const char *secret, const VALUE_PAIR **pvp,
uint8_t *ptr, size_t room)
{
+ const VALUE_PAIR *vp = *pvp;
+
if (vp->vendor != 0) {
fr_strerror_printf("rad_vp2rfc called with VSA");
return -1;
return -1;
}
+ /*
+ * Only CUI is allowed to have zero length.
+ * Thank you, WiMAX!
+ */
if ((vp->length == 0) &&
- (vp->attribute != PW_CHARGEABLE_USER_IDENTITY)) {
- return 0;
+ (vp->attribute == PW_CHARGEABLE_USER_IDENTITY)) {
+ ptr[0] = PW_CHARGEABLE_USER_IDENTITY;
+ ptr[1] = 2;
+
+ *pvp = vp->next;
+ return 2;
}
- return vp2attr_rfc(packet, original, secret, vp, vp->attribute,
+ return vp2attr_rfc(packet, original, secret, pvp, vp->attribute,
ptr, room);
}
-/*
- * Parse a data structure into a RADIUS attribute.
+/**
+ * @brief Parse a data structure into a RADIUS attribute.
*/
int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const char *secret, VALUE_PAIR *vp, uint8_t *start,
+ const char *secret, const VALUE_PAIR **pvp, uint8_t *start,
size_t room)
{
+ const VALUE_PAIR *vp;
+
+ if (!pvp || !*pvp || !start || (room <= 2)) return -1;
+
+ vp = *pvp;
+
/*
* RFC format attributes take the fast path.
*/
if (vp->vendor == 0) {
- return rad_vp2rfc(packet, original, secret, vp,
+ if (vp->attribute > 255) return 0;
+
+ /*
+ * Message-Authenticator is hard-coded.
+ */
+ if (vp->attribute == PW_MESSAGE_AUTHENTICATOR) {
+ if (room < 18) return -1;
+
+ debug_pair(vp);
+ start[0] = PW_MESSAGE_AUTHENTICATOR;
+ start[1] = 18;
+ memset(start + 2, 0, 16);
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) {
+ fprintf(fr_log_fp, "\t\t50 12 ...\n");
+ }
+#endif
+
+ *pvp = (*pvp)->next;
+ return 18;
+ }
+
+ return rad_vp2rfc(packet, original, secret, pvp,
start, room);
}
if (vp->vendor > FR_MAX_VENDOR) {
- return rad_vp2extended(packet, original, secret, vp,
+ return rad_vp2extended(packet, original, secret, pvp,
start, room);
}
- if (vp->vendor == VENDORPEC_WIMAX) {
- return rad_vp2wimax(packet, original, secret, vp,
+ if (vp->flags.wimax) {
+ return rad_vp2wimax(packet, original, secret, pvp,
start, room);
}
- return rad_vp2vsa(packet, original, secret, vp,
+ return rad_vp2vsa(packet, original, secret, pvp,
start, room);
}
-/*
- * Encode a packet.
+/**
+ * @brief Encode a packet.
*/
int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
const char *secret)
uint8_t *ptr;
uint16_t total_length;
int len;
- VALUE_PAIR *reply;
+ const VALUE_PAIR *reply;
const char *what;
char ip_buffer[128];
/*
* Loop over the reply attributes for the packet.
*/
- for (reply = packet->vps; reply; reply = reply->next) {
+ reply = packet->vps;
+ while (reply) {
+ size_t last_len;
+ const char *last_name = NULL;
+
/*
* Ignore non-wire attributes, but allow extended
* attributes.
if (reply->attribute == PW_RAW_ATTRIBUTE) {
memcpy(ptr, reply->vp_octets, reply->length);
len = reply->length;
+ reply = reply->next;
goto next;
}
#endif
+ reply = reply->next;
continue;
}
* length and initial value.
*/
if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
- reply->length = AUTH_VECTOR_LEN;
- memset(reply->vp_strvalue, 0, AUTH_VECTOR_LEN);
-
/*
* Cache the offset to the
* Message-Authenticator
*/
packet->offset = total_length;
+ last_len = 16;
+ } else {
+ last_len = reply->length;
}
+ last_name = reply->name;
- /*
- * Print out ONLY the attributes which
- * we're sending over the wire, and print
- * them out BEFORE they're encrypted.
- */
- debug_pair(reply);
-
- /*
- * Skip attributes that have already been
- * encoded. This can be done when the "vp2attr"
- * function sees multiple contiguous TLVs.
- */
- if (reply->flags.encoded) continue;
-
- len = rad_vp2attr(packet, original, secret, reply, ptr,
+ len = rad_vp2attr(packet, original, secret, &reply, ptr,
((uint8_t *) data) + sizeof(data) - ptr);
if (len < 0) return -1;
* Failed to encode the attribute, likely because
* the packet is full.
*/
- if ((len == 0) &&
- (total_length > (sizeof(data) - 2 - reply->length))) {
- DEBUG("WARNING: Attributes are too long for packet. Discarding data past %d bytes", total_length);
- break;
+ if (len == 0) {
+ if (last_len != 0) {
+ DEBUG("WARNING: Failed encoding attribute %s\n", last_name);
+ } else {
+ DEBUG("WARNING: Skipping zero-length attribute %s\n", last_name);
+ }
}
- next:
+#ifndef NDEBUG
+ next: /* Used only for Raw-Attribute */
+#endif
ptr += len;
total_length += len;
} /* done looping over all attributes */
}
-/*
- * Sign a previously encoded packet.
+/**
+ * @brief Sign a previously encoded packet.
*/
int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
const char *secret)
uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
switch (packet->code) {
- case PW_ACCOUNTING_REQUEST:
case PW_ACCOUNTING_RESPONSE:
+ if (original && original->code == PW_STATUS_SERVER) {
+ goto do_ack;
+ }
+
+ case PW_ACCOUNTING_REQUEST:
case PW_DISCONNECT_REQUEST:
case PW_DISCONNECT_ACK:
case PW_DISCONNECT_NAK:
memset(hdr->vector, 0, AUTH_VECTOR_LEN);
break;
+ do_ack:
case PW_AUTHENTICATION_ACK:
case PW_AUTHENTICATION_REJECT:
case PW_ACCESS_CHALLENGE:
return 0;
}
-/*
- * Reply to the request. Also attach
+/**
+ * @brief Reply to the request. Also attach
* reply attribute value pairs and any user message provided.
*/
int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
}
}
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+#endif
+
/*
* And send it on it's way.
*/
&packet->dst_ipaddr, packet->dst_port);
}
-/*
- * Do a comparison of two authentication digests by comparing
- * the FULL digest. Otehrwise, the server can be subject to
+/**
+ * @brief Do a comparison of two authentication digests by comparing
+ * the FULL digest.
+ *
+ * Otherwise, the server can be subject to
* timing attacks that allow attackers find a valid message
* authenticator.
*
* http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf
*/
-static int digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
+int rad_digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
{
int result = 0;
size_t i;
}
-/*
- * Validates the requesting client NAS. Calculates the
+/**
+ * @brief Validates the requesting client NAS. Calculates the
* signature based on the clients private key.
*/
static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
/*
* Return 0 if OK, 2 if not OK.
*/
- if (digest_cmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
+ if (rad_digest_cmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
return 0;
}
-/*
- * Validates the requesting client NAS. Calculates the
+/**
+ * @brief Validates the requesting client NAS. Calculates the
* signature based on the clients private key.
*/
static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
/*
* Return 0 if OK, 2 if not OK.
*/
- if (digest_cmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
+ if (rad_digest_cmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
return 0;
}
-/*
- * Check if a set of RADIUS formatted TLVs are OK.
+/**
+ * @brief Check if a set of RADIUS formatted TLVs are OK.
*/
int rad_tlv_ok(const uint8_t *data, size_t length,
size_t dv_type, size_t dv_length)
}
-/*
- * See if the data pointed to by PTR is a valid RADIUS packet.
+/**
+ * @brief See if the data pointed to by PTR is a valid RADIUS packet.
*
* packet is not 'const * const' because we may update data_len,
* if there's more data in the UDP packet than in the RADIUS packet.
}
-/*
- * Receive UDP client requests, and fill in
+/**
+ * @brief Receive UDP client requests, and fill in
* the basics of a RADIUS_PACKET structure.
*/
RADIUS_PACKET *rad_recv(int fd, int flags)
packet->id, (int) packet->data_len);
}
+#ifndef NDEBUG
+ if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+#endif
+
return packet;
}
-/*
- * Verify the signature of a packet.
+/**
+ * @brief Verify the signature of a packet.
*/
int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
const char *secret)
default:
break;
- case PW_ACCOUNTING_REQUEST:
case PW_ACCOUNTING_RESPONSE:
+ if (original &&
+ (original->code == PW_STATUS_SERVER)) {
+ goto do_ack;
+ }
+
+ case PW_ACCOUNTING_REQUEST:
case PW_DISCONNECT_REQUEST:
case PW_DISCONNECT_ACK:
case PW_DISCONNECT_NAK:
memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
break;
+ do_ack:
case PW_AUTHENTICATION_ACK:
case PW_AUTHENTICATION_REJECT:
case PW_ACCESS_CHALLENGE:
fr_hmac_md5(packet->data, packet->data_len,
(const uint8_t *) secret, strlen(secret),
calc_auth_vector);
- if (digest_cmp(calc_auth_vector, msg_auth_vector,
+ if (rad_digest_cmp(calc_auth_vector, msg_auth_vector,
sizeof(calc_auth_vector)) != 0) {
char buffer[32];
fr_strerror_printf("Received packet from %s with invalid Message-Authenticator! (Shared secret is incorrect.)",
}
-/*
- * Create a "raw" attribute from the attribute contents.
+/**
+ * @brief Create a "raw" attribute from the attribute contents.
*/
static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
UNUSED const RADIUS_PACKET *original,
{
VALUE_PAIR *vp;
-#ifndef NDEBUG
- if (length > sizeof(vp->vp_octets)) {
- fr_strerror_printf("data2vp_raw: Too much data");
- return -1;
- }
-#endif
-
/*
* Keep the next function happy.
*/
}
vp->length = length;
- memcpy(vp->vp_octets, data, length);
+
+ /*
+ * If it's short, put it into the array. If it's too
+ * long, flag it as such, and put it somewhere else;
+ */
+ if (length <= sizeof(vp->vp_octets)) {
+ memcpy(vp->vp_octets, data, length);
+ } else {
+ vp->type |= PW_FLAG_LONG;
+ vp->vp_tlv = malloc(length);
+ if (!vp->vp_tlv) {
+ pairfree(&vp);
+ return -1;
+ }
+ memcpy(vp->vp_tlv, data, length);
+ }
*pvp = vp;
const uint8_t *start, size_t length,
VALUE_PAIR **pvp);
-/*
- * Create any kind of VP from the attribute contents.
- *
- * Will return -1 on error, or "length".
+/**
+ * @brief Create any kind of VP from the attribute contents.
+ * @return -1 on error, or "length".
*/
static ssize_t data2vp_any(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
int data_offset = 0;
DICT_ATTR *da;
VALUE_PAIR *vp = NULL;
+ uint8_t buffer[256];
if (length == 0) {
/*
* Unknown attribute. Create it as a "raw" attribute.
*/
if (!da) {
+ VP_TRACE("Not found %u.%u\n", vendor, attribute);
raw:
if (vp) pairfree(&vp);
return data2vp_raw(packet, original, secret,
* they can't be encrypted.
*/
if (da->type == PW_TYPE_TLV) {
+ VP_TRACE("Found TLV %u.%u\n", vendor, attribute);
return data2vp_tlvs(packet, original, secret,
attribute, vendor, nest,
data, length, pvp);
}
/*
+ * The data is very long.
+ */
+ if (length > sizeof(vp->vp_octets)) {
+ /*
+ * Long encrypted attributes are forbidden.
+ */
+ if (da->flags.encrypt != FLAG_ENCRYPT_NONE) goto raw;
+
+#ifndef NDEBUG
+ /*
+ * Catch programming errors.
+ */
+ if ((da->type != PW_TYPE_STRING) &&
+ (da->type != PW_TYPE_OCTETS)) goto raw;
+
+#endif
+
+ /*
+ * FIXME: Figure out how to deal with long
+ * strings and binary data!
+ */
+ goto raw;
+ }
+
+ /*
* The attribute is known, and well formed. We can now
* create it. The main failure from here on in is being
* out of memory.
/*
* Copy the data to be decrypted
*/
- vp->length = length - data_offset;
- memcpy(&vp->vp_octets[0], data + data_offset, vp->length);
+ vp->length = length - data_offset;
+ memcpy(buffer, data + data_offset, vp->length);
/*
* Decrypt the attribute.
*/
- switch (vp->flags.encrypt) {
+ if (secret && packet) switch (vp->flags.encrypt) {
/*
* User-Password
*/
case FLAG_ENCRYPT_USER_PASSWORD:
if (original) {
- rad_pwdecode(vp->vp_strvalue,
+ rad_pwdecode((char *) buffer,
vp->length, secret,
original->vector);
} else {
- rad_pwdecode(vp->vp_strvalue,
+ rad_pwdecode((char *) buffer,
vp->length, secret,
packet->vector);
}
+ buffer[253] = '\0';
if (vp->attribute == PW_USER_PASSWORD) {
- vp->length = strlen(vp->vp_strvalue);
+ vp->length = strlen((char *) buffer);
}
break;
case FLAG_ENCRYPT_TUNNEL_PASSWORD:
if (!original) goto raw;
- if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
+ if (rad_tunnel_pwdecode(buffer, &vp->length,
secret, original->vector) < 0) {
goto raw;
}
make_secret(my_digest,
original->vector,
secret, data);
- memcpy(vp->vp_strvalue, my_digest,
+ memcpy(buffer, my_digest,
AUTH_VECTOR_LEN );
- vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
- vp->length = strlen(vp->vp_strvalue);
+ buffer[AUTH_VECTOR_LEN] = '\0';
+ vp->length = strlen((char *) buffer);
}
break;
switch (vp->type) {
case PW_TYPE_STRING:
+ memcpy(vp->vp_strvalue, buffer, vp->length);
+ vp->vp_strvalue[vp->length] = '\0';
+ break;
+
case PW_TYPE_OCTETS:
case PW_TYPE_ABINARY:
- /* nothing more to do */
+ memcpy(vp->vp_octets, buffer, vp->length);
break;
case PW_TYPE_BYTE:
if (vp->length != 1) goto raw;
- vp->vp_integer = vp->vp_octets[0];
+ vp->vp_integer = buffer[0];
break;
case PW_TYPE_SHORT:
if (vp->length != 2) goto raw;
- vp->vp_integer = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
+ vp->vp_integer = (buffer[0] << 8) | buffer[1];
break;
case PW_TYPE_INTEGER:
if (vp->length != 4) goto raw;
- memcpy(&vp->vp_integer, vp->vp_octets, 4);
+ memcpy(&vp->vp_integer, buffer, 4);
vp->vp_integer = ntohl(vp->vp_integer);
if (vp->flags.has_tag) vp->vp_integer &= 0x00ffffff;
+ break;
- /*
- * Try to get named VALUEs
- */
- {
- DICT_VALUE *dval;
- dval = dict_valbyattr(vp->attribute, vp->vendor,
- vp->vp_integer);
- if (dval) {
- strlcpy(vp->vp_strvalue,
- dval->name,
- sizeof(vp->vp_strvalue));
- }
- }
+ case PW_TYPE_INTEGER64:
+ if (vp->length != 8) goto raw;
+
+ /* vp_integer64 is a union with vp_octets */
+ memcpy(&vp->vp_integer64, buffer, 8);
+ vp->vp_integer64 = ntohll(vp->vp_integer64);
break;
case PW_TYPE_DATE:
if (vp->length != 4) goto raw;
- memcpy(&vp->vp_date, vp->vp_octets, 4);
+ memcpy(&vp->vp_date, buffer, 4);
vp->vp_date = ntohl(vp->vp_date);
break;
case PW_TYPE_IPADDR:
if (vp->length != 4) goto raw;
- memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+ memcpy(&vp->vp_ipaddr, buffer, 4);
break;
/*
*/
case PW_TYPE_IFID:
if (vp->length != 8) goto raw;
- /* vp->vp_ifid == vp->vp_octets */
+ memcpy(&vp->vp_ifid, buffer, 8);
break;
/*
*/
case PW_TYPE_IPV6ADDR:
if (vp->length != 16) goto raw;
- /* vp->vp_ipv6addr == vp->vp_octets */
+ memcpy(&vp->vp_ipv6addr, buffer, 16);
break;
/*
*/
case PW_TYPE_IPV6PREFIX:
if (vp->length < 2 || vp->length > 18) goto raw;
- if (vp->vp_octets[1] > 128) goto raw;
+ if (buffer[1] > 128) goto raw;
/*
* FIXME: double-check that
* (vp->vp_octets[1] >> 3) matches vp->length + 2
*/
+ memcpy(&vp->vp_ipv6prefix, buffer, vp->length);
if (vp->length < 18) {
- memset(vp->vp_octets + vp->length, 0,
+ memset(((uint8_t *)vp->vp_ipv6prefix) + vp->length, 0,
18 - vp->length);
}
break;
* Overload vp_integer for ntohl, which takes
* uint32_t, not int32_t
*/
- memcpy(&vp->vp_integer, vp->vp_octets, 4);
+ memcpy(&vp->vp_integer, buffer, 4);
vp->vp_integer = ntohl(vp->vp_integer);
- memcpy(&vp->vp_signed, &vp->vp_integer, 4);
break;
case PW_TYPE_TLV:
case PW_TYPE_COMBO_IP:
if (vp->length == 4) {
vp->type = PW_TYPE_IPADDR;
- memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+ memcpy(&vp->vp_ipaddr, buffer, 4);
break;
} else if (vp->length == 16) {
vp->type = PW_TYPE_IPV6ADDR;
- /* vp->vp_ipv6addr == vp->vp_octets */
+ memcpy(&vp->vp_ipv6addr, buffer, 16);
break;
}
}
-/*
- * Convert a top-level VSA to a VP.
+/**
+ * @brief Convert a top-level VSA to a VP.
*/
static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
return dv_type + dv_length + attrlen;
}
-/*
- * Convert one or more TLVs to VALUE_PAIRs. This function can
+/**
+ * @brief Convert one or more TLVs to VALUE_PAIRs. This function can
* be called recursively...
*/
static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
* The *entire* TLV is malformed.
*/
if (rad_tlv_ok(data, length, dv_type, dv_length) < 0) {
+ VP_TRACE("TLV malformed %u.%u\n", vendor, attribute);
return data2vp_raw(packet, original, secret,
attribute, vendor, data, length, pvp);
}
}
-/*
- * Group "continued" attributes together, and create VPs from them.
+/**
+ * @brief Group "continued" attributes together, and create VPs from them.
+ *
* The caller ensures that the RADIUS packet is OK, and that the
* continuations have all been checked.
*/
while (left > 0) {
#ifndef NDEBUG
if (data >= (start + length)) {
+ free(attr);
fr_strerror_printf("data2vp_continued: Internal sanity check failed");
return -1;
}
}
-/*
- * Create a "raw" VALUE_PAIR from a RADIUS attribute.
+/**
+ * @brief Create a "raw" VALUE_PAIR from a RADIUS attribute.
*/
ssize_t rad_attr2vp_raw(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
{
ssize_t my_len;
- if ((data[1] < 2) || (data[1] > length)) {
+ if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
fr_strerror_printf("rad_attr2vp_raw: Invalid length");
return -1;
}
}
-/*
- * Get the length of the data portion of all of the contiguous
+/**
+ * @brief Get the length of the data portion of all of the contiguous
* continued attributes.
- *
+ * @return
* 0 for "no continuation"
* -1 on malformed packets (continuation followed by non-wimax, etc.)
*/
-static ssize_t wimax_attrlen(const uint8_t *start, const uint8_t *end)
+static ssize_t wimax_attrlen(uint32_t vendor,
+ const uint8_t *start, const uint8_t *end)
{
- uint32_t lvalue = htonl(VENDORPEC_WIMAX);
ssize_t total;
const uint8_t *data = start;
if ((data[0] != PW_VENDOR_SPECIFIC) ||
(data[1] < 9) ||
- (memcmp(data + 2, &lvalue, 4) != 0) ||
+ (memcmp(data + 2, &vendor, 4) != 0) ||
(data[6] != start[6]) ||
((data[7] + 6) != data[1])) return -1;
}
-/*
- * Get the length of the data portion of all of the contiguous
+/**
+ * @brief Get the length of the data portion of all of the contiguous
* continued attributes.
*
- * 0 for "no continuation"
+ * @return
+ * 0 for "no continuation"
* -1 on malformed packets (continuation followed by non-wimax, etc.)
*/
static ssize_t extended_attrlen(const uint8_t *start, const uint8_t *end)
}
-/*
- * Create WiMAX VALUE_PAIRs from a RADIUS attribute.
+/**
+ * @brief Create WiMAX VALUE_PAIRs from a RADIUS attribute.
*/
ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
lvalue = ntohl(lvalue);
/*
- * Not WiMAX
+ * Not WiMAX format.
*/
if (lvalue != VENDORPEC_WIMAX) {
- fr_strerror_printf("rad_attr2vp_wimax: Not a WiMAX attribute");
- return -1;
+ DICT_VENDOR *dv;
+
+ dv = dict_vendorbyvalue(lvalue);
+ if (!dv || !dv->flags) {
+ fr_strerror_printf("rad_attr2vp_wimax: Not a WiMAX attribute");
+ return -1;
+ }
}
/*
* Attribute is continued. Do some more work.
*/
if (data[8] != 0) {
- my_len = wimax_attrlen(data, data + length);
+ my_len = wimax_attrlen(htonl(lvalue), data, data + length);
if (my_len < 0) {
return rad_attr2vp_raw(packet, original, secret,
data, length, pvp);
return data2vp_continued(packet, original, secret,
data, length, pvp, 0,
- data[6], VENDORPEC_WIMAX,
+ data[6], lvalue,
9, 9, my_len);
}
return data[1];
}
-/*
- * Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
+/**
+ * @brief Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
*/
ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
* WiMAX gets its own set of magic.
*/
if (lvalue == VENDORPEC_WIMAX) {
+ wimax:
return rad_attr2vp_wimax(packet, original, secret,
data, length, pvp);
}
if (dv) {
dv_type = dv->type;
dv_length = dv->length;
+
+ if (dv->flags) goto wimax;
}
/*
data + 6, data[1] - 6, pvp);
if (my_len < 0) return my_len;
-#ifndef NDEBUG
+ /*
+ * Incomplete decode means that something is wrong
+ * with the attribute. Back up, and make it "raw".
+ */
if (my_len != (data[1] - 6)) {
pairfree(pvp);
- fr_strerror_printf("rad_attr2vp_vsa: Incomplete decode");
- return -1;
+ return rad_attr2vp_raw(packet, original, secret,
+ data, length, pvp);
}
-#endif
return data[1];
}
-/*
- * Create an "extended" VALUE_PAIR from a RADIUS attribute.
+/**
+ * @brief Create an "extended" VALUE_PAIR from a RADIUS attribute.
*/
ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
}
da = dict_attrbyvalue(data[0], vendor);
- if (!da) {
+ if (!da ||
+ (!da->flags.extended && !da->flags.extended_flags)) {
fr_strerror_printf("rad_attr2vp_extended: Attribute is not extended format");
return -1;
}
/*
* Pack the *encapsulating* attribute number into
- * the vendor id.
+ * the vendor id. This number should be >= 241.
*/
vendor |= start[0] * FR_MAX_VENDOR;
shift = 0;
}
-/*
- * Create a "standard" RFC VALUE_PAIR from the given data.
+/**
+ * @brief Create a "standard" RFC VALUE_PAIR from the given data.
*/
ssize_t rad_attr2vp_rfc(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
return data[1];
}
-/*
- * Create a "normal" VALUE_PAIR from the given data.
+/**
+ * @brief Create a "normal" VALUE_PAIR from the given data.
*/
ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
const RADIUS_PACKET *original,
}
-/*
- * Calculate/check digest, and decode radius attributes.
- * Returns:
- * -1 on decoding error
- * 0 on success
+/**
+ * @brief Calculate/check digest, and decode radius attributes.
+ * @return -1 on decoding error, 0 on success
*/
int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
const char *secret)
}
-/*
- * Encode password.
+/**
+ * @brief Encode password.
*
* We assume that the passwd buffer passed is big enough.
* RFC2138 says the password is max 128 chars, so the size
return 0;
}
-/*
- * Decode password.
+/**
+ * @brief Decode password.
*/
int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
const uint8_t *vector)
}
-/*
- * Encode Tunnel-Password attributes when sending them out on the wire.
+/**
+ * @brief Encode Tunnel-Password attributes when sending them out on the wire.
*
* int *pwlen is updated to the new length of the encrypted
* password - a multiple of 16 bytes.
return 0;
}
-/*
- * Decode Tunnel-Password encrypted attributes.
+/**
+ * @brief Decode Tunnel-Password encrypted attributes.
*
* Defined in RFC-2868, this uses a two char SALT along with the
* initial intermediate value, to differentiate it from the
return reallen;
}
-/*
- * Encode a CHAP password
+/**
+ * @brief Encode a CHAP password
*
- * FIXME: might not work with Ascend because
+ * @bug FIXME: might not work with Ascend because
* we use vp->length, and Ascend gear likes
* to send an extra '\0' in the string!
*/
}
-/*
- * Seed the random number generator.
+/**
+ * @brief Seed the random number generator.
*
* May be called any number of times.
*/
}
-/*
- * Return a 32-bit random number.
+/**
+ * @brief Return a 32-bit random number.
*/
uint32_t fr_rand(void)
{
}
-/*
- * Allocate a new RADIUS_PACKET
+/**
+ * @brief Allocate a new RADIUS_PACKET
*/
RADIUS_PACKET *rad_alloc(int newvector)
{
}
-/*
- * Free a RADIUS_PACKET
+/**
+ * @brief Free a RADIUS_PACKET
*/
void rad_free(RADIUS_PACKET **radius_packet_ptr)
{