/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version. either
+ * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
#include <freeradius-devel/udpfromto.h>
#endif
+/*
+ * Some messages get printed out only in debugging mode.
+ */
+#define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf
+
#if 0
#define VP_TRACE printf
/*
- * The RFC says 4096 octets max, and most packets are less than 256.
- */
-#define MAX_PACKET_LEN 4096
-
-/*
* The maximum number of attributes which we allow in an incoming
* request. If there are more attributes than this, the request
* is rejected.
va_list ap;
va_start(ap, fmt);
- if ((fr_debug_flag == 0) || !fr_log_fp) {
+ if ((fr_debug_lvl == 0) || !fr_log_fp) {
va_end(ap);
return;
}
}
-void rad_print_hex(RADIUS_PACKET *packet)
+void rad_print_hex(RADIUS_PACKET const *packet)
{
int i;
if (!packet->data || !fr_log_fp) return;
- fprintf(fr_log_fp, " Code:\t\t%u\n", packet->data[0]);
+ fprintf(fr_log_fp, " Socket:\t%d\n", packet->sockfd);
+#ifdef WITH_TCP
+ fprintf(fr_log_fp, " Proto:\t%d\n", packet->proto);
+#endif
+
+ if (packet->src_ipaddr.af == AF_INET) {
+ char buffer[32];
+
+ fprintf(fr_log_fp, " Src IP:\t%s\n",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)));
+ fprintf(fr_log_fp, " port:\t%u\n", packet->src_port);
+
+ fprintf(fr_log_fp, " Dst IP:\t%s\n",
+ inet_ntop(packet->dst_ipaddr.af,
+ &packet->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)));
+ fprintf(fr_log_fp, " port:\t%u\n", packet->dst_port);
+ }
+
+ if (packet->data[0] < FR_MAX_PACKET_CODE) {
+ fprintf(fr_log_fp, " Code:\t\t(%d) %s\n", packet->data[0], fr_packet_codes[packet->data[0]]);
+ } else {
+ 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])));
(struct sockaddr *)&src, &sizeof_src);
}
-
+/** Basic validation of RADIUS packet header
+ *
+ * @note fr_strerror errors are only available if fr_debug_lvl > 0. This is to reduce CPU time
+ * consumed when discarding malformed packet.
+ *
+ * @param[in] sockfd we're reading from.
+ * @param[out] src_ipaddr of the packet.
+ * @param[out] src_port of the packet.
+ * @param[out] code Pointer to where to write the packet code.
+ * @return
+ * - -1 on failure.
+ * - 1 on decode error.
+ * - >= RADIUS_HDR_LEN on success. This is the packet length as specified in the header.
+ */
ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code)
{
ssize_t data_len, packet_len;
struct sockaddr_storage src;
socklen_t sizeof_src = sizeof(src);
- data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
- (struct sockaddr *)&src, &sizeof_src);
+ data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK, (struct sockaddr *)&src, &sizeof_src);
if (data_len < 0) {
if ((errno == EAGAIN) || (errno == EINTR)) return 0;
return -1;
}
/*
- * Too little data is available, discard the packet.
+ * Convert AF. If unknown, discard packet.
*/
- if (data_len < 4) {
+ if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
+ FR_DEBUG_STRERROR_PRINTF("Unknown address family");
rad_recv_discard(sockfd);
return 1;
+ }
- } else { /* we got 4 bytes of data. */
- /*
- * See how long the packet says it is.
- */
- packet_len = (header[2] * 256) + header[3];
-
- /*
- * The length in the packet says it's less than
- * a RADIUS header length: discard it.
- */
- if (packet_len < RADIUS_HDR_LEN) {
- rad_recv_discard(sockfd);
+ /*
+ * Too little data is available, discard the packet.
+ */
+ if (data_len < 4) {
+ FR_DEBUG_STRERROR_PRINTF("Expected at least 4 bytes of header data, got %zu bytes", data_len);
+invalid:
+ FR_DEBUG_STRERROR_PRINTF("Invalid data from %s: %s",
+ fr_inet_ntop(src_ipaddr->af, &src_ipaddr->ipaddr),
+ fr_strerror());
+ rad_recv_discard(sockfd);
- return 1;
+ return 1;
+ }
- /*
- * Enforce RFC requirements, for sanity.
- * Anything after 4k will be discarded.
- */
- } else if (packet_len > MAX_PACKET_LEN) {
- rad_recv_discard(sockfd);
+ /*
+ * See how long the packet says it is.
+ */
+ packet_len = (header[2] * 256) + header[3];
- return 1;
- }
+ /*
+ * The length in the packet says it's less than
+ * a RADIUS header length: discard it.
+ */
+ if (packet_len < RADIUS_HDR_LEN) {
+ FR_DEBUG_STRERROR_PRINTF("Expected at least " STRINGIFY(RADIUS_HDR_LEN) " bytes of packet "
+ "data, got %zu bytes", packet_len);
+ goto invalid;
}
/*
- * Convert AF. If unknown, discard packet.
+ * Enforce RFC requirements, for sanity.
+ * Anything after 4k will be discarded.
*/
- if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
- rad_recv_discard(sockfd);
-
- return 1;
+ if (packet_len > MAX_PACKET_LEN) {
+ FR_DEBUG_STRERROR_PRINTF("Length field value too large, expected maximum of "
+ STRINGIFY(MAX_PACKET_LEN) " bytes, got %zu bytes", packet_len);
+ goto invalid;
}
*code = header[0];
* encrypting passwords to RADIUS.
*/
static void make_secret(uint8_t *digest, uint8_t const *vector,
- char const *secret, uint8_t const *value)
+ char const *secret, uint8_t const *value, size_t length)
{
FR_MD5_CTX context;
- int i;
+ size_t i;
fr_md5_init(&context);
fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
fr_md5_final(digest, &context);
- for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
+ for ( i = 0; i < length; i++ ) {
digest[i] ^= value[i];
}
}
memcpy(output, passwd, len);
}
+
static void make_tunnel_passwd(uint8_t *output, ssize_t *outlen,
uint8_t const *input, size_t inlen, size_t room,
char const *secret, uint8_t const *vector)
{
FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
- uint8_t passwd[MAX_STRING_LEN + AUTH_VECTOR_LEN];
- int i, n;
- int len;
+ size_t i, n;
+ size_t encrypted_len;
/*
- * Be paranoid.
+ * The password gets encoded with a 1-byte "length"
+ * field. Ensure that it doesn't overflow.
*/
if (room > 253) room = 253;
/*
- * Account for 2 bytes of the salt, and round the room
- * available down to the nearest multiple of 16. Then,
- * subtract one from that to account for the length byte,
- * and the resulting number is the upper bound on the data
- * to copy.
- *
- * We could short-cut this calculation just be forcing
- * inlen to be no more than 239. It would work for all
- * VSA's, as we don't pack multiple VSA's into one
- * attribute.
- *
- * However, this calculation is more general, if a little
- * complex. And it will work in the future for all possible
- * kinds of weird attribute packing.
+ * Limit the maximum size of the input password. 2 bytes
+ * are taken up by the salt, and one by the encoded
+ * "length" field. Note that if we have a tag, the
+ * "room" will be 252 octets, not 253 octets.
*/
- room -= 2;
- room -= (room & 0x0f);
- room--;
-
- if (inlen > room) inlen = room;
+ if (inlen > (room - 3)) inlen = room - 3;
/*
- * Length of the encrypted data is password length plus
- * one byte for the length of the password.
+ * Length of the encrypted data is the clear-text
+ * password length plus one byte which encodes the length
+ * of the password. We round up to the nearest encoding
+ * block. Note that this can result in the encoding
+ * length being more than 253 octets.
*/
- len = inlen + 1;
- if ((len & 0x0f) != 0) {
- len += 0x0f;
- len &= ~0x0f;
+ encrypted_len = inlen + 1;
+ if ((encrypted_len & 0x0f) != 0) {
+ encrypted_len += 0x0f;
+ encrypted_len &= ~0x0f;
}
- *outlen = len + 2; /* account for the salt */
/*
- * Copy the password over.
+ * We need 2 octets for the salt, followed by the actual
+ * encrypted data.
+ */
+ if (encrypted_len > (room - 2)) encrypted_len = room - 2;
+
+ *outlen = encrypted_len + 2; /* account for the salt */
+
+ /*
+ * Copy the password over, and zero-fill the remainder.
*/
- memcpy(passwd + 3, input, inlen);
- memset(passwd + 3 + inlen, 0, sizeof(passwd) - 3 - inlen);
+ memcpy(output + 3, input, inlen);
+ memset(output + 3 + inlen, 0, *outlen - 3 - inlen);
/*
- * Generate salt. The RFC's say:
+ * Generate salt. The RFCs say:
*
* The high bit of salt[0] must be set, each salt in a
* packet should be unique, and they should be random
* So, we set the high bit, add in a counter, and then
* add in some CSPRNG data. should be OK..
*/
- passwd[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
+ output[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
(fr_rand() & 0x07));
- passwd[1] = fr_rand();
- passwd[2] = inlen; /* length of the password string */
+ output[1] = fr_rand();
+ output[2] = inlen; /* length of the password string */
fr_md5_init(&context);
fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
old = context;
fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
- fr_md5_update(&context, &passwd[0], 2);
+ fr_md5_update(&context, &output[0], 2);
+
+ for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) {
+ size_t block_len;
- for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
context = old;
fr_md5_update(&context,
- passwd + 2 + n - AUTH_PASS_LEN,
+ output + 2 + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
}
fr_md5_final(digest, &context);
- for (i = 0; i < AUTH_PASS_LEN; i++) {
- passwd[i + 2 + n] ^= digest[i];
+ if ((2 + n + AUTH_PASS_LEN) < room) {
+ block_len = AUTH_PASS_LEN;
+ } else {
+ block_len = room - 2 - n;
+ }
+
+ for (i = 0; i < block_len; i++) {
+ output[i + 2 + n] ^= digest[i];
}
}
- memcpy(output, passwd, len + 2);
}
static int do_next_tlv(VALUE_PAIR const *vp, VALUE_PAIR const *next, int nest)
ptr[1] += len;
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 3) && fr_log_fp) {
fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
print_hex_data(ptr + 2, len, 3);
}
}
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 3) && fr_log_fp) {
DICT_ATTR const *da;
da = dict_attrbyvalue(svp->da->attr & ((1 << fr_attr_shift[nest ]) - 1), svp->da->vendor);
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;
- }
+ if (!data) return 0;
break;
case PW_TYPE_IFID:
make_tunnel_passwd(ptr + lvalue, &len, data, len,
room - lvalue,
secret, original->vector);
+ len += lvalue;
break;
case PW_CODE_ACCOUNTING_REQUEST:
case PW_CODE_DISCONNECT_REQUEST:
case PW_CODE_COA_REQUEST:
ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
- make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
+ make_tunnel_passwd(ptr + 1, &len, data, len, room - 1,
secret, packet->vector);
+ len += lvalue;
break;
}
break;
* always fits.
*/
case FLAG_ENCRYPT_ASCEND_SECRET:
- if (len != 16) return 0;
- make_secret(ptr, packet->vector, secret, data);
+ if (len > AUTH_VECTOR_LEN) len = AUTH_VECTOR_LEN;
+ make_secret(ptr, packet->vector, secret, data, len);
len = AUTH_VECTOR_LEN;
break;
ptr[1] += len;
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 3) && fr_log_fp) {
int jump = 3;
fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
ptr[7] += len;
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 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],
memcpy(ptr + 2, p, left);
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 3) && fr_log_fp) {
fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
print_hex_data(ptr + 2, len, 3);
}
ptr[0] = attribute & 0xff;
ptr[1] = 2;
- if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
+ if (room > 255) room = 255;
- len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1], room);
+ len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1], room - ptr[1]);
if (len <= 0) return len;
ptr[1] += len;
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 3) && fr_log_fp) {
fprintf(fr_log_fp, "\t\t%02x %02x ", ptr[0], ptr[1]);
print_hex_data(ptr + 2, len, 3);
}
}
- if (room > ((unsigned) 255 - (dv->type + dv->length))) {
- room = 255 - (dv->type + dv->length);
- }
+ if (room > 255) room = 255;
len = vp2data_any(packet, original, secret, 0, pvp,
- ptr + dv->type + dv->length, room);
+ ptr + dv->type + dv->length, room - (dv->type + dv->length));
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) {
+ if ((fr_debug_lvl > 3) && fr_log_fp) {
switch (dv->type) {
default:
break;
case 4:
- if ((fr_debug_flag > 3) && fr_log_fp)
+ if ((fr_debug_lvl > 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)
+ if ((fr_debug_lvl > 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)
+ if ((fr_debug_lvl > 3) && fr_log_fp)
fprintf(fr_log_fp, "\t\t%02x ", ptr[0]);
break;
}
lvalue = htonl(vp->da->vendor);
memcpy(ptr + 2, &lvalue, 4);
- if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
+ if (room > 255) room = 255;
len = vp2attr_vsa(packet, original, secret, pvp,
vp->da->attr, vp->da->vendor,
- ptr + ptr[1], room);
+ ptr + ptr[1], room - ptr[1]);
if (len < 0) return len;
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 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[1] = 18;
memset(ptr + 2, 0, 16);
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) {
+ if ((fr_debug_lvl > 3) && fr_log_fp) {
fprintf(fr_log_fp, "\t\t50 12 ...\n");
}
#endif
* RFC format attributes take the fast path.
*/
if (!vp->da->vendor) {
- if (vp->da->attr > 255) return 0;
+ if (vp->da->attr > 255) {
+ *pvp = vp->next;
+ return 0;
+ }
return rad_vp2rfc(packet, original, secret, pvp,
start, room);
*/
reply = packet->vps;
while (reply) {
- size_t last_len;
+ size_t last_len, room;
char const *last_name = NULL;
VERIFY_VP(reply);
}
/*
+ * We allow zero-length strings in "unlang", but
+ * skip them (except for CUI, thanks WiMAX!) on
+ * all other attributes.
+ */
+ if (reply->vp_length == 0) {
+ if ((reply->da->vendor != 0) ||
+ ((reply->da->attr != PW_CHARGEABLE_USER_IDENTITY) &&
+ (reply->da->attr != PW_MESSAGE_AUTHENTICATOR))) {
+ reply = reply->next;
+ continue;
+ }
+ }
+
+ /*
* Set the Message-Authenticator to the correct
* length and initial value.
*/
- if (reply->da->attr == PW_MESSAGE_AUTHENTICATOR) {
+ if (!reply->da->vendor && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) {
/*
* Cache the offset to the
* Message-Authenticator
}
last_name = reply->da->name;
- len = rad_vp2attr(packet, original, secret, &reply, ptr,
- ((uint8_t *) data) + sizeof(data) - ptr);
+ room = ((uint8_t *) data) + sizeof(data) - ptr;
+
+ if (room <= 2) break;
+
+ len = rad_vp2attr(packet, original, secret, &reply, ptr, room);
if (len < 0) return -1;
/*
}
/*
+ * Set up the authentication vector with zero, or with
+ * the original vector, prior to signing.
+ */
+ switch (packet->code) {
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_COA_REQUEST:
+ memset(packet->vector, 0, AUTH_VECTOR_LEN);
+ break;
+
+ 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:
+ if (!original) {
+ fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
+ return -1;
+ }
+ memcpy(packet->vector, original->vector, AUTH_VECTOR_LEN);
+ break;
+
+ case PW_CODE_ACCESS_REQUEST:
+ case PW_CODE_STATUS_SERVER:
+ default:
+ break; /* packet->vector is already random bytes */
+ }
+
+#ifndef NDEBUG
+ if ((fr_debug_lvl > 3) && fr_log_fp) rad_print_hex(packet);
+#endif
+
+ /*
* If there's a Message-Authenticator, update it
- * now, BEFORE updating the authentication vector.
+ * now.
*/
if (packet->offset > 0) {
uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
case PW_CODE_DISCONNECT_NAK:
case PW_CODE_COA_REQUEST:
case PW_CODE_COA_ACK:
+ case PW_CODE_COA_NAK:
memset(hdr->vector, 0, AUTH_VECTOR_LEN);
break;
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");
- return -1;
- }
- memcpy(hdr->vector, original->vector,
- AUTH_VECTOR_LEN);
+ memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN);
break;
- default: /* others have vector already set to zero */
+ default:
break;
-
}
/*
(uint8_t const *) secret, strlen(secret));
memcpy(packet->data + packet->offset + 2,
calc_auth_vector, AUTH_VECTOR_LEN);
-
- /*
- * Copy the original request vector back
- * to the raw packet.
- */
- memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
}
/*
+ * Copy the request authenticator over to the packet.
+ */
+ memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
+
+ /*
* Switch over the packet code, deciding how to
* sign the packet.
*/
switch (packet->code) {
/*
- * Request packets are not signed, bur
+ * Request packets are not signed, but
* have a random authentication vector.
*/
case PW_CODE_ACCESS_REQUEST:
}
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+ if ((fr_debug_lvl > 3) && fr_log_fp) rad_print_hex(packet);
#endif
#ifdef WITH_TCP
bool seen_ma = false;
uint32_t num_attributes;
decode_fail_t failure = DECODE_FAIL_NONE;
+ bool eap = false;
+ bool non_eap = false;
/*
* Check for packets smaller than the packet header.
* "The minimum length is 20 ..."
*/
if (packet->data_len < RADIUS_HDR_LEN) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (received %zu < minimum %d)",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: too short (received %zu < minimum %d)",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
*/
if ((hdr->code == 0) ||
(hdr->code >= FR_MAX_PACKET_CODE)) {
- fr_strerror_printf("WARNING: Bad RADIUS packet from host %s: unknown packet code %d",
+ FR_DEBUG_STRERROR_PRINTF("Bad RADIUS packet from host %s: unknown packet code %d",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* "The minimum length is 20 ..."
*/
if (totallen < RADIUS_HDR_LEN) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (length %zu < minimum %d)",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: too short (length %zu < minimum %d)",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* i.e. No response to the NAS.
*/
if (packet->data_len < totallen) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: received %zu octets, packet length says %zu",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: received %zu octets, packet length says %zu",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* attribute header.
*/
if (count < 2) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute header overflows the packet",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: attribute header overflows the packet",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
* Attribute number zero is NOT defined.
*/
if (attr[0] == 0) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: Invalid attribute 0",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
* fields. Anything shorter is an invalid attribute.
*/
if (attr[1] < 2) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u too short",
+ FR_DEBUG_STRERROR_PRINTF("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)),
* attribute, it's a bad packet.
*/
if (count < attr[1]) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u data overflows the packet",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: attribute %u data overflows the packet",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
*/
case PW_EAP_MESSAGE:
require_ma = true;
+ eap = true;
+ break;
+
+ case PW_USER_PASSWORD:
+ case PW_CHAP_PASSWORD:
+ case PW_ARAP_PASSWORD:
+ non_eap = true;
break;
case PW_MESSAGE_AUTHENTICATOR:
if (attr[1] != 2 + AUTH_VECTOR_LEN) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* If not, we complain, and throw the packet away.
*/
if (count != 0) {
- fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
+ FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
*/
if ((fr_max_attributes > 0) &&
(num_attributes > fr_max_attributes)) {
- fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+ FR_DEBUG_STRERROR_PRINTF("Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* Message-Authenticator attributes.
*/
if (require_ma && !seen_ma) {
- fr_strerror_printf("WARNING: Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute",
+ FR_DEBUG_STRERROR_PRINTF("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)));
goto finish;
}
+ if (eap && non_eap) {
+ FR_DEBUG_STRERROR_PRINTF("Bad packet from host %s: Packet contains EAP-Message and non-EAP authentication attribute",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ host_ipaddr, sizeof(host_ipaddr)));
+ failure = DECODE_FAIL_TOO_MANY_AUTH;
+ goto finish;
+ }
+
/*
* Fill RADIUS header fields
*/
/** Receive UDP client requests, and fill in the basics of a RADIUS_PACKET structure
*
*/
-RADIUS_PACKET *rad_recv(int fd, int flags)
+RADIUS_PACKET *rad_recv(TALLOC_CTX *ctx, int fd, int flags)
{
int sock_flags = 0;
ssize_t data_len;
/*
* Allocate the new request data structure
*/
- packet = rad_alloc(NULL, false);
+ packet = rad_alloc(ctx, 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", fr_syserror(errno));
+ FR_DEBUG_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_DEBUG_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_DEBUG_STRERROR_PRINTF("Empty packet: Socket is not ready");
rad_free(&packet);
return NULL;
}
packet->vps = NULL;
#ifndef NDEBUG
- if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+ if ((fr_debug_lvl > 3) && fr_log_fp) rad_print_hex(packet);
#endif
return packet;
if (ptr[0] != attr) break;
}
- vp = pairalloc(ctx, da);
+ vp = fr_pair_afrom_da(ctx, da);
if (!vp) return -1;
vp->vp_length = total;
vp->vp_octets = p = talloc_array(vp, uint8_t, vp->vp_length);
if (!p) {
- pairfree(&vp);
+ fr_pair_list_free(&vp);
return -1;
}
/** Convert TLVs to one or more VPs
*
*/
-static ssize_t data2vp_tlvs(TALLOC_CTX *ctx,
+ssize_t rad_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,
my_vendor = da->vendor;
if (!dict_attr_child(da, &my_attr, &my_vendor)) {
- pairfree(&head);
+ fr_pair_list_free(&head);
return -1;
}
child = dict_unknown_afrom_fields(ctx, my_attr, my_vendor);
if (!child) {
- pairfree(&head);
+ fr_pair_list_free(&head);
return -1;
}
}
tlv_len = data2vp(ctx, packet, original, secret, child,
data + 2, data[1] - 2, data[1] - 2, tail);
if (tlv_len < 0) {
- pairfree(&head);
+ fr_pair_list_free(&head);
return -1;
}
if (*tail) tail = &((*tail)->next);
vsa_len = data2vp_vsa(ctx, packet, original, secret, dv,
data, attrlen, tail);
if (vsa_len < 0) {
- pairfree(&head);
+ fr_pair_list_free(&head);
fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
ssize_t rcode;
uint32_t vendor;
DICT_ATTR const *child;
- DICT_VENDOR *dv;
VALUE_PAIR *vp;
uint8_t const *data = start;
char *p;
packet->vector);
}
buffer[253] = '\0';
- datalen = strlen((char *) buffer);
+
+ /*
+ * MS-CHAP-MPPE-Keys are 24 octets, and
+ * encrypted. Since it's binary, we can't
+ * look for trailing zeros.
+ */
+ if (da->flags.length) {
+ if (datalen > da->flags.length) {
+ datalen = da->flags.length;
+ } /* else leave datalen alone */
+ } else {
+ /*
+ * Take off trailing zeros from the END.
+ * This allows passwords to have zeros in
+ * the middle of a field.
+ *
+ * However, if the password has a zero at
+ * the end, it will get mashed by this
+ * code. There's really no way around
+ * that.
+ */
+ while ((datalen > 0) && (buffer[datalen - 1] == '\0')) datalen--;
+ }
break;
/*
goto raw;
} else {
uint8_t my_digest[AUTH_VECTOR_LEN];
+ size_t secret_len;
+
+ secret_len = datalen;
+ if (secret_len > AUTH_VECTOR_LEN) secret_len = AUTH_VECTOR_LEN;
+
make_secret(my_digest,
original->vector,
- secret, data);
+ secret, data, secret_len);
memcpy(buffer, my_digest,
AUTH_VECTOR_LEN );
buffer[AUTH_VECTOR_LEN] = '\0';
memcpy(&vendor, data, 4);
vendor = ntohl(vendor);
- dv = dict_vendorbyvalue(vendor);
- if (!dv) {
- child = dict_unknown_afrom_fields(ctx, data[4], da->vendor | vendor);
- } else {
- child = dict_attrbyparent(da, data[4], vendor);
- if (!child) {
- child = dict_unknown_afrom_fields(ctx, data[4], da->vendor | vendor);
- }
+ vendor |= da->vendor;
+
+ child = dict_attrbyvalue(data[4], vendor);
+ if (!child) {
+ /*
+ * Create a "raw" attribute from the
+ * contents of the EVS VSA.
+ */
+ da = dict_unknown_afrom_fields(ctx, data[4], vendor);
+ data += 5;
+ datalen -= 5;
+ break;
}
- if (!child) goto raw;
rcode = data2vp(ctx, packet, original, secret, child,
data + 5, attrlen - 5, attrlen - 5, pvp);
* attribute, OR they've already been grouped
* into a contiguous memory buffer.
*/
- rcode = data2vp_tlvs(ctx, packet, original, secret, da,
- data, attrlen, pvp);
+ rcode = rad_data2vp_tlvs(ctx, packet, original, secret, da,
+ data, attrlen, pvp);
if (rcode < 0) goto raw;
return rcode;
* information, decode the actual data.
*/
alloc_cui:
- vp = pairalloc(ctx, da);
+ vp = fr_pair_afrom_da(ctx, da);
if (!vp) return -1;
vp->vp_length = datalen;
break;
case PW_TYPE_OCTETS:
- pairmemcpy(vp, data, vp->vp_length);
+ fr_pair_value_memcpy(vp, data, vp->vp_length);
break;
case PW_TYPE_ABINARY:
break;
default:
- pairfree(&vp);
+ fr_pair_list_free(&vp);
fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
switch (vp->da->type) {
case PW_TYPE_STRING:
case PW_TYPE_OCTETS:
- case PW_TYPE_TLV:
memcpy(out, &vp->data.ptr, sizeof(*out));
break;
case PW_TYPE_LONG_EXTENDED:
case PW_TYPE_EVS:
case PW_TYPE_VSA:
+ case PW_TYPE_TLV:
case PW_TYPE_TIMEVAL:
case PW_TYPE_MAX:
fr_strerror_printf("Cannot get data for VALUE_PAIR type %i", vp->da->type);
my_len = rad_attr2vp(packet, packet, original, secret,
ptr, packet_length, &vp);
if (my_len < 0) {
- pairfree(&head);
+ fr_pair_list_free(&head);
return -1;
}
(num_attributes > fr_max_attributes)) {
char host_ipaddr[128];
- pairfree(&head);
- fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+ fr_pair_list_free(&head);
+ fr_strerror_printf("Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
* This is per RFC-2868 which adds a two char SALT to the initial intermediate
* value MD5 hash.
*/
-int rad_tunnel_pwencode(char *passwd, size_t *pwlen, char const *secret,
- uint8_t const *vector)
+ssize_t rad_tunnel_pwencode(char *passwd, size_t *pwlen, char const *secret, uint8_t const *vector)
{
uint8_t buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 3];
unsigned char digest[AUTH_VECTOR_LEN];
if (len > 127) len = 127;
/*
- * Shift the password 3 positions right to place a salt and original
- * length, tag will be added automatically on packet send
+ * Shift the password 3 positions right to place a salt and original
+ * length, tag will be added automatically on packet send.
*/
- for (n=len ; n>=0 ; n--) passwd[n+3] = passwd[n];
+ for (n = len ; n >= 0 ; n--) passwd[n + 3] = passwd[n];
salt = passwd;
passwd += 2;
+
/*
- * save original password length as first password character;
+ * save original password length as first password character;
*/
*passwd = len;
len += 1;
* initial intermediate value, to differentiate it from the
* above.
*/
-int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret,
- uint8_t const *vector)
+ssize_t rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret, uint8_t const *vector)
{
FR_MD5_CTX context, old;
uint8_t digest[AUTH_VECTOR_LEN];
int secretlen;
- unsigned i, n, len, reallen;
+ size_t i, n, encrypted_len, reallen;
- len = *pwlen;
+ encrypted_len = *pwlen;
/*
* We need at least a salt.
*/
- if (len < 2) {
+ if (encrypted_len < 2) {
fr_strerror_printf("tunnel password is too short");
return -1;
}
* more data. So the 'data_len' field may be wrong,
* but that's ok...
*/
- if (len <= 3) {
+ if (encrypted_len <= 3) {
passwd[0] = 0;
*pwlen = 0;
return 0;
}
- len -= 2; /* discount the salt */
+ encrypted_len -= 2; /* discount the salt */
/*
* Use the secret to setup the decryption digest
fr_md5_update(&context, passwd, 2);
reallen = 0;
- for (n = 0; n < len; n += AUTH_PASS_LEN) {
- int base = 0;
+ for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) {
+ size_t base;
+ size_t block_len = AUTH_PASS_LEN;
+
+ /*
+ * Ensure we don't overflow the input on MD5
+ */
+ if ((n + 2 + AUTH_PASS_LEN) > *pwlen) {
+ block_len = *pwlen - n - 2;
+ }
if (n == 0) {
+ base = 1;
+
fr_md5_final(digest, &context);
context = old;
* 'data_len' field. Ensure it's sane.
*/
reallen = passwd[2] ^ digest[0];
- if (reallen >= len) {
+ if (reallen > encrypted_len) {
fr_strerror_printf("tunnel password is too long for the attribute");
return -1;
}
- fr_md5_update(&context, passwd + 2, AUTH_PASS_LEN);
+ fr_md5_update(&context, passwd + 2, block_len);
- base = 1;
} else {
+ base = 0;
+
fr_md5_final(digest, &context);
context = old;
- fr_md5_update(&context, passwd + n + 2, AUTH_PASS_LEN);
+ fr_md5_update(&context, passwd + n + 2, block_len);
}
- for (i = base; i < AUTH_PASS_LEN; i++) {
+ for (i = base; i < block_len; i++) {
passwd[n + i - 1] = passwd[n + i + 2] ^ digest[i];
}
}
- /*
- * See make_tunnel_password, above.
- */
- if (reallen > 239) reallen = 239;
-
*pwlen = reallen;
passwd[reallen] = 0;
* Use Chap-Challenge pair if present,
* Request Authenticator otherwise.
*/
- challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
+ challenge = fr_pair_find_by_num(packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
if (challenge) {
memcpy(ptr, challenge->vp_strvalue, challenge->vp_length);
i += challenge->vp_length;
VERIFY_PACKET(radius_packet);
- pairfree(&radius_packet->vps);
+ fr_pair_list_free(&radius_packet->vps);
talloc_free(radius_packet);
*radius_packet_ptr = NULL;
out->data = NULL;
out->data_len = 0;
- out->vps = paircopy(out, in->vps);
+ out->vps = fr_pair_list_copy(out, in->vps);
out->offset = 0;
return out;