Coverage Report

Created: 2026-06-30 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/protocols/radius/packet.c
Line
Count
Source
1
/*
2
 *   This library is free software; you can redistribute it and/or
3
 *   modify it under the terms of the GNU Lesser General Public
4
 *   License as published by the Free Software Foundation; either
5
 *   version 2.1 of the License, or (at your option) any later version.
6
 *
7
 *   This library is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
 *   Lesser General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU Lesser General Public
13
 *   License along with this library; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: df2c6f246ce53d8bbd4be4add2edfb6831c3855e $
19
 *
20
 * @file protocols/radius/packet.c
21
 * @brief Functions to deal with fr_packet_t data structures.
22
 *
23
 * @copyright 2000-2017 The FreeRADIUS server project
24
 */
25
RCSID("$Id: df2c6f246ce53d8bbd4be4add2edfb6831c3855e $")
26
27
#include "attrs.h"
28
29
#include <freeradius-devel/util/udp.h>
30
#include <freeradius-devel/util/syserror.h>
31
32
#include <fcntl.h>
33
34
/*
35
 *  Some messages get printed out only in debugging mode.
36
 */
37
0
#define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf
38
39
40
/** Encode a packet
41
 *
42
 */
43
ssize_t fr_radius_packet_encode(fr_packet_t *packet, fr_pair_list_t *list,
44
        fr_packet_t const *original, char const *secret)
45
0
{
46
0
  ssize_t slen;
47
0
  fr_radius_ctx_t common = {};
48
0
  fr_radius_encode_ctx_t packet_ctx;
49
50
  /*
51
   *  A 4K packet, aligned on 64-bits.
52
   */
53
0
  uint8_t data[MAX_PACKET_LEN];
54
55
0
#ifndef NDEBUG
56
0
  if (fr_debug_lvl >= L_DBG_LVL_4) fr_packet_log_hex(&default_log, packet);
57
0
#endif
58
59
0
  common.secret = secret;
60
0
  common.secret_length = talloc_strlen(secret);
61
62
0
  packet_ctx = (fr_radius_encode_ctx_t) {
63
0
    .common = &common,
64
0
    .request_authenticator = original ? original->data + 4 : NULL,
65
0
    .rand_ctx = (fr_fast_rand_t) {
66
0
      .a = fr_rand(),
67
0
      .b = fr_rand(),
68
0
    },
69
0
    .request_code = original ? original->data[0] : 0,
70
0
    .code = packet->code,
71
0
    .id = packet->id,
72
0
  };
73
74
0
  slen = fr_radius_encode(&FR_DBUFF_TMP(data, sizeof(data)), list, &packet_ctx);
75
0
  if (slen < 0) return slen;
76
77
  /*
78
   *  Fill in the rest of the fields, and copy the data over
79
   *  from the local stack to the newly allocated memory.
80
   *
81
   *  Yes, all this 'memcpy' is slow, but it means
82
   *  that we only allocate the minimum amount of
83
   *  memory for a request.
84
   */
85
0
  packet->data_len = (size_t) slen;
86
0
  packet->data = talloc_array(packet, uint8_t, packet->data_len);
87
0
  if (!packet->data) {
88
0
    fr_strerror_const("Out of memory");
89
0
    return -1;
90
0
  }
91
92
0
  memcpy(packet->data, data, packet->data_len);
93
94
0
  return 0;
95
0
}
96
97
/** See if the data pointed to by PTR is a valid RADIUS packet.
98
 *
99
 * Packet is not 'const * const' because we may update data_len, if there's more data
100
 * in the UDP packet than in the RADIUS packet.
101
 *
102
 * @param[in] packet    to check.
103
 * @param[in] max_attributes  to decode.
104
 * @param[in] require_message_authenticator to require Message-Authenticator.
105
 * @param[out] reason   if not NULL, will have the failure reason written to where it points.
106
 * @return
107
 *  - True on success.
108
 *  - False on failure.
109
 */
110
bool fr_packet_ok(fr_packet_t *packet, uint32_t max_attributes, bool require_message_authenticator, fr_radius_decode_fail_t *reason)
111
0
{
112
0
  if (!fr_radius_ok(packet->data, &packet->data_len, max_attributes, require_message_authenticator, reason)) {
113
0
    return false;
114
0
  }
115
116
  /*
117
   *  Fill RADIUS header fields
118
   */
119
0
  packet->code = packet->data[0];
120
0
  packet->id = packet->data[1];
121
0
  memcpy(packet->vector, packet->data + 4, sizeof(packet->vector));
122
0
  return true;
123
0
}
124
125
126
/** Verify the Request/Response Authenticator (and Message-Authenticator if present) of a packet
127
 *
128
 */
129
int fr_radius_packet_verify(fr_packet_t *packet, fr_packet_t *original, char const *secret)
130
0
{
131
0
  char    buffer[INET6_ADDRSTRLEN];
132
133
0
  if (!packet->data) return -1;
134
135
0
  if (fr_radius_verify(packet->data, original ? original->data + 4 : NULL,
136
0
           (uint8_t const *) secret, talloc_strlen(secret), false, false) < 0) {
137
0
    fr_strerror_printf_push("Received invalid packet from %s",
138
0
          inet_ntop(packet->socket.inet.src_ipaddr.af, &packet->socket.inet.src_ipaddr.addr,
139
0
              buffer, sizeof(buffer)));
140
0
    return -1;
141
0
  }
142
143
0
  return 0;
144
0
}
145
146
147
/** Sign a previously encoded packet
148
 *
149
 */
150
int fr_radius_packet_sign(fr_packet_t *packet, fr_packet_t const *original,
151
        char const *secret)
152
0
{
153
0
  int ret;
154
155
0
  ret = fr_radius_sign(packet->data, original ? original->data + 4 : NULL,
156
0
             (uint8_t const *) secret, talloc_strlen(secret));
157
0
  if (ret < 0) return ret;
158
159
0
  memcpy(packet->vector, packet->data + 4, RADIUS_AUTH_VECTOR_LENGTH);
160
0
  return 0;
161
0
}
162
163
164
/** Wrapper for recvfrom, which handles recvfromto, IPv6, and all possible combinations
165
 *
166
 */
167
static ssize_t rad_recvfrom(int sockfd, fr_packet_t *packet, int flags)
168
0
{
169
0
  ssize_t     data_len;
170
171
0
  data_len = fr_radius_recv_header(sockfd, &packet->socket.inet.src_ipaddr, &packet->socket.inet.src_port, &packet->code);
172
0
  if (data_len < 0) {
173
0
    if ((errno == EAGAIN) || (errno == EINTR)) return 0;
174
0
    return -1;
175
0
  }
176
177
0
  if (data_len == 0) return -1; /* invalid packet */
178
179
0
  packet->data = talloc_array(packet, uint8_t, data_len);
180
0
  if (!packet->data) return -1;
181
182
0
  packet->data_len = data_len;
183
184
0
  return udp_recv(sockfd, flags, &packet->socket, packet->data, packet->data_len, &packet->timestamp);
185
0
}
186
187
188
/** Receive UDP client requests, and fill in the basics of a fr_packet_t structure
189
 *
190
 */
191
fr_packet_t *fr_packet_recv(TALLOC_CTX *ctx, int fd, int flags, uint32_t max_attributes, bool require_message_authenticator)
192
0
{
193
0
  ssize_t     data_len;
194
0
  fr_packet_t *packet;
195
196
  /*
197
   *  Allocate the new request data structure
198
   */
199
0
  packet = fr_packet_alloc(ctx, false);
200
0
  if (!packet) {
201
0
    fr_strerror_const("out of memory");
202
0
    return NULL;
203
0
  }
204
205
0
  data_len = rad_recvfrom(fd, packet, flags);
206
0
  if (data_len < 0) {
207
0
    FR_DEBUG_STRERROR_PRINTF("Error receiving packet: %s", fr_syserror(errno));
208
0
    fr_packet_free(&packet);
209
0
    return NULL;
210
0
  }
211
212
0
#ifdef WITH_VERIFY_PTR
213
  /*
214
   *  Double-check that the fields we want are filled in.
215
   */
216
0
  if ((packet->socket.inet.src_ipaddr.af == AF_UNSPEC) ||
217
0
      (packet->socket.inet.src_port == 0) ||
218
0
      (packet->socket.inet.dst_ipaddr.af == AF_UNSPEC) ||
219
0
      (packet->socket.inet.dst_port == 0)) {
220
0
    FR_DEBUG_STRERROR_PRINTF("Error receiving packet: %s", fr_syserror(errno));
221
0
    fr_packet_free(&packet);
222
0
    return NULL;
223
0
  }
224
0
#endif
225
226
0
  packet->data_len = data_len; /* unsigned vs signed */
227
228
  /*
229
   *  If the packet is too big, then rad_recvfrom did NOT
230
   *  allocate memory.  Instead, it just discarded the
231
   *  packet.
232
   */
233
0
  if (packet->data_len > MAX_PACKET_LEN) {
234
0
    FR_DEBUG_STRERROR_PRINTF("Discarding packet: Larger than RFC limitation of 4096 bytes");
235
0
    fr_packet_free(&packet);
236
0
    return NULL;
237
0
  }
238
239
  /*
240
   *  Read no data.  Continue.
241
   *  This check is AFTER the MAX_PACKET_LEN check above, because
242
   *  if the packet is larger than MAX_PACKET_LEN, we also have
243
   *  packet->data == NULL
244
   */
245
0
  if ((packet->data_len == 0) || !packet->data) {
246
0
    FR_DEBUG_STRERROR_PRINTF("Empty packet: Socket is not ready");
247
0
    fr_packet_free(&packet);
248
0
    return NULL;
249
0
  }
250
251
  /*
252
   *  See if it's a well-formed RADIUS packet.
253
   */
254
0
  if (!fr_packet_ok(packet, max_attributes, require_message_authenticator, NULL)) {
255
0
    fr_packet_free(&packet);
256
0
    return NULL;
257
0
  }
258
259
  /*
260
   *  Remember which socket we read the packet from.
261
   */
262
0
  packet->socket.fd = fd;
263
264
  /*
265
   *  FIXME: Do even more filtering by only permitting
266
   *  certain IP's.  The problem is that we don't know
267
   *  how to do this properly for all possible clients...
268
   */
269
270
0
  return packet;
271
0
}
272
273
/** Reply to the request
274
 *
275
 * Also attach reply attribute value pairs and any user message provided.
276
 */
277
int fr_radius_packet_send(fr_packet_t *packet, fr_pair_list_t *list,
278
        fr_packet_t const *original, char const *secret)
279
0
{
280
  /*
281
   *  Maybe it's a fake packet.  Don't send it.
282
   */
283
0
  if (packet->socket.fd < 0) {
284
0
    return 0;
285
0
  }
286
287
  /*
288
   *  First time through, allocate room for the packet
289
   */
290
0
  if (!packet->data) {
291
    /*
292
     *  Encode the packet.
293
     */
294
0
    if (fr_radius_packet_encode(packet, list, original, secret) < 0) {
295
0
      return -1;
296
0
    }
297
298
    /*
299
     *  Re-sign it, including updating the
300
     *  Message-Authenticator.
301
     */
302
0
    if (fr_radius_packet_sign(packet, original, secret) < 0) {
303
0
      return -1;
304
0
    }
305
306
    /*
307
     *  If packet->data points to data, then we print out
308
     *  the VP list again only for debugging.
309
     */
310
0
  }
311
312
  /*
313
   *  If the socket is TCP, call write().  Calling sendto()
314
   *  is allowed on some platforms, but it's not nice.
315
   */
316
0
  if (packet->socket.type == SOCK_STREAM) {
317
0
    ssize_t ret;
318
319
0
    ret = write(packet->socket.fd, packet->data, packet->data_len);
320
0
    if (ret >= 0) return ret;
321
322
0
    fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
323
0
    return -1;
324
0
  }
325
326
  /*
327
   *  And send it on it's way.
328
   *
329
   *  No need to call fr_socket_addr_swap as apparently
330
   *  the address is already inverted.
331
   */
332
0
  return udp_send(&packet->socket, 0, packet->data, packet->data_len);
333
0
}
334
335
void _fr_packet_log_hex(fr_log_t const *log, fr_packet_t const *packet, char const *file, int line)
336
0
{
337
0
  uint8_t const *attr, *end;
338
0
  char buffer[1024];
339
340
0
  if (!packet->data) return;
341
342
0
  fr_log(log, L_DBG, file, line, "  Socket   : %d", packet->socket.fd);
343
0
  fr_log(log, L_DBG, file, line, "  Proto    : %d", (packet->socket.type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP);
344
345
0
  if ((packet->socket.inet.src_ipaddr.af == AF_INET) || (packet->socket.inet.src_ipaddr.af == AF_INET6)) {
346
0
    fr_log(log, L_DBG, file, line, "  Src IP   : %pV", fr_box_ipaddr(packet->socket.inet.src_ipaddr));
347
0
    fr_log(log, L_DBG, file, line, "  Src Port : %u", packet->socket.inet.src_port);
348
0
    fr_log(log, L_DBG, file, line, "  Dst IP   : %pV", fr_box_ipaddr(packet->socket.inet.dst_ipaddr));
349
0
    fr_log(log, L_DBG, file, line, "  Dst Port : %u", packet->socket.inet.dst_port);
350
0
  }
351
352
0
       if ((packet->data[0] > 0) && (packet->data[0] < FR_RADIUS_CODE_MAX)) {
353
0
               fr_log(log, L_DBG, file, line, "  Code     : %s", fr_radius_packet_name[packet->data[0]]);
354
0
       } else {
355
0
               fr_log(log, L_DBG, file, line, "  Code     : %u", packet->data[0]);
356
0
       }
357
358
0
       fr_log(log, L_DBG, file, line, "  Id       : %u", packet->data[1]);
359
0
       fr_log(log, L_DBG, file, line, "  Length   : %u", fr_nbo_to_uint16(packet->data + 2));
360
0
       fr_log(log, L_DBG, file, line, "  Vector   : %pH", fr_box_octets(packet->data + 4, RADIUS_AUTH_VECTOR_LENGTH));
361
362
0
       if (packet->data_len <= 20) return;
363
364
0
       for (attr = packet->data + 20, end = packet->data + packet->data_len;
365
0
            attr < end;
366
0
            attr += attr[1]) {
367
0
               int    i, len, offset = 2;
368
0
               unsigned int vendor = 0;
369
0
         char   *p;
370
0
         char const *truncated = "";
371
372
         /*
373
    * rad_packet_ok() already checks, but let's do defense in depth.
374
    */
375
0
               if (attr[1] < 2) break;
376
377
0
         snprintf(buffer, sizeof(buffer), "%02x %02x  ", attr[0], attr[1]);
378
0
         p = buffer + strlen(buffer);
379
0
               if ((attr[0] == FR_VENDOR_SPECIFIC) &&
380
0
                   (attr[1] > 6)) {
381
0
                       vendor = fr_nbo_to_uint32(attr + 2);
382
383
0
           snprintf(p, buffer + sizeof(buffer) - p, "%02x%02x%02x%02x (%u)  ",
384
0
        attr[2], attr[3], attr[4], attr[5], vendor);
385
0
                       offset = 6;
386
0
           p += strlen(p);
387
0
               }
388
389
0
         len = attr[1] - offset;
390
0
         if (len > 15) {
391
0
           len = 15;
392
0
           truncated = "...";
393
0
         }
394
395
0
         for (i = 0; i < len; i++) {
396
0
           snprintf(p, buffer + sizeof(buffer) - p, "%02x ", attr[offset + i]);
397
0
           p += 3;
398
0
         }
399
400
0
         fr_log(log, L_DBG, file, line, "      %s%s\n", buffer, truncated);
401
0
       }
402
0
}
403
404
/*
405
 *  Debug the packet if requested.
406
 */
407
void fr_radius_packet_header_log(fr_log_t const *log, fr_packet_t *packet, bool received)
408
0
{
409
0
  char src_ipaddr[FR_IPADDR_STRLEN];
410
0
  char dst_ipaddr[FR_IPADDR_STRLEN];
411
0
#ifdef WITH_IFINDEX_NAME_RESOLUTION
412
0
  char if_name[IFNAMSIZ];
413
0
#endif
414
415
0
  if (!log) return;
416
0
  if (!packet) return;
417
418
  /*
419
   *  Client-specific debugging re-prints the input
420
   *  packet into the client log.
421
   *
422
   *  This really belongs in a utility library
423
   */
424
0
  if (FR_RADIUS_PACKET_CODE_VALID(packet->code)) {
425
0
    fr_log(log, L_DBG, __FILE__, __LINE__,
426
0
           "%s %s Id %i from %s%s%s:%i to %s%s%s:%i "
427
0
#ifdef WITH_IFINDEX_NAME_RESOLUTION
428
0
           "%s%s%s"
429
0
#endif
430
0
           "length %zu\n",
431
0
            received ? "Received" : "Sent",
432
0
            fr_radius_packet_name[packet->code],
433
0
            packet->id,
434
0
            packet->socket.inet.src_ipaddr.af == AF_INET6 ? "[" : "",
435
0
      fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->socket.inet.src_ipaddr),
436
0
      packet->socket.inet.src_ipaddr.af == AF_INET6 ? "]" : "",
437
0
            packet->socket.inet.src_port,
438
0
            packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "[" : "",
439
0
      fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->socket.inet.dst_ipaddr),
440
0
            packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "]" : "",
441
0
            packet->socket.inet.dst_port,
442
0
#ifdef WITH_IFINDEX_NAME_RESOLUTION
443
0
      received ? "via " : "",
444
0
      received ? fr_ifname_from_ifindex(if_name, packet->socket.inet.ifindex) : "",
445
0
      received ? " " : "",
446
0
#endif
447
0
      packet->data_len);
448
0
  } else {
449
0
    fr_log(log, L_DBG, __FILE__, __LINE__,
450
0
           "%s code %u Id %i from %s%s%s:%i to %s%s%s:%i "
451
0
#ifdef WITH_IFINDEX_NAME_RESOLUTION
452
0
           "%s%s%s"
453
0
#endif
454
0
           "length %zu\n",
455
0
            received ? "Received" : "Sent",
456
0
            packet->code,
457
0
            packet->id,
458
0
            packet->socket.inet.src_ipaddr.af == AF_INET6 ? "[" : "",
459
0
      fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->socket.inet.src_ipaddr),
460
0
            packet->socket.inet.src_ipaddr.af == AF_INET6 ? "]" : "",
461
0
            packet->socket.inet.src_port,
462
0
            packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "[" : "",
463
0
      fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->socket.inet.dst_ipaddr),
464
0
            packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "]" : "",
465
0
            packet->socket.inet.dst_port,
466
0
#ifdef WITH_IFINDEX_NAME_RESOLUTION
467
0
      received ? "via " : "",
468
0
      received ? fr_ifname_from_ifindex(if_name, packet->socket.inet.ifindex) : "",
469
0
      received ? " " : "",
470
0
#endif
471
0
            packet->data_len);
472
0
  }
473
0
}
474
475
/*
476
 *  Debug the packet header and all attributes.  This function is only called by the client code.
477
 */
478
void fr_radius_packet_log(fr_log_t const *log, fr_packet_t *packet, fr_pair_list_t *list, bool received)
479
0
{
480
0
  fr_radius_packet_header_log(log, packet, received);
481
482
0
  if (!fr_debug_lvl) return;
483
484
  /*
485
   *  If we're auto-adding Message Authenticator, then print
486
   *  out that we're auto-adding it.
487
   */
488
0
  if (!received) switch (packet->code) {
489
0
  case FR_RADIUS_CODE_ACCESS_REQUEST:
490
0
  case FR_RADIUS_CODE_STATUS_SERVER:
491
0
    if (!fr_pair_find_by_da(list, NULL, attr_message_authenticator)) {
492
0
      fr_log(log, L_DBG, __FILE__, __LINE__, "\tMessage-Authenticator = 0x\n");
493
0
    }
494
0
    break;
495
496
0
  default:
497
0
    break;
498
0
  }
499
500
0
  fr_pair_list_log(log, 4, list);
501
0
#ifndef NDEBUG
502
0
  if (fr_debug_lvl >= L_DBG_LVL_4) fr_packet_log_hex(log, packet);
503
0
#endif
504
0
}