Coverage Report

Created: 2026-10-18 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/client.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or (at
5
 *   your option) any later version.
6
 *
7
 *   This program 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
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; 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: 4702a52f7ee404568b81d8f124e7e5b63e1213b9 $
19
 * @file src/lib/server/client.c
20
 * @brief Manage clients allowed to communicate with the server.
21
 *
22
 * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23
 * @copyright 2000,2006 The FreeRADIUS server project
24
 * @copyright 2000 Alan DeKok (aland@freeradius.org)
25
 * @copyright 2000 Miquel van Smoorenburg (miquels@cistron.nl)
26
 */
27
RCSID("$Id: 4702a52f7ee404568b81d8f124e7e5b63e1213b9 $")
28
29
#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
30
#include <freeradius-devel/server/cf_file.h>
31
#include <freeradius-devel/server/cf_parse.h>
32
#include <freeradius-devel/server/client.h>
33
#include <freeradius-devel/server/main_config.h>
34
#include <freeradius-devel/server/module.h>
35
#include <freeradius-devel/server/virtual_servers.h>
36
#include <freeradius-devel/unlang/call.h>
37
38
#include <freeradius-devel/util/base16.h>
39
#include <freeradius-devel/util/misc.h>
40
#include <freeradius-devel/util/trie.h>
41
42
#include <fcntl.h>
43
#include <sys/stat.h>
44
45
//#define WITH_TRIE (1)
46
47
/** Group of clients
48
 *
49
 */
50
struct fr_client_list_s {
51
  char const  *name;      //!< Name of the client list.
52
#ifdef WITH_TRIE
53
  fr_trie_t *v4_udp;
54
  fr_trie_t *v6_udp;
55
  fr_trie_t *v4_tcp;
56
  fr_trie_t *v6_tcp;
57
#else
58
  fr_rb_tree_t  *tree[129];
59
#endif
60
};
61
62
static fr_client_list_t *root_clients = NULL; //!< Global client list.
63
64
#ifndef WITH_TRIE
65
static fr_cmp_ret_t client_cmp(void const *one, void const *two)
66
0
{
67
0
  fr_cmp_ret_t ret;
68
0
  fr_client_t const *a = one;
69
0
  fr_client_t const *b = two;
70
71
0
  ret = fr_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
72
0
  if (ret != 0) return ret;
73
74
  /*
75
   *  0 is "wildcard", or "both" protocols
76
   */
77
0
  if ((a->proto == IPPROTO_IP) || (b->proto == IPPROTO_IP)) return CMP_EQ;
78
79
0
  return CMP(a->proto, b->proto);
80
0
}
81
82
#endif
83
84
void client_list_free(void)
85
0
{
86
0
  TALLOC_FREE(root_clients);
87
0
}
88
89
/** Free a client
90
 *
91
 *  It's up to the caller to ensure that it's deleted from any fr_client_list_t.
92
 */
93
void client_free(fr_client_t *client)
94
0
{
95
0
  if (!client) return;
96
97
0
  talloc_free(client);
98
0
}
99
100
/** Return a new client list
101
 *
102
 * @note The container won't contain any clients.
103
 *
104
 * @return
105
 *  - New client list on success.
106
 *  - NULL on error (OOM).
107
 */
108
fr_client_list_t *client_list_init(CONF_SECTION *cs)
109
0
{
110
0
  fr_client_list_t *clients = talloc_zero(cs, fr_client_list_t);
111
112
0
  if (!clients) return NULL;
113
114
0
  clients->name = talloc_strdup(clients, cs ? cf_section_name1(cs) : "root");
115
116
#ifdef WITH_TRIE
117
  clients->v4_udp = fr_trie_alloc(clients, NULL, NULL);
118
  if (!clients->v4_udp) {
119
    talloc_free(clients);
120
    return NULL;
121
  }
122
123
  clients->v6_udp = fr_trie_alloc(clients, NULL, NULL);
124
  if (!clients->v6_udp) {
125
    talloc_free(clients);
126
    return NULL;
127
  }
128
129
  clients->v4_tcp = fr_trie_alloc(clients, NULL, NULL);
130
  if (!clients->v4_tcp) {
131
    talloc_free(clients);
132
    return NULL;
133
  }
134
135
  clients->v6_tcp = fr_trie_alloc(clients, NULL, NULL);
136
  if (!clients->v6_tcp) {
137
    talloc_free(clients);
138
    return NULL;
139
  }
140
#endif  /* WITH_TRIE */
141
142
0
  return clients;
143
0
}
144
145
#ifdef WITH_TRIE
146
/*
147
 *  @todo - either support client definitions where "proto = *",
148
 *  or update this code to allow for that.  i.e. we create yet
149
 *  another set of v4/v6 tries, for "proto = *" clients.  And then
150
 *  do lookups there, too.  Or, just unify the udp/tcp tries, and
151
 *  instead do post-processing?  Though those two clients can have
152
 *  different secrets... and the trie code doesn't allow 2
153
 *  fr_trie_user_t nodes in a row.  So we would have to instead
154
 *  handle that ourselves, with a wrapper around the fr_client_t
155
 *  structure that does udp/tcp/wildcard demultiplexing
156
 */
157
static fr_trie_t *clients_trie(fr_client_list_t const *clients, fr_ipaddr_t const *ipaddr,
158
             int proto)
159
{
160
  if (ipaddr->af == AF_INET) {
161
    if (proto == IPPROTO_TCP) return clients->v4_tcp;
162
163
    return clients->v4_udp;
164
  }
165
166
  fr_assert(ipaddr->af == AF_INET6);
167
168
  if (proto == IPPROTO_TCP) return clients->v6_tcp;
169
170
  return clients->v6_udp;
171
}
172
#endif  /* WITH_TRIE */
173
174
/** Add a client to a fr_client_list_t
175
 *
176
 * @param clients list to add client to, may be NULL if global client list is being used.
177
 * @param client to add.
178
 * @return
179
 *  - true on success.
180
 *  - false on failure.
181
 */
182
bool client_add(fr_client_list_t *clients, fr_client_t *client)
183
0
{
184
#ifdef WITH_TRIE
185
  fr_trie_t *trie;
186
#else
187
0
#endif
188
0
  fr_client_t *old;
189
0
  char buffer[FR_IPADDR_PREFIX_STRLEN];
190
191
0
  if (!client) return false;
192
193
  /*
194
   *  Hack to fixup wildcard clients
195
   *
196
   *  If the IP is all zeros, with a 32 or 128 bit netmask
197
   *  assume the user meant to configure 0.0.0.0/0 instead
198
   *  of 0.0.0.0/32 - which would require the src IP of
199
   *  the client to be all zeros.
200
   */
201
0
  if (fr_ipaddr_is_inaddr_any(&client->ipaddr) == 1) switch (client->ipaddr.af) {
202
0
  case AF_INET:
203
0
    if (client->ipaddr.prefix == 32) client->ipaddr.prefix = 0;
204
0
    break;
205
206
0
  case AF_INET6:
207
0
    if (client->ipaddr.prefix == 128) client->ipaddr.prefix = 0;
208
0
    break;
209
210
0
  default:
211
0
    fr_assert(0);
212
0
  }
213
214
0
  fr_inet_ntop_prefix(buffer, sizeof(buffer), &client->ipaddr);
215
0
  DEBUG3("Adding client %s (%s) to prefix tree %i", buffer, client->longname, client->ipaddr.prefix);
216
217
  /*
218
   *  If "clients" is NULL, it means add to the global list,
219
   *  unless we're trying to add it to a virtual server...
220
   */
221
0
  if (!clients) {
222
0
    if (client->server != NULL) {
223
0
      CONF_SECTION *cs;
224
0
      CONF_SECTION *subcs;
225
226
0
      if (!client->cs) {
227
0
        ERROR("Failed to find configuration section in client.  Ignoring 'virtual_server' directive");
228
0
        return false;
229
0
      }
230
231
0
      cs = cf_section_find(cf_root(client->cs), "server", client->server);
232
0
      if (!cs) {
233
0
        ERROR("Failed to find virtual server %s", client->server);
234
0
        return false;
235
0
      }
236
237
      /*
238
       *  If this server has no "listen" section, add the clients
239
       *  to the global client list.
240
       */
241
0
      subcs = cf_section_find(cs, "listen", NULL);
242
0
      if (!subcs) goto global_clients;
243
244
      /*
245
       *  If the client list already exists, use that.
246
       *  Otherwise, create a new client list.
247
       */
248
0
      clients = cf_data_value(cf_data_find(cs, fr_client_list_t, NULL));
249
0
      if (!clients) {
250
0
        MEM(clients = client_list_init(cs));
251
0
        if (!cf_data_add(cs, clients, NULL, false)) {
252
0
          ERROR("Failed to associate clients with virtual server %s", client->server);
253
0
          talloc_free(clients);
254
0
          return false;
255
0
        }
256
0
      }
257
258
0
    } else {
259
0
    global_clients:
260
      /*
261
       *  Initialize the global list, if not done already.
262
       */
263
0
      if (!root_clients) {
264
0
        root_clients = client_list_init(NULL);
265
0
        if (!root_clients) return false;
266
0
      }
267
0
      clients = root_clients;
268
0
    }
269
0
  }
270
271
0
#define namecmp(a) ((!old->a && !client->a) || (old->a && client->a && (strcmp(old->a, client->a) == 0)))
272
273
#ifdef WITH_TRIE
274
  trie = clients_trie(clients, &client->ipaddr, client->proto);
275
276
  /*
277
   *  Cannot insert the same client twice.
278
   */
279
  old = fr_trie_match_by_key(trie, &client->ipaddr.addr, client->ipaddr.prefix);
280
281
#else  /* WITH_TRIE */
282
283
0
  if (!clients->tree[client->ipaddr.prefix]) {
284
0
    clients->tree[client->ipaddr.prefix] = fr_rb_inline_talloc_alloc(clients, fr_client_t, node, client_cmp,
285
0
                     NULL);
286
0
    if (!clients->tree[client->ipaddr.prefix]) {
287
0
      return false;
288
0
    }
289
0
  }
290
291
0
  fr_rb_find((void **)&old, clients->tree[client->ipaddr.prefix], client);
292
0
#endif
293
0
  if (old) {
294
    /*
295
     *  If it's a complete duplicate, then free the new
296
     *  one, and return "OK".
297
     */
298
0
    if (namecmp(longname) && namecmp(secret) &&
299
0
        namecmp(shortname) && namecmp(nas_type) &&
300
0
        namecmp(server) &&
301
0
        (old->require_message_authenticator == client->require_message_authenticator)) {
302
0
      WARN("Ignoring duplicate client %s", client->longname);
303
0
      client_free(client);
304
0
      return true;
305
0
    }
306
307
0
    ERROR("Failed to add duplicate client %s", client->shortname);
308
0
    return false;
309
0
  }
310
0
#undef namecmp
311
312
#ifdef WITH_TRIE
313
  /*
314
   *  Other error adding client: likely is fatal.
315
   */
316
  if (fr_trie_insert_by_key(trie, &client->ipaddr.addr, client->ipaddr.prefix, client) < 0) {
317
    return false;
318
  }
319
#else
320
0
  if (fr_rb_insert(clients->tree[client->ipaddr.prefix], client) != 0) {
321
0
    return false;
322
0
  }
323
0
#endif
324
325
  /*
326
   *  @todo - do we want to do this for dynamic clients?
327
   */
328
0
  (void) talloc_steal(clients, client); /* reparent it */
329
330
0
  return true;
331
0
}
332
333
334
void client_delete(fr_client_list_t *clients, fr_client_t *client)
335
0
{
336
#ifdef WITH_TRIE
337
  fr_trie_t *trie;
338
#endif
339
340
0
  if (!client) return;
341
342
0
  if (!clients) clients = root_clients;
343
344
0
  fr_assert(client->ipaddr.prefix <= 128);
345
346
#ifdef WITH_TRIE
347
  trie = clients_trie(clients, &client->ipaddr, client->proto);
348
349
  /*
350
   *  Don't free the client.  The caller is responsible for that.
351
   */
352
  (void) fr_trie_remove_by_key(trie, &client->ipaddr.addr, client->ipaddr.prefix);
353
#else
354
355
0
  if (!clients->tree[client->ipaddr.prefix]) return;
356
357
0
  (void) fr_rb_delete(clients->tree[client->ipaddr.prefix], client);
358
0
#endif
359
0
}
360
361
fr_client_t *client_findbynumber(UNUSED const fr_client_list_t *clients, UNUSED int number)
362
0
{
363
0
  return NULL;
364
0
}
365
366
367
/*
368
 *  Find a client in the fr_client_t list.
369
 */
370
fr_client_t *client_find(fr_client_list_t const *clients, fr_ipaddr_t const *ipaddr, int proto)
371
0
{
372
#ifdef WITH_TRIE
373
  fr_trie_t *trie;
374
#else
375
0
  int i, max;
376
0
  fr_client_t my_client, *client;
377
0
#endif
378
379
0
  if (!clients) clients = root_clients;
380
381
0
  if (!clients || !ipaddr) return NULL;
382
383
#ifdef WITH_TRIE
384
  trie = clients_trie(clients, ipaddr, proto);
385
386
  return fr_trie_lookup_by_key(trie, &ipaddr->addr, ipaddr->prefix);
387
#else
388
389
0
  if (ipaddr->af == AF_INET) {
390
0
    max = 32;
391
0
  } else {
392
0
    max = 128;
393
0
  }
394
395
0
  if (max > ipaddr->prefix) max = ipaddr->prefix;
396
397
0
  my_client.proto = proto;
398
0
  for (i = max; i >= 0; i--) {
399
0
    if (!clients->tree[i]) continue;
400
401
0
    my_client.ipaddr = *ipaddr;
402
0
    fr_ipaddr_mask(&my_client.ipaddr, i);
403
0
    fr_rb_find((void **)&client, clients->tree[i], &my_client);
404
0
    if (client) {
405
0
      return client;
406
0
    }
407
0
  }
408
409
0
  return NULL;
410
0
#endif
411
0
}
412
413
static fr_ipaddr_t cl_ipaddr;
414
static char const *cl_srcipaddr = NULL;
415
static char const *hs_proto = NULL;
416
417
static conf_parser_t limit_config[] = {
418
  { FR_CONF_OFFSET("max_connections", fr_client_t, limit.max_connections), .dflt = "16" },
419
420
  { FR_CONF_OFFSET("lifetime", fr_client_t, limit.lifetime), .dflt = "0" },
421
422
  { FR_CONF_OFFSET("idle_timeout", fr_client_t, limit.idle_timeout), .dflt = "30s" },
423
  CONF_PARSER_TERMINATOR
424
};
425
426
static const conf_parser_t client_config[] = {
427
  { FR_CONF_POINTER("ipaddr", FR_TYPE_COMBO_IP_PREFIX, 0, &cl_ipaddr) },
428
  { FR_CONF_POINTER("ipv4addr", FR_TYPE_IPV4_PREFIX, 0, &cl_ipaddr) },
429
  { FR_CONF_POINTER("ipv6addr", FR_TYPE_IPV6_PREFIX, 0, &cl_ipaddr) },
430
431
  { FR_CONF_POINTER("src_ipaddr", FR_TYPE_STRING, 0, &cl_srcipaddr) },
432
433
  { FR_CONF_OFFSET_FLAGS("secret", CONF_FLAG_SECRET, fr_client_t, secret) },
434
  { FR_CONF_OFFSET("shortname", fr_client_t, shortname) },
435
436
  { FR_CONF_OFFSET("nas_type", fr_client_t, nas_type) },
437
438
  { FR_CONF_OFFSET_IS_SET("require_message_authenticator", FR_TYPE_UINT8, 0, fr_client_t, require_message_authenticator),
439
    .func = cf_table_parse_uint8,
440
    .uctx = &(cf_table_parse_ctx_t){ .table = fr_radius_require_ma_table, .len = &fr_radius_require_ma_table_len },
441
    .dflt = "no" },
442
443
  { FR_CONF_OFFSET_IS_SET("limit_proxy_state", FR_TYPE_UINT8, 0, fr_client_t, limit_proxy_state),
444
    .func = cf_table_parse_uint8,
445
    .uctx = &(cf_table_parse_ctx_t){ .table = fr_radius_limit_proxy_state_table, .len = &fr_radius_limit_proxy_state_table_len },
446
    .dflt = "auto" },
447
448
  { FR_CONF_OFFSET("protocol_error", fr_client_t, protocol_error) },
449
450
  { FR_CONF_OFFSET("response_window", fr_client_t, response_window) },
451
452
#ifdef NAS_VIOLATES_RFC
453
  /*
454
   *  For vendors who violate the RFCs and go out of their way to make their systems vulnerable.
455
   */
456
  { FR_CONF_OFFSET("nas_violates_message_authenticator_rfc", fr_client_t, allow_vulnerable_clients) },
457
#endif
458
459
  { FR_CONF_POINTER("proto", FR_TYPE_STRING, 0, &hs_proto) },
460
  { FR_CONF_POINTER("limit", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) limit_config },
461
462
  CONF_PARSER_TERMINATOR
463
};
464
465
/** Create a list of clients from a client section
466
 *
467
 * Iterates over all client definitions in the specified section, adding them to a client list.
468
 */
469
#ifdef WITH_TLS
470
#define TLS_UNUSED
471
#else
472
#define TLS_UNUSED UNUSED
473
#endif
474
475
fr_client_list_t *client_list_parse_section(CONF_SECTION *section, int proto, TLS_UNUSED bool tls_required)
476
0
{
477
0
  bool    global = false;
478
0
  CONF_SECTION  *cs = NULL;
479
0
  fr_client_t *c = NULL;
480
0
  fr_client_list_t  *clients = NULL;
481
0
  CONF_SECTION  *server_cs = NULL;
482
0
  char const  *cache_name;
483
484
  /*
485
   *  Key the cached list by protocol.  The section walk below
486
   *  filters out client definitions whose proto doesn't match
487
   *  the caller's, so caching the filtered list under a single
488
   *  key would let whichever listener instantiated first poison
489
   *  the cache for the other.  In particular the UDP listener
490
   *  would reuse a TCP-filtered list (with UDP-default clients
491
   *  dropped) and never see its own clients.
492
   */
493
0
  switch (proto) {
494
0
  case IPPROTO_UDP:
495
0
    cache_name = "udp";
496
0
    break;
497
498
0
  case IPPROTO_TCP:
499
0
    cache_name = "tcp";
500
0
    break;
501
502
0
  default:
503
    /*
504
     *  proto == 0 is the unfiltered / global path
505
     *  (main_config.c passes 0 for root clients).
506
     */
507
0
    cache_name = NULL;
508
0
    break;
509
0
  }
510
511
  /*
512
   *  Be forgiving.  If there's already a clients, return
513
   *  it.  Otherwise create a new one.
514
   */
515
0
  clients = cf_data_value(cf_data_find(section, fr_client_list_t, cache_name));
516
0
  if (clients) return clients;
517
518
  /*
519
   *  Parent the client list from the section.
520
   */
521
0
  clients = client_list_init(section);
522
0
  if (!clients) return NULL;
523
524
  /*
525
   *  If the section is hung off the config root, this is
526
   *  the global client list, else it's virtual server
527
   *  specific client list.
528
   */
529
0
  if (cf_root(section) == section) global = true;
530
531
0
  if (strcmp("server", cf_section_name1(section)) == 0) server_cs = section;
532
533
  /*
534
   *  Iterate over all the clients in the section, adding
535
   *  them to the client list.
536
   */
537
0
  while ((cs = cf_section_find_next(section, cs, "client", CF_IDENT_ANY))) {
538
    /*
539
     *  Check this before parsing the client.
540
     */
541
0
    if (proto) {
542
0
      CONF_PAIR *cp;
543
0
      int client_proto = IPPROTO_UDP;
544
545
0
      cp = cf_pair_find(cs, "proto");
546
0
      if (cp) {
547
0
        char const *value = cf_pair_value(cp);
548
549
0
        if (!value) {
550
0
          cf_log_err(cs, "'proto' field must have a value");
551
0
          talloc_free(clients);
552
0
          return NULL;
553
0
        }
554
555
0
        if (strcmp(value, "udp") == 0) {
556
          /* do nothing */
557
558
0
        } else if (strcmp(value, "tcp") == 0) {
559
0
          client_proto = IPPROTO_TCP;
560
0
#ifdef WITH_TLS
561
0
        } else if (strcmp(value, "tls") == 0) {
562
0
          client_proto = IPPROTO_TCP;
563
0
#endif
564
0
        } else if (strcmp(value, "*") == 0) {
565
0
          client_proto = IPPROTO_IP; /* fake for dual */
566
0
        } else {
567
0
          cf_log_err(cs, "Unknown proto \"%s\".", value);
568
0
          talloc_free(clients);
569
0
          return NULL;
570
0
        }
571
0
      }
572
573
      /*
574
       *  We don't have "proto = *", so the
575
       *  protocol MUST match what the caller
576
       *  asked for.  Otherwise, we ignore the
577
       *  client.
578
       */
579
0
      if ((client_proto != IPPROTO_IP) && (proto != client_proto)) continue;
580
0
    }
581
582
583
0
    c = client_afrom_cs(cs, cs, server_cs, 0);
584
0
    if (!c) {
585
0
    error:
586
0
      client_free(c);
587
0
      talloc_free(clients);
588
0
      return NULL;
589
0
    }
590
591
    /*
592
     *  TCP sockets are always connected.
593
     */
594
0
    c->use_connected |= (c->proto == IPPROTO_TCP);
595
596
0
#ifdef WITH_TLS
597
    /*
598
     *  TLS clients CANNOT use non-TLS listeners.
599
     *  non-TLS clients CANNOT use TLS listeners.
600
     */
601
0
    if (tls_required != c->tls_required) {
602
0
      cf_log_err(cs, "Client does not have the same TLS configuration as the listener");
603
0
      goto error;
604
0
    }
605
0
#endif
606
607
0
    if (!client_add(clients, c)) {
608
0
      cf_log_err(cs, "Failed to add client %s", cf_section_name2(cs));
609
0
      goto error;
610
0
    }
611
612
0
    cf_item_mark_parsed(cs);
613
0
  }
614
615
  /*
616
   *  Associate the clients structure with the section,
617
   *  keyed by protocol so a per-proto listener's filtered
618
   *  list doesn't shadow another proto's.
619
   */
620
0
  if (!cf_data_add(section, clients, cache_name, false)) {
621
0
    cf_log_err(section, "Failed to associate clients with section %s", cf_section_name1(section));
622
0
    talloc_free(clients);
623
0
    return NULL;
624
0
  }
625
626
  /*
627
   *  Replace the global list of clients with the new one.
628
   *  The old one is still referenced from the original
629
   *  configuration, and will be freed when that is freed.
630
   */
631
0
  if (global) root_clients = clients;
632
633
0
  return clients;
634
0
}
635
636
/** Create a client CONF_SECTION using a mapping section to map values from a result set to client attributes
637
 *
638
 * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too.
639
 *
640
 * @note Caller should free CONF_SECTION passed in as out, on error.
641
 *   Contents of that section will be in an undefined state.
642
 *
643
 * @param[in,out] out Section to perform mapping on. Either the root of the client config, or a parent section
644
 *  (when this function is called recursively).
645
 *  Should be alloced with cf_section_alloc, or if there's a separate template section, the
646
 *  result of calling cf_section_dup on that section.
647
 * @param[in] map section.
648
 * @param[in] func to call to retrieve CONF_PAIR values. Must return a talloced buffer containing the value.
649
 * @param[in] data to pass to func, usually a result pointer.
650
 * @return
651
 *  - 0 on success.
652
 *  - -1 on failure.
653
 */
654
int client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data)
655
0
{
656
0
  CONF_ITEM const *ci;
657
658
0
  for (ci = cf_item_next(map, NULL);
659
0
       ci != NULL;
660
0
       ci = cf_item_next(map, ci)) {
661
0
        CONF_PAIR const *cp;
662
0
        CONF_PAIR *old;
663
0
        char *value;
664
0
    char const *attr;
665
666
    /*
667
     *  Recursively process map subsection
668
     */
669
0
    if (cf_item_is_section(ci)) {
670
0
      CONF_SECTION *cs, *cc;
671
672
0
      cs = cf_item_to_section(ci);
673
      /*
674
       *  Use pre-existing section or alloc a new one
675
       */
676
0
      cc = cf_section_find(out, cf_section_name1(cs), cf_section_name2(cs));
677
0
      if (!cc) {
678
0
        cc = cf_section_alloc(out, out, cf_section_name1(cs), cf_section_name2(cs));
679
0
        if (!cc) return -1;
680
0
      }
681
682
0
      if (client_map_section(cc, cs, func, data) < 0) return -1;
683
0
      continue;
684
0
    }
685
686
0
    cp = cf_item_to_pair(ci);
687
0
    attr = cf_pair_attr(cp);
688
689
    /*
690
     *  The callback can return 0 (success) and not provide a value
691
     *  in which case we skip the mapping pair.
692
     *
693
     *  Or return -1 in which case we error out.
694
     */
695
0
    if (func(&value, cp, data) < 0) {
696
0
      cf_log_err(out, "Failed performing mapping \"%s\" = \"%s\"", attr, cf_pair_value(cp));
697
0
      return -1;
698
0
    }
699
0
    if (!value) continue;
700
701
    /*
702
     *  Replace an existing CONF_PAIR
703
     */
704
0
    old = cf_pair_find(out, attr);
705
0
    if (old) {
706
0
      cf_pair_replace(out, old, value);
707
0
      talloc_free(value);
708
0
      continue;
709
0
    }
710
711
    /*
712
     *  ...or add a new CONF_PAIR
713
     */
714
0
    cp = cf_pair_alloc(out, attr, value, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
715
0
    if (!cp) {
716
0
      cf_log_err(out, "Failed allocing pair \"%s\" = \"%s\"", attr, value);
717
0
      talloc_free(value);
718
0
      return -1;
719
0
    }
720
0
    talloc_free(value);
721
0
  }
722
723
0
  return 0;
724
0
}
725
726
/** Allocate a new client from a config section
727
 *
728
 * @param ctx to allocate new clients in.
729
 * @param cs to process as a client.
730
 * @param server_cs The virtual server that this client belongs to.
731
 * @param extra 0                 allocate sizeof(fr_client_t) bytes
732
 *              >sizeof(client_t) number of bytes to allocate
733
 * @return new fr_client_t struct.
734
 */
735
fr_client_t *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, CONF_SECTION *server_cs, size_t extra)
736
0
{
737
0
  fr_client_t *c;
738
0
  char const  *name2;
739
0
  CONF_PAIR *cp;
740
741
0
  name2 = cf_section_name2(cs);
742
0
  if (!name2) {
743
0
    cf_log_err(cs, "Missing client name");
744
0
    return NULL;
745
0
  }
746
747
  /*
748
   *  The size is fine.. Let's create the buffer
749
   */
750
0
  if (!extra) {
751
0
    c = talloc_zero(ctx, fr_client_t);
752
0
    if (!c) return NULL;
753
0
  } else {
754
0
    fr_assert(extra > sizeof(fr_client_t));
755
756
0
    c = (fr_client_t *) talloc_zero_array(ctx, uint8_t, extra);
757
0
    if (!c) return NULL;
758
759
0
    talloc_set_name_const(c, "fr_client_t");
760
0
  }
761
762
0
  c->cs = cs;
763
764
0
  memset(&cl_ipaddr, 0, sizeof(cl_ipaddr));
765
0
  if (cf_section_rules_push(cs, client_config) < 0) goto error;
766
767
0
  if (cf_section_parse(c, c, cs) < 0) {
768
0
    cf_log_err(cs, "Error parsing client section");
769
0
  error:
770
0
    client_free(c);
771
0
    hs_proto = NULL;
772
0
    cl_srcipaddr = NULL;
773
0
    return NULL;
774
0
  }
775
776
  /*
777
   *  Allow for binary secrets.
778
   */
779
0
  cp = cf_pair_find(cs, "secret");
780
0
  if (cp && (cf_pair_operator(cp) == T_BARE_WORD)) {
781
0
    char const *value;
782
783
0
    value = cf_pair_value(cp);
784
0
    if ((value[0] == '0') && (value[1] == 'x')) {
785
0
      size_t bin_len, hex_len, converted;
786
0
      uint8_t *bin;
787
788
      /*
789
       *  '0x...' plus trailing NUL.
790
       */
791
0
      hex_len = talloc_array_length(value) - 3;
792
0
      bin_len = (hex_len / 2) + 1;
793
0
      MEM(bin = talloc_array(c, uint8_t, bin_len));
794
0
      converted = fr_base16_decode(NULL,
795
0
                 &FR_DBUFF_TMP(bin, bin_len),
796
0
                 &FR_SBUFF_IN(value + 2, hex_len), false);
797
0
      if (converted < (bin_len - 1)) {
798
0
        cf_log_err(cs, "Invalid hex string in shared secret");
799
0
        goto error;
800
0
      }
801
802
0
      talloc_const_free(c->secret);
803
0
      c->secret = (char const *) bin;
804
0
    }
805
0
  }
806
807
  /*
808
   *  Find the virtual server for this client.
809
   */
810
0
  if (c->server) {
811
0
    virtual_server_t const *vs;
812
0
    if (server_cs) {
813
0
      cf_log_err(cs, "Clients inside of a 'server' section cannot point to a server");
814
0
      goto error;
815
0
    }
816
817
0
    vs = virtual_server_find(c->server);
818
0
    if (!vs) goto error;
819
820
0
    c->server_cs = virtual_server_cs(vs);
821
0
    if (!c->server_cs) {
822
0
      cf_log_err(cs, "Failed to find virtual server %s", c->server);
823
0
      goto error;
824
0
    }
825
826
0
  } else if (server_cs) {
827
0
    c->server = cf_section_name2(server_cs);
828
0
    c->server_cs = server_cs;
829
830
0
  } /* else don't set c->server or c->server_cs, we will use listener->server */
831
832
  /*
833
   *  Newer style client definitions with either ipaddr or ipaddr6
834
   *  config items.
835
   */
836
0
  if (cf_pair_find(cs, "ipaddr") || cf_pair_find(cs, "ipv4addr") || cf_pair_find(cs, "ipv6addr")) {
837
0
    char buffer[128];
838
839
    /*
840
     *  Sets ipv4/ipv6 address and prefix.
841
     */
842
0
    c->ipaddr = cl_ipaddr;
843
844
    /*
845
     *  Set the long name to be the result of a reverse lookup on the IP address.
846
     */
847
0
    fr_inet_ntoh(&c->ipaddr, buffer, sizeof(buffer));
848
0
    c->longname = talloc_strdup(c, buffer);
849
850
    /*
851
     *  Set the short name to the name2.
852
     */
853
0
    if (!c->shortname) c->shortname = talloc_strdup(c, name2);
854
  /*
855
   *  No "ipaddr" or "ipv6addr", use old-style "client <ipaddr> {" syntax.
856
   */
857
0
  } else {
858
0
    cf_log_err(cs, "No 'ipaddr' or 'ipv4addr' or 'ipv6addr' configuration "
859
0
            "directive found in client %s", name2);
860
0
    goto error;
861
0
  }
862
863
0
  c->proto = IPPROTO_UDP;
864
0
  if (hs_proto) {
865
0
    if (strcmp(hs_proto, "udp") == 0) {
866
0
      hs_proto = NULL;
867
868
0
    } else if (strcmp(hs_proto, "tcp") == 0) {
869
0
      hs_proto = NULL;
870
0
      c->proto = IPPROTO_TCP;
871
0
#ifdef WITH_TLS
872
0
    } else if (strcmp(hs_proto, "tls") == 0) {
873
0
      hs_proto = NULL;
874
0
      c->proto = IPPROTO_TCP;
875
0
      c->tls_required = true;
876
877
0
#endif
878
0
    } else if (strcmp(hs_proto, "*") == 0) {
879
0
      hs_proto = NULL;
880
0
      c->proto = IPPROTO_IP; /* fake for dual */
881
0
    } else {
882
0
      cf_log_err(cs, "Unknown proto \"%s\".", hs_proto);
883
0
      goto error;
884
0
    }
885
0
  }
886
887
  /*
888
   *  If a src_ipaddr is specified, when we send the return packet
889
   *  we will use this address instead of the src from the
890
   *  request.
891
   */
892
0
  if (cl_srcipaddr) {
893
0
    switch (c->ipaddr.af) {
894
0
    case AF_INET:
895
0
      if (fr_inet_pton4(&c->src_ipaddr, cl_srcipaddr, -1, true, false, true) < 0) {
896
0
        cf_log_perr(cs, "Failed parsing src_ipaddr");
897
0
        goto error;
898
0
      }
899
0
      break;
900
901
0
    case AF_INET6:
902
0
      if (fr_inet_pton6(&c->src_ipaddr, cl_srcipaddr, -1, true, false, true) < 0) {
903
0
        cf_log_perr(cs, "Failed parsing src_ipaddr");
904
0
        goto error;
905
0
      }
906
0
      break;
907
0
    default:
908
0
      cf_log_err(cs, "ipaddr was not defined");
909
0
      goto error;
910
0
    }
911
0
    cl_srcipaddr = NULL;
912
0
  } else {
913
0
    c->src_ipaddr.af = c->ipaddr.af; /* leave the rest as IFADDR_ANY */
914
0
  }
915
916
  /*
917
   *  A response_window of zero is OK, and means that it's
918
   *  ignored by the rest of the server timers.
919
   */
920
0
  if (fr_time_delta_ispos(c->response_window)) {
921
0
    FR_TIME_DELTA_BOUND_CHECK("response_window", c->response_window, >=, fr_time_delta_from_usec(1000));
922
0
    FR_TIME_DELTA_BOUND_CHECK("response_window", c->response_window, <=, fr_time_delta_from_sec(60));
923
0
    FR_TIME_DELTA_BOUND_CHECK("response_window", c->response_window, <=, main_config->worker.max_request_time);
924
0
  }
925
926
0
#ifdef WITH_TLS
927
  /*
928
   *  If the client is TLS only, the secret can be
929
   *  omitted.  When omitted, it's hard-coded to
930
   *  "radsec".  See RFC 6614.
931
   */
932
0
  if (c->tls_required) {
933
0
    if (c->secret) {
934
0
      if (strcmp(c->secret, "radsec") != 0) {
935
0
        cf_log_warn(cs, "'secret' is not 'radsec' for TLS");
936
0
        cf_log_warn(cs, "Packets may not be processed correctly!");
937
0
      }
938
0
    } else {
939
0
      c->secret = talloc_strdup(cs, "radsec");
940
0
    }
941
0
  }
942
0
#endif
943
944
0
  if ((c->proto == IPPROTO_TCP) || (c->proto == IPPROTO_IP)) {
945
0
    if (fr_time_delta_ispos(c->limit.idle_timeout) && fr_time_delta_lt(c->limit.idle_timeout, fr_time_delta_from_sec(5)))
946
0
      c->limit.idle_timeout = fr_time_delta_from_sec(5);
947
0
    if (fr_time_delta_ispos(c->limit.lifetime) && (fr_time_delta_lt(c->limit.lifetime, fr_time_delta_from_sec(5))))
948
0
      c->limit.lifetime = fr_time_delta_from_sec(5);
949
0
    if (fr_time_delta_ispos(c->limit.lifetime) && (fr_time_delta_lt(c->limit.idle_timeout, c->limit.lifetime)))
950
0
      c->limit.idle_timeout = fr_time_delta_wrap(0);
951
0
  }
952
953
0
  cf_item_mark_parsed(cs);
954
0
  return c;
955
0
}
956
957
/** Create a new client, consuming all attributes in the control list of the request
958
 *
959
 * @param ctx the talloc context
960
 * @param request containing the client attributes.
961
 * @return
962
 *  - New client on success.
963
 *  - NULL on error.
964
 */
965
fr_client_t *client_afrom_request(TALLOC_CTX *ctx, request_t *request)
966
0
{
967
0
  static int  cnt;
968
0
  CONF_SECTION  *cs;
969
0
  fr_client_t *c;
970
0
  fr_sbuff_t  *tmp;
971
972
0
  if (!request) return NULL;
973
974
0
  FR_SBUFF_TALLOC_THREAD_LOCAL(&tmp, 128, SIZE_MAX);
975
976
0
  if (unlikely(fr_sbuff_in_sprintf(tmp, "dynamic_%i_", cnt++) <= 0)) {
977
0
  name_error:
978
0
    RERROR("Failed to generate dynamic client name");
979
0
    return NULL;
980
0
  }
981
0
  if (unlikely(fr_value_box_print(tmp, fr_box_ipaddr(request->packet->socket.inet.src_ipaddr), NULL) <= 0)) goto name_error;
982
0
  fr_sbuff_set_to_start(tmp);
983
984
0
  cs = cf_section_alloc(ctx, NULL, "client", fr_sbuff_current(tmp));
985
986
0
  RDEBUG2("Converting control.FreeRADIUS-Client-* to client {...} section");
987
0
  RINDENT();
988
989
0
  fr_pair_list_foreach(&request->control_pairs, vp) {
990
0
    CONF_PAIR *cp = NULL;
991
0
    char const  *value;
992
0
    char const  *attr;
993
0
    fr_token_t  v_token = T_BARE_WORD;
994
995
0
    if (!fr_dict_attr_is_top_level(vp->da)) continue;
996
997
0
    switch (vp->da->attr) {
998
0
    case FR_FREERADIUS_CLIENT_IP_ADDRESS:
999
0
      attr = "ipv4addr";
1000
0
    vb_to_str:
1001
0
      fr_sbuff_set_to_start(tmp);
1002
0
      if (unlikely(fr_pair_print_value_quoted(tmp, vp, T_BARE_WORD) < 0)) {
1003
0
        RERROR("Failed to convert %pP to string", vp);
1004
0
      error:
1005
0
        talloc_free(cs);
1006
0
        return NULL;
1007
0
      }
1008
0
      value = fr_sbuff_start(tmp);
1009
0
      break;
1010
1011
0
    case FR_FREERADIUS_CLIENT_IP_PREFIX:
1012
0
      attr = "ipv4addr";
1013
0
      goto vb_to_str;
1014
1015
0
    case FR_FREERADIUS_CLIENT_IPV6_ADDRESS:
1016
0
      attr = "ipv6addr";
1017
0
      goto vb_to_str;
1018
1019
0
    case FR_FREERADIUS_CLIENT_IPV6_PREFIX:
1020
0
      attr = "ipv6addr";
1021
0
      goto vb_to_str;
1022
1023
0
    case FR_FREERADIUS_CLIENT_SECRET:
1024
0
      attr = "secret";
1025
0
      value = vp->vp_strvalue;
1026
0
      break;
1027
1028
0
    case FR_FREERADIUS_CLIENT_NAS_TYPE:
1029
0
      attr = "nas_type";
1030
0
      value = vp->vp_strvalue;
1031
0
      break;
1032
1033
0
    case FR_FREERADIUS_CLIENT_SHORTNAME:
1034
0
      attr = "shortname";
1035
0
      value = vp->vp_strvalue;
1036
0
      break;
1037
1038
0
    case FR_FREERADIUS_CLIENT_SRC_IP_ADDRESS:
1039
0
      attr = "src_ipaddr";
1040
0
      goto vb_to_str;
1041
1042
0
    case FR_FREERADIUS_CLIENT_REQUIRE_MA:
1043
0
      attr = "require_message_authenticator";
1044
0
      goto vb_to_str;
1045
1046
0
    case FR_FREERADIUS_CLIENT_LIMIT_PROXY_STATE:
1047
0
      attr = "limit_proxy_state";
1048
0
      goto vb_to_str;
1049
1050
0
    case FR_FREERADIUS_CLIENT_TRACK_CONNECTIONS:
1051
0
      attr = "track_connections";
1052
0
      goto vb_to_str;
1053
1054
0
    default:
1055
0
      attr = vp->da->name;
1056
0
      fr_sbuff_set_to_start(tmp);
1057
0
      if (unlikely(fr_value_box_print(tmp, &vp->data, &fr_value_escape_single) < 0)) {
1058
0
        RERROR("Failed printing value for %s", attr);
1059
0
        goto error;
1060
0
      }
1061
0
      value = fr_sbuff_start(tmp);
1062
0
      v_token = T_SINGLE_QUOTED_STRING;
1063
0
      break;
1064
0
    }
1065
1066
0
    cp = cf_pair_alloc(cs, attr, value, T_OP_SET, T_BARE_WORD, v_token);
1067
0
    if (!cp) {
1068
0
      RERROR("Error creating equivalent conf pair for %s", vp->da->name);
1069
0
      goto error;
1070
0
    }
1071
1072
0
    RDEBUG2("%s = %s", cf_pair_attr(cp), cf_pair_value(cp));
1073
0
  }
1074
1075
0
  REXDENT();
1076
1077
  /*
1078
   *  @todo - allow for setting a DIFFERENT virtual server,
1079
   *  src IP, protocol, etc.  This should all be in TLVs..
1080
   */
1081
0
  c = client_afrom_cs(cs, cs, unlang_call_current(request), 0);
1082
0
  if (!c) goto error;
1083
1084
0
  return c;
1085
0
}
1086
1087
/** Read a single client from a file
1088
 *
1089
 * This function supports asynchronous runtime loading of clients.
1090
 *
1091
 * @param[in] filename    To read clients from.
1092
 * @param[in] server_cs   of virtual server clients should be added to.
1093
 * @param[in] check_dns   Check reverse lookup of IP address matches filename.
1094
 * @return
1095
 *  - The new client on success.
1096
 *  - NULL on failure.
1097
 */
1098
fr_client_t *client_read(char const *filename, CONF_SECTION *server_cs, bool check_dns)
1099
0
{
1100
0
  char const  *p;
1101
0
  fr_client_t *c;
1102
0
  CONF_SECTION  *root_cs, *cs;
1103
0
  char buffer[256];
1104
1105
0
  if (!filename) return NULL;
1106
1107
0
  root_cs = cf_section_alloc(NULL, NULL, "main", NULL);
1108
0
  if (!root_cs) return NULL;
1109
1110
0
  if ((cf_file_read(root_cs, filename, false) < 0) || (cf_section_pass2(root_cs) < 0)) {
1111
0
  error:
1112
0
    talloc_free(root_cs);
1113
0
    return NULL;
1114
0
  }
1115
1116
0
  cs = cf_section_find(root_cs, "client", CF_IDENT_ANY);
1117
0
  if (!cs) {
1118
0
    ERROR("No \"client\" section found in client file");
1119
0
    goto error;
1120
0
  }
1121
1122
0
  c = client_afrom_cs(cs, cs, server_cs, 0);
1123
0
  if (!c) goto error;
1124
1125
  /*
1126
   *  Detach c from cs (breaking the cs -> c parent link), then make root_cs a child of c.  This
1127
   *  avoids a talloc cycle (c -> root_cs -> cs -> c), and ensures that freeing c also frees root_cs
1128
   *  and cs.
1129
   */
1130
0
  (void) talloc_steal(NULL, c);
1131
0
  (void) talloc_steal(c, root_cs);
1132
1133
0
  p = strrchr(filename, FR_DIR_SEP);
1134
0
  if (p) {
1135
0
    p++;
1136
0
  } else {
1137
0
    p = filename;
1138
0
  }
1139
1140
0
  if (!check_dns) return c;
1141
1142
  /*
1143
   *  Additional validations
1144
   */
1145
0
  fr_inet_ntoh(&c->ipaddr, buffer, sizeof(buffer));
1146
0
  if (strcmp(p, buffer) != 0) {
1147
0
    ERROR("Invalid client definition in %s: IP address %s does not match name %s", filename, buffer, p);
1148
0
    talloc_free(c); /* also frees root_cs and cs */
1149
0
    return NULL;
1150
0
  }
1151
1152
0
  return c;
1153
0
}
1154
1155
/** Search up a list of requests trying to locate one which has a client
1156
 *
1157
 */
1158
fr_client_t *client_from_request(request_t *request)
1159
0
{
1160
0
  fr_client_t *client;
1161
0
  request_t *parent = request;
1162
1163
0
  do {
1164
0
    client = parent->client;
1165
0
  } while (!client && (parent = parent->parent));
1166
1167
0
  return client;
1168
0
}