26 RCSID(
"$Id: bb5ce533b6dde6e72c54a7d67756ced7ac36b4fe $")
28 #include <freeradius-devel/libradius.h>
30 #include <freeradius-devel/md5.h>
31 #include <freeradius-devel/udp.h>
37 #include <freeradius-devel/udpfromto.h>
51 #define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf
67 #define MAX_PACKET_LEN 4096
87 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";
89 for (i = 0; i < attrlen; i++) {
90 if ((i > 0) && ((i & 0x0f) == 0x00))
91 fprintf(fr_log_fp,
"%.*s", depth, tabs);
92 fprintf(fr_log_fp,
"%02x ", ptr[i]);
93 if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp,
"\n");
95 if ((i & 0x0f) != 0) fprintf(fr_log_fp,
"\n");
106 "Accounting-Request",
107 "Accounting-Response",
112 "Accounting-Message",
123 "Resource-Free-Request",
124 "Resource-Free-Response",
125 "Resource-Query-Request",
126 "Resource-Query-Response",
127 "Alternate-Resource-Reclaim-Request",
128 "NAS-Reboot-Request",
129 "NAS-Reboot-Response",
142 "Disconnect-Request",
152 "IP-Address-Allocate",
153 "IP-Address-Release",
166 vfprintf(fr_log_fp, fmt, ap);
176 if (!packet->
data || !fr_log_fp)
return;
178 fprintf(fr_log_fp,
" Socket:\t%d\n", packet->
sockfd);
180 fprintf(fr_log_fp,
" Proto:\t%d\n", packet->
proto);
184 char buffer[INET6_ADDRSTRLEN];
186 fprintf(fr_log_fp,
" Src IP:\t%s\n",
189 buffer,
sizeof(buffer)));
190 fprintf(fr_log_fp,
" port:\t%u\n", packet->
src_port);
192 fprintf(fr_log_fp,
" Dst IP:\t%s\n",
195 buffer,
sizeof(buffer)));
196 fprintf(fr_log_fp,
" port:\t%u\n", packet->
dst_port);
200 fprintf(fr_log_fp,
" Code:\t\t(%d) %s\n", packet->
data[0], fr_packet_codes[packet->
data[0]]);
202 fprintf(fr_log_fp,
" Code:\t\t%u\n", packet->
data[0]);
204 fprintf(fr_log_fp,
" Id:\t\t%u\n", packet->
data[1]);
205 fprintf(fr_log_fp,
" Length:\t%u\n", ((packet->
data[2] << 8) |
207 fprintf(fr_log_fp,
" Vector:\t");
208 for (i = 4; i < 20; i++) {
209 fprintf(fr_log_fp,
"%02x", packet->
data[i]);
211 fprintf(fr_log_fp,
"\n");
216 fprintf(fr_log_fp,
" Data:");
219 ptr = packet->
data + 20;
223 unsigned int vendor = 0;
225 fprintf(fr_log_fp,
"\t\t");
227 fprintf(fr_log_fp,
"%02x\n", *ptr);
231 if (ptr[1] > total) {
232 for (i = 0; i < total; i++) {
233 fprintf(fr_log_fp,
"%02x ", ptr[i]);
238 fprintf(fr_log_fp,
"%02x %02x ", ptr[0], ptr[1]);
239 attrlen = ptr[1] - 2;
241 if ((ptr[0] == PW_VENDOR_SPECIFIC) &&
243 vendor = (ptr[3] << 16) | (ptr[4] << 8) | ptr[5];
244 fprintf(fr_log_fp,
"%02x%02x%02x%02x (%u) ",
245 ptr[2], ptr[3], ptr[4], ptr[5], vendor);
279 fr_md5_update(&context, (uint8_t
const *) secret, strlen(secret));
301 ssize_t data_len, packet_len;
306 if ((errno == EAGAIN) || (errno == EINTR))
return 0;
314 char buffer[INET6_ADDRSTRLEN];
329 packet_len = (header[2] * 256) + header[3];
337 "data, got %zu bytes", packet_len);
369 if ((errno == EAGAIN) || (errno == EINTR))
return 0;
373 if (data_len == 0)
return -1;
375 packet->
data = talloc_array(packet, uint8_t, data_len);
376 if (!packet->
data)
return -1;
397 if (packet->
id < 0) {
404 fr_strerror_printf(
"ERROR: You must call fr_radius_encode() before fr_radius_sign()");
415 switch (packet->
code) {
454 (uint8_t
const *) secret, strlen(secret));
469 switch (packet->
code) {
512 if (!packet || (packet->
sockfd < 0)) {
552 if (packet->
proto == IPPROTO_TCP) {
556 if (rcode >= 0)
return rcode;
583 for (i = 0; i <
length; i++) {
584 result |= a[i] ^ b[i];
613 fr_md5_update(&context, (uint8_t
const *) secret, strlen(secret));
638 if (original == NULL) {
652 fr_md5_update(&context, (uint8_t
const *) secret, strlen(secret));
682 uint8_t
const *attr, *end;
692 totallen = (data[2] << 8) | data[3];
693 if (data_len < totallen)
return totallen;
698 end = data + totallen;
704 if ((end - attr) < 2)
return -(attr - data);
706 if (attr[0] == 0)
return -(attr -
data);
708 if (attr[1] < 2)
return - (attr + 1 -
data);
710 if ((attr + attr[1]) > end)
return -(attr + 1 - data);
737 char host_ipaddr[INET6_ADDRSTRLEN];
738 bool require_ma =
false;
739 bool seen_ma =
false;
740 uint32_t num_attributes;
754 host_ipaddr,
sizeof(host_ipaddr)),
766 totallen = (packet->
data[2] << 8) | packet->
data[3];
773 if ((hdr->
code == 0) ||
778 host_ipaddr,
sizeof(host_ipaddr)),
793 if (flags) require_ma =
true;
810 host_ipaddr,
sizeof(host_ipaddr)),
843 host_ipaddr,
sizeof(host_ipaddr)),
860 memset(packet->
data + totallen, 0, packet->
data_len - totallen);
889 host_ipaddr,
sizeof(host_ipaddr)));
901 host_ipaddr,
sizeof(host_ipaddr)));
914 host_ipaddr,
sizeof(host_ipaddr)),
924 if (count < attr[1]) {
928 host_ipaddr,
sizeof(host_ipaddr)),
945 if (attr[1] > 2) packet->
rounds = attr[2];
956 case PW_MESSAGE_AUTHENTICATOR:
961 host_ipaddr,
sizeof(host_ipaddr)),
990 host_ipaddr,
sizeof(host_ipaddr)));
1000 if ((fr_max_attributes > 0) &&
1001 (num_attributes > fr_max_attributes)) {
1002 FR_DEBUG_STRERROR_PRINTF(
"Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
1005 host_ipaddr,
sizeof(host_ipaddr)),
1006 num_attributes, fr_max_attributes);
1022 if (require_ma && !seen_ma) {
1023 FR_DEBUG_STRERROR_PRINTF(
"Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute",
1026 host_ipaddr,
sizeof(host_ipaddr)));
1035 packet->
id = hdr->
id;
1071 #ifdef WITH_VERIFY_PTR
1150 char buffer[INET6_ADDRSTRLEN];
1152 if (!packet || !packet->
data)
return -1;
1160 while (length > 0) {
1174 case PW_MESSAGE_AUTHENTICATOR:
1175 memcpy(msg_auth_vector, &ptr[2],
sizeof(msg_auth_vector));
1178 switch (packet->
code) {
1204 "packet without a request packet");
1212 (uint8_t
const *) secret, strlen(secret));
1214 sizeof(calc_auth_vector)) != 0) {
1216 "(Shared secret is incorrect.)",
1219 buffer,
sizeof(buffer)));
1242 "from client %s port %d: Cannot validate Request/Response Authenticator.",
1246 buffer,
sizeof(buffer)),
1254 switch (packet->
code) {
1268 "from client %s with invalid Request Authenticator! "
1269 "(Shared secret is incorrect.)",
1270 fr_packet_codes[packet->
code],
1273 buffer,
sizeof(buffer)));
1290 "from home server %s port %d with invalid Response Authenticator! "
1291 "(Shared secret is incorrect.)",
1292 fr_packet_codes[packet->
code],
1295 buffer,
sizeof(buffer)),
1303 "from client %s port %d: Cannot validate Request/Response Authenticator",
1307 buffer,
sizeof(buffer)),
1323 uint16_t total_length;
1337 switch (packet->
code) {
1370 hdr->
id = packet->
id;
1388 char const *last_name = NULL;
1405 if (vp->
da->
attr == PW_RAW_ATTRIBUTE) {
1406 memcpy(ptr, vp->vp_octets, vp->vp_length);
1407 len = vp->vp_length;
1420 if (!vp->
da->
vendor && (vp->
da->
attr == PW_MESSAGE_AUTHENTICATOR)) {
1425 packet->
offset = total_length;
1428 last_len = vp->vp_length;
1430 last_name = vp->
da->
name;
1433 if (len < 0)
return -1;
1440 if (last_len != 0) {
1452 total_length += len;
1464 packet->
data = talloc_array(packet, uint8_t, packet->
data_len);
1465 if (!packet->
data) {
1473 total_length = htons(total_length);
1474 memcpy(hdr->
length, &total_length,
sizeof(total_length));
1488 uint32_t num_attributes;
1511 while (packet_length > 0) {
1535 if ((fr_max_attributes > 0) && (num_attributes > fr_max_attributes)) {
1536 char host_ipaddr[INET6_ADDRSTRLEN];
1539 fr_strerror_printf(
"Possible DoS attack from host %s: Too many attributes in request "
1540 "(received %d, max %d are allowed)",
1543 host_ipaddr,
sizeof(host_ipaddr)),
1544 num_attributes, fr_max_attributes);
1549 packet_length -= my_len;
1576 if (!fr_rand_initialized) {
1579 memset(&fr_rand_pool, 0,
sizeof(fr_rand_pool));
1581 fd = open(
"/dev/urandom", O_RDONLY);
1587 while (total <
sizeof(fr_rand_pool.randrsl)) {
1588 this = read(fd, fr_rand_pool.randrsl,
1589 sizeof(fr_rand_pool.randrsl) - total);
1590 if ((
this < 0) && (errno != EINTR))
break;
1591 if (
this > 0) total +=
this;
1595 fr_rand_pool.randrsl[0] = fd;
1596 fr_rand_pool.randrsl[1] = time(NULL);
1597 fr_rand_pool.randrsl[2] = errno;
1601 fr_rand_pool.randcnt = 0;
1602 fr_rand_initialized = 1;
1614 fr_rand_pool.randmem[fr_rand_pool.randcnt] ^=
hash;
1628 if (!fr_rand_initialized) {
1632 num = fr_rand_pool.randrsl[fr_rand_pool.randcnt++];
1633 if (fr_rand_pool.randcnt >= 256) {
1634 fr_rand_pool.randcnt = 0;
1665 uint32_t
hash, base;
1674 memcpy(rp->
vector + i, &hash,
sizeof(hash));
1695 if (!packet)
return NULL;
1698 if (!reply)
return NULL;
1709 reply->
id = packet->
id;
1731 if (!radius_packet_ptr || !*radius_packet_ptr)
return;
1732 radius_packet = *radius_packet_ptr;
1738 talloc_free(radius_packet);
1739 *radius_packet_ptr = NULL;
1756 if (!out)
return NULL;
1761 memcpy(out, in,
sizeof(*out));
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
int sockfd
Socket this packet was read from.
int fr_radius_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original, char const *secret)
Sign a previously encoded packet.
int id
Packet ID (used to link requests/responses).
struct timeval timestamp
When we received the packet.
ssize_t udp_recv_peek(int sockfd, void *data, size_t data_len, int flags, fr_ipaddr_t *src_ipaddr, uint16_t *src_port)
Peek at the header of a UDP packet.
#define FR_MAX_PACKET_CODE
int fr_radius_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, void *encoder_ctx)
Encode a data structure into a RADIUS attribute.
VALUE_PAIR * fr_cursor_last(vp_cursor_t *cursor)
Wind cursor to the last pair in the list.
RFC2865 - Access-Challenge.
RADIUS_PACKET * radius_packet(REQUEST *request, pair_lists_t list_name)
Resolve a list to the RADIUS_PACKET holding the HEAD pointer for a VALUE_PAIR list.
RADIUS_PACKET * fr_radius_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet)
Allocate a new RADIUS_PACKET response.
fr_ipaddr_t src_ipaddr
Src IP address of packet.
char const * fr_packet_codes[FR_MAX_PACKET_CODE]
#define FR_DEBUG_STRERROR_PRINTF
void fr_md5_init(FR_MD5_CTX *ctx)
Initialise a new MD5 context.
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
FR_NAME_NUMBER const fr_request_types[]
void fr_rand_seed(void const *data, size_t size)
Seed the random number generator.
RADIUS_PACKET * fr_radius_copy(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
Duplicate a RADIUS_PACKET.
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
static int calc_acctdigest(RADIUS_PACKET *packet, char const *secret)
Validates the requesting client NAS.
void fr_radius_make_secret(uint8_t *digest, uint8_t const *vector, char const *secret, uint8_t const *value)
Build an encrypted secret value to return in a reply packet.
void fr_randinit(fr_randctx *ctx, int flag)
static void print_hex_data(uint8_t const *ptr, int attrlen, int depth)
uint32_t fr_rand(void)
Return a 32-bit random number.
uint8_t * data
Packet data (body).
RFC3575/RFC5176 - Disconnect-Ack (positive)
uint16_t dst_port
DST Port of packet.
uint16_t src_port
Src port of packet.
fr_ipaddr_t dst_ipaddr
Dst IP address of packet.
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
fr_dict_attr_flags_t flags
Flags.
static ssize_t rad_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags)
Wrapper for recvfrom, which handles recvfromto, IPv6, and all possible combinations.
static unsigned int hash(char const *username, unsigned int tablesize)
RFC2866 - Accounting-Response.
uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen) CC_BOUNDED(__string__
RFC3575/RFC5176 - CoA-Ack (positive)
RFC2865 - Access-Request.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
VALUE_PAIR * fr_pair_list_copy(TALLOC_CTX *ctx, VALUE_PAIR *from)
Copy a pairlist.
static _fr_thread_local bool fr_rand_initialized
void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *text, size_t text_len, uint8_t const *key, size_t key_len) CC_BOUNDED(__minbytes__
void fr_printf_log(char const *fmt,...)
void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *vp)
Merges multiple VALUE_PAIR into the cursor.
RFC2866 - Accounting-Request.
int fr_radius_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
Verify the Request/Response Authenticator (and Message-Authenticator if present) of a packet...
ssize_t udp_recv(int sockfd, void *data, size_t data_len, int flags, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, fr_ipaddr_t *dst_ipaddr, uint16_t *dst_port, int *if_index, struct timeval *when)
unsigned int attr
Attribute number.
ssize_t udp_send(int sockfd, void *data, size_t data_len, int flags, fr_ipaddr_t *src_ipaddr, uint16_t src_port, int if_index, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
union fr_ipaddr_t::@1 ipaddr
RFC3575/RFC5176 - CoA-Nak (not willing to perform)
unsigned int code
Packet code (type).
RADIUS_PACKET * fr_radius_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new RADIUS_PACKET.
unsigned int vendor
Vendor that defines this attribute.
Stores an attribute, a value and various bits of other data.
uint32_t fr_max_attributes
VALUE_PAIR * fr_cursor_current(vp_cursor_t *cursor)
Return the VALUE_PAIR the cursor current points to.
void fr_isaac(fr_randctx *ctx)
#define VERIFY_PACKET(_x)
unsigned int internal
Internal attribute, should not be received in protocol packets, should not be encoded.
RFC3575/RFC5176 - CoA-Request.
uint8_t vector[AUTH_VECTOR_LEN]
RADIUS authentication vector.
char const * fr_strerror(void)
Get the last library error.
bool fr_radius_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
See if the data pointed to by PTR is a valid RADIUS packet.
Packet code has not been set.
int if_index
Index of receiving interface.
RADIUS_PACKET const * original
ssize_t fr_radius_len(uint8_t const *data, size_t data_len)
See how big of a packet is in the buffer.
struct radius_packet_t radius_packet_t
int fr_radius_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original, char const *secret)
Reply to the request.
uint32_t rounds
for State[0]
char name[1]
Attribute name.
ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len, void *decoder_ctx)
Create a "normal" VALUE_PAIR from the given data.
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
size_t data_len
Length of packet data.
int fr_radius_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, char const *secret)
Encode a packet.
static char const * secret
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
int fr_radius_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
Do a comparison of two authentication digests by comparing the FULL digest.
RFC2865/RFC5997 - Status Server (request)
void void fr_md5_final(uint8_t out[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx) CC_BOUNDED(__minbytes__
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
RADIUS_PACKET const * packet
ssize_t fr_radius_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, unsigned int *code)
Basic validation of RADIUS packet header.
RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
uint8_t vector[AUTH_VECTOR_LEN]
RADIUS_PACKET * fr_radius_recv(TALLOC_CTX *ctx, int fd, int flags)
Receive UDP client requests, and fill in the basics of a RADIUS_PACKET structure. ...
fr_dict_t * fr_dict_internal
Internal server dictionary.
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
void fr_radius_free(RADIUS_PACKET **radius_packet_ptr)
Free a RADIUS_PACKET.
void udp_recv_discard(int sockfd)
Discard the next UDP packet.
int fr_radius_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
Calculate/check digest, and decode radius attributes.
static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
Validates the requesting client NAS.
RFC3575/RFC5176 - Disconnect-Request.
void fr_radius_print_hex(RADIUS_PACKET *packet)
static _fr_thread_local fr_randctx fr_rand_pool
A pool of pre-generated random integers.