2 * This program is 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.
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.
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
20 * @brief Manage clients allowed to communicate with the server.
22 * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23 * @copyright 2000,2006 The FreeRADIUS server project
24 * @copyright 2000 Alan DeKok <aland@ox.org>
25 * @copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/rad_assert.h>
37 #ifdef WITH_DYNAMIC_CLIENTS
43 struct radclient_list {
45 * FIXME: One set of trees for IPv4, and another for IPv6?
47 rbtree_t *trees[129]; /* for 0..128, inclusive. */
53 static rbtree_t *tree_num = NULL; /* client numbers 0..N */
54 static int tree_num_max = 0;
56 static RADCLIENT_LIST *root_clients = NULL;
58 #ifdef WITH_DYNAMIC_CLIENTS
59 static fr_fifo_t *deleted_clients = NULL;
63 * Callback for freeing a client.
65 void client_free(RADCLIENT *client)
69 #ifdef WITH_DYNAMIC_CLIENTS
70 if (client->dynamic == 2) {
73 if (!deleted_clients) {
74 deleted_clients = fr_fifo_create(NULL, 1024, (void (*)(void *))client_free);
75 if (!deleted_clients) return; /* MEMLEAK */
79 * Mark it as in the fifo, and remember when we
83 client->created = now = time(NULL); /* re-set it */
84 fr_fifo_push(deleted_clients, client);
87 * Peek at the head of the fifo. If it might
88 * still be in use, return. Otherwise, pop it
89 * from the queue and delete it.
91 client = fr_fifo_peek(deleted_clients);
92 if ((client->created + 120) >= now) return;
94 client = fr_fifo_pop(deleted_clients);
95 rad_assert(client != NULL);
103 * Callback for comparing two clients.
105 static int client_ipaddr_cmp(void const *one, void const *two)
107 RADCLIENT const *a = one;
108 RADCLIENT const *b = two;
111 return fr_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
115 rcode = fr_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
116 if (rcode != 0) return rcode;
121 if ((a->proto == IPPROTO_IP) ||
122 (b->proto == IPPROTO_IP)) return 0;
124 return (a->proto - b->proto);
129 static int client_num_cmp(void const *one, void const *two)
131 RADCLIENT const *a = one;
132 RADCLIENT const *b = two;
134 return (a->number - b->number);
139 * Free a RADCLIENT list.
141 void client_list_free(RADCLIENT_LIST *clients)
145 if (!clients) clients = root_clients;
146 if (!clients) return; /* Clients may not have been initialised yet */
148 for (i = 0; i <= 128; i++) {
149 if (clients->trees[i]) rbtree_free(clients->trees[i]);
150 clients->trees[i] = NULL;
153 if (clients == root_clients) {
155 if (tree_num) rbtree_free(tree_num);
162 #ifdef WITH_DYNAMIC_CLIENTS
164 * FIXME: No fr_fifo_delete()
168 talloc_free(clients);
172 * Return a new, initialized, set of clients.
174 RADCLIENT_LIST *client_list_init(CONF_SECTION *cs)
176 RADCLIENT_LIST *clients = talloc_zero(cs, RADCLIENT_LIST);
178 if (!clients) return NULL;
180 clients->min_prefix = 128;
185 /** Add a client to a RADCLIENT_LIST
187 * @param clients list to add client to, may be NULL if global client list is being used.
188 * @param client to add.
189 * @return true on success, false on failure.
191 bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
194 char buffer[INET6_ADDRSTRLEN + 3];
196 if (!client) return false;
199 * Hack to fixup wildcard clients
201 * If the IP is all zeros, with a 32 or 128 bit netmask
202 * assume the user meant to configure 0.0.0.0/0 instead
203 * of 0.0.0.0/32 - which would require the src IP of
204 * the client to be all zeros.
206 if (fr_inaddr_any(&client->ipaddr) == 1) switch (client->ipaddr.af) {
208 if (client->ipaddr.prefix == 32) client->ipaddr.prefix = 0;
212 if (client->ipaddr.prefix == 128) client->ipaddr.prefix = 0;
219 fr_ntop(buffer, sizeof(buffer), &client->ipaddr);
220 DEBUG3("Adding client %s (%s) to prefix tree %i", buffer, client->longname, client->ipaddr.prefix);
223 * If the client also defines a server, do that now.
225 if (client->defines_coa_server) if (!realm_home_server_add(client->coa_server)) return false;
228 * If "clients" is NULL, it means add to the global list,
229 * unless we're trying to add it to a virtual server...
232 if (client->server != NULL) {
236 cs = cf_section_sub_find_name2(main_config.config, "server", client->server);
238 ERROR("Failed to find virtual server %s", client->server);
243 * If this server has no "listen" section, add the clients
244 * to the global client list.
246 subcs = cf_section_sub_find(cs, "listen");
247 if (!subcs) goto global_clients;
250 * If the client list already exists, use that.
251 * Otherwise, create a new client list.
253 clients = cf_data_find(cs, "clients");
255 clients = client_list_init(cs);
257 ERROR("Out of memory");
261 if (cf_data_add(cs, "clients", clients, (void (*)(void *)) client_list_free) < 0) {
262 ERROR("Failed to associate clients with virtual server %s", client->server);
263 client_list_free(clients);
271 * Initialize the global list, if not done already.
274 root_clients = client_list_init(NULL);
275 if (!root_clients) return false;
277 clients = root_clients;
282 * Create a tree for it.
284 if (!clients->trees[client->ipaddr.prefix]) {
285 clients->trees[client->ipaddr.prefix] = rbtree_create(clients, client_ipaddr_cmp, NULL, 0);
286 if (!clients->trees[client->ipaddr.prefix]) {
291 #define namecmp(a) ((!old->a && !client->a) || (old->a && client->a && (strcmp(old->a, client->a) == 0)))
294 * Cannot insert the same client twice.
296 old = rbtree_finddata(clients->trees[client->ipaddr.prefix], client);
299 * If it's a complete duplicate, then free the new
300 * one, and return "OK".
302 if ((fr_ipaddr_cmp(&old->ipaddr, &client->ipaddr) == 0) &&
303 (old->ipaddr.prefix == client->ipaddr.prefix) &&
304 namecmp(longname) && namecmp(secret) &&
305 namecmp(shortname) && namecmp(nas_type) &&
306 namecmp(login) && namecmp(password) && namecmp(server) &&
307 #ifdef WITH_DYNAMIC_CLIENTS
308 (old->lifetime == client->lifetime) &&
309 namecmp(client_server) &&
313 (old->coa_server == client->coa_server) &&
314 (old->coa_pool == client->coa_pool) &&
316 (old->message_authenticator == client->message_authenticator)) {
317 WARN("Ignoring duplicate client %s", client->longname);
322 ERROR("Failed to add duplicate client %s", client->shortname);
328 * Other error adding client: likely is fatal.
330 if (!rbtree_insert(clients->trees[client->ipaddr.prefix], client)) {
336 tree_num = rbtree_create(clients, client_num_cmp, NULL, 0);
339 #ifdef WITH_DYNAMIC_CLIENTS
341 * More catching of clients added by rlm_sql.
343 * The sql modules sets the dynamic flag BEFORE calling
344 * us. The client_afrom_request() function sets it AFTER
347 if (client->dynamic && (client->lifetime == 0)) {
351 * If there IS an enclosing network,
352 * inherit the lifetime from it.
354 network = client_find(clients, &client->ipaddr, client->proto);
356 client->lifetime = network->lifetime;
361 client->number = tree_num_max;
363 if (tree_num) rbtree_insert(tree_num, client);
366 if (client->ipaddr.prefix < clients->min_prefix) {
367 clients->min_prefix = client->ipaddr.prefix;
370 (void) talloc_steal(clients, client); /* reparent it */
376 #ifdef WITH_DYNAMIC_CLIENTS
377 void client_delete(RADCLIENT_LIST *clients, RADCLIENT *client)
381 if (!clients) clients = root_clients;
383 if (!client->dynamic) return;
385 rad_assert(client->ipaddr.prefix <= 128);
387 client->dynamic = 2; /* signal to client_free */
390 rbtree_deletebydata(tree_num, client);
392 rbtree_deletebydata(clients->trees[client->ipaddr.prefix], client);
398 * Find a client in the RADCLIENTS list by number.
399 * This is a support function for the statistics code.
401 RADCLIENT *client_findbynumber(RADCLIENT_LIST const *clients, int number)
403 if (!clients) clients = root_clients;
405 if (!clients) return NULL;
407 if (number >= tree_num_max) return NULL;
412 myclient.number = number;
414 return rbtree_finddata(tree_num, &myclient);
420 RADCLIENT *client_findbynumber(UNUSED const RADCLIENT_LIST *clients, UNUSED int number)
428 * Find a client in the RADCLIENTS list.
430 RADCLIENT *client_find(RADCLIENT_LIST const *clients, fr_ipaddr_t const *ipaddr, int proto)
432 int32_t i, max_prefix;
435 if (!clients) clients = root_clients;
437 if (!clients || !ipaddr) return NULL;
439 switch (ipaddr->af) {
452 for (i = max_prefix; i >= (int32_t) clients->min_prefix; i--) {
455 myclient.ipaddr = *ipaddr;
456 myclient.proto = proto;
457 fr_ipaddr_mask(&myclient.ipaddr, i);
459 if (!clients->trees[i]) continue;
461 data = rbtree_finddata(clients->trees[i], &myclient);
462 if (data) return data;
469 * Old wrapper for client_find
471 RADCLIENT *client_find_old(fr_ipaddr_t const *ipaddr)
473 return client_find(root_clients, ipaddr, IPPROTO_UDP);
476 static fr_ipaddr_t cl_ipaddr;
477 static uint32_t cl_netmask;
478 static char const *cl_srcipaddr = NULL;
480 static char const *hs_proto = NULL;
484 static CONF_PARSER limit_config[] = {
485 { "max_connections", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, limit.max_connections), "16" },
487 { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, limit.lifetime), "0" },
489 { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, limit.idle_timeout), "30" },
491 { NULL, -1, 0, NULL, NULL } /* end the list */
495 static const CONF_PARSER client_config[] = {
496 { "ipaddr", FR_CONF_POINTER(PW_TYPE_COMBO_IP_PREFIX, &cl_ipaddr), NULL },
497 { "ipv4addr", FR_CONF_POINTER(PW_TYPE_IPV4_PREFIX, &cl_ipaddr), NULL },
498 { "ipv6addr", FR_CONF_POINTER(PW_TYPE_IPV6_PREFIX, &cl_ipaddr), NULL },
500 { "netmask", FR_CONF_POINTER(PW_TYPE_INTEGER, &cl_netmask), NULL },
502 { "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &cl_srcipaddr), NULL },
504 { "require_message_authenticator", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), "no" },
506 { "secret", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, RADCLIENT, secret), NULL },
507 { "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), NULL },
509 { "nas_type", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, nas_type), NULL },
511 { "login", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, login), NULL },
512 { "password", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, password), NULL },
513 { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, server), NULL },
514 { "response_window", FR_CONF_OFFSET(PW_TYPE_TIMEVAL, RADCLIENT, response_window), NULL },
517 { "proto", FR_CONF_POINTER(PW_TYPE_STRING, &hs_proto), NULL },
518 { "limit", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) limit_config },
521 #ifdef WITH_DYNAMIC_CLIENTS
522 { "dynamic_clients", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, client_server), NULL },
523 { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, lifetime), NULL },
524 { "rate_limit", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, rate_limit), NULL },
527 { NULL, -1, 0, NULL, NULL }
531 * Create the linked list of clients from the new configuration
532 * type. This way we don't have to change too much in the other
536 RADCLIENT_LIST *client_list_parse_section(CONF_SECTION *section, bool tls_required)
538 RADCLIENT_LIST *client_list_parse_section(CONF_SECTION *section, UNUSED bool tls_required)
541 bool global = false, in_server = false;
544 RADCLIENT_LIST *clients;
547 * Be forgiving. If there's already a clients, return
548 * it. Otherwise create a new one.
550 clients = cf_data_find(section, "clients");
551 if (clients) return clients;
553 clients = client_list_init(section);
554 if (!clients) return NULL;
556 if (cf_top_section(section) == section) global = true;
558 if (strcmp("server", cf_section_name1(section)) == 0) in_server = true;
561 * Associate the clients structure with the section.
563 if (cf_data_add(section, "clients", clients, NULL) < 0) {
564 cf_log_err_cs(section,
565 "Failed to associate clients with section %s",
566 cf_section_name1(section));
567 client_list_free(clients);
571 for (cs = cf_subsection_find_next(section, NULL, "client");
573 cs = cf_subsection_find_next(section, cs, "client")) {
574 c = client_afrom_cs(cs, cs, in_server, false);
581 * TLS clients CANNOT use non-TLS listeners.
582 * non-TLS clients CANNOT use TLS listeners.
584 if (tls_required != c->tls_required) {
585 cf_log_err_cs(cs, "Client does not have the same TLS configuration as the listener");
587 client_list_free(clients);
593 * FIXME: Add the client as data via cf_data_add,
594 * for migration issues.
597 #ifdef WITH_DYNAMIC_CLIENTS
599 if (c->client_server) {
604 struct stat stat_buf;
608 * Find the directory where individual
609 * client definitions are stored.
611 cp = cf_pair_find(cs, "directory");
612 if (!cp) goto add_client;
614 value = cf_pair_value(cp);
617 "The \"directory\" entry must not be empty");
622 DEBUG("including dynamic clients in %s", value);
624 dir = opendir(value);
626 cf_log_err_cs(cs, "Error reading directory %s: %s", value, fr_syserror(errno));
632 * Read the directory, ignoring "." files.
634 while ((dp = readdir(dir)) != NULL) {
638 if (dp->d_name[0] == '.') continue;
641 * Check for valid characters
643 for (p = dp->d_name; *p != '\0'; p++) {
644 if (isalpha((int)*p) ||
647 (*p == '.')) continue;
650 if (*p != '\0') continue;
652 snprintf(buf2, sizeof(buf2), "%s/%s",
655 if ((stat(buf2, &stat_buf) != 0) ||
656 S_ISDIR(stat_buf.st_mode)) continue;
658 dc = client_read(buf2, in_server, true);
661 "Failed reading client file \"%s\"", buf2);
668 * Validate, and add to the list.
670 if (!client_add_dynamic(clients, c, dc)) {
676 } /* loop over the directory */
679 #endif /* HAVE_DIRENT_H */
682 #endif /* WITH_DYNAMIC_CLIENTS */
683 if (!client_add(clients, c)) {
685 "Failed to add client %s",
686 cf_section_name2(cs));
694 * Replace the global list of clients with the new one.
695 * The old one is still referenced from the original
696 * configuration, and will be freed when that is freed.
699 root_clients = clients;
705 #ifdef WITH_DYNAMIC_CLIENTS
707 * We overload this structure a lot.
709 static const CONF_PARSER dynamic_config[] = {
710 { "FreeRADIUS-Client-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, ipaddr), NULL },
711 { "FreeRADIUS-Client-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, ipaddr), NULL },
712 { "FreeRADIUS-Client-IP-Prefix", FR_CONF_OFFSET(PW_TYPE_IPV4_PREFIX, RADCLIENT, ipaddr), NULL },
713 { "FreeRADIUS-Client-IPv6-Prefix", FR_CONF_OFFSET(PW_TYPE_IPV6_PREFIX, RADCLIENT, ipaddr), NULL },
714 { "FreeRADIUS-Client-Src-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, src_ipaddr), NULL },
715 { "FreeRADIUS-Client-Src-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, src_ipaddr), NULL },
717 { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), NULL },
719 { "FreeRADIUS-Client-Secret", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, secret), "" },
720 { "FreeRADIUS-Client-Shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), "" },
721 { "FreeRADIUS-Client-NAS-Type", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, nas_type), NULL },
722 { "FreeRADIUS-Client-Virtual-Server", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, server), NULL },
724 { NULL, -1, 0, NULL, NULL }
727 /** Add a dynamic client
730 bool client_add_dynamic(RADCLIENT_LIST *clients, RADCLIENT *master, RADCLIENT *c)
735 * No virtual server defined. Inherit the parent's
738 if (master->server && !c->server) {
739 c->server = talloc_typed_strdup(c, master->server);
743 * If the client network isn't global (not tied to a
744 * virtual server), then ensure that this clients server
745 * is the same as the enclosing networks virtual server.
747 if (master->server && (strcmp(master->server, c->server) != 0)) {
748 ERROR("Cannot add client %s/%i: Virtual server %s is not the same as the virtual server for the network",
749 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer)), c->ipaddr.prefix, c->server);
754 if (!client_add(clients, c)) {
755 ERROR("Cannot add client %s/%i: Internal error",
756 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer)), c->ipaddr.prefix);
762 * Initialize the remaining fields.
765 c->lifetime = master->lifetime;
766 c->created = time(NULL);
767 c->longname = talloc_typed_strdup(c, c->shortname);
769 INFO("Adding client %s/%i with shared secret \"%s\"",
770 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer)), c->ipaddr.prefix, c->secret);
779 /** Create a client CONF_SECTION using a mapping section to map values from a result set to client attributes
781 * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too.
783 * @note Caller should free CONF_SECTION passed in as out, on error.
784 * Contents of that section will be in an undefined state.
786 * @param[in,out] out Section to perform mapping on. Either the root of the client config, or a parent section
787 * (when this function is called recursively).
788 * Should be alloced with cf_section_alloc, or if there's a separate template section, the
789 * result of calling cf_section_dup on that section.
790 * @param[in] map section.
791 * @param[in] func to call to retrieve CONF_PAIR values. Must return a talloced buffer containing the value.
792 * @param[in] data to pass to func, usually a result pointer.
793 * @return 0 on success else -1 on error.
795 int client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data)
799 for (ci = cf_item_find_next(map, NULL);
801 ci = cf_item_find_next(map, ci)) {
808 * Recursively process map subsection
810 if (cf_item_is_section(ci)) {
811 CONF_SECTION *cs, *cc;
813 cs = cf_item_to_section(ci);
815 * Use pre-existing section or alloc a new one
817 cc = cf_section_sub_find_name2(out, cf_section_name1(cs), cf_section_name2(cs));
819 cc = cf_section_alloc(out, cf_section_name1(cs), cf_section_name2(cs));
820 cf_section_add(out, cc);
824 if (client_map_section(cc, cs, func, data) < 0) return -1;
828 cp = cf_item_to_pair(ci);
829 attr = cf_pair_attr(cp);
832 * The callback can return 0 (success) and not provide a value
833 * in which case we skip the mapping pair.
835 * Or return -1 in which case we error out.
837 if (func(&value, cp, data) < 0) {
838 cf_log_err_cs(out, "Failed performing mapping \"%s\" = \"%s\"", attr, cf_pair_value(cp));
841 if (!value) continue;
844 * Replace an existing CONF_PAIR
846 old = cf_pair_find(out, attr);
848 cf_pair_replace(out, old, value);
854 * ...or add a new CONF_PAIR
856 cp = cf_pair_alloc(out, attr, value, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
858 cf_log_err_cs(out, "Failed allocing pair \"%s\" = \"%s\"", attr, value);
863 cf_item_add(out, cf_pair_to_item(cp));
869 /** Allocate a new client from a config section
871 * @param ctx to allocate new clients in.
872 * @param cs to process as a client.
873 * @param in_server Whether the client should belong to a specific virtual server.
874 * @param with_coa If true and coa_server or coa_pool aren't specified automatically,
875 * create a coa home_server section and add it to the client CONF_SECTION.
876 * @return new RADCLIENT struct.
878 RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bool with_coa)
883 name2 = cf_section_name2(cs);
885 cf_log_err_cs(cs, "Missing client name");
890 * The size is fine.. Let's create the buffer
892 c = talloc_zero(ctx, RADCLIENT);
895 memset(&cl_ipaddr, 0, sizeof(cl_ipaddr));
898 if (cf_section_parse(cs, c, client_config) < 0) {
899 cf_log_err_cs(cs, "Error parsing client section");
911 * Global clients can set servers to use, per-server clients cannot.
913 if (in_server && c->server) {
914 cf_log_err_cs(cs, "Clients inside of an server section cannot point to a server");
919 * Allow the old method to specify "netmask". Just using "1.2.3.4" means it's a /32.
921 if (cl_netmask != 255) {
922 if ((cl_ipaddr.prefix != cl_netmask) &&
923 (((cl_ipaddr.af == AF_INET) && cl_ipaddr.prefix != 32) ||
924 ((cl_ipaddr.af == AF_INET6) && cl_ipaddr.prefix != 128))) {
925 cf_log_err_cs(cs, "Clients cannot use 'ipaddr/mask' and 'netmask' at the same time.");
929 cl_ipaddr.prefix = cl_netmask;
933 * Newer style client definitions with either ipaddr or ipaddr6
936 if (cf_pair_find(cs, "ipaddr") || cf_pair_find(cs, "ipv4addr") || cf_pair_find(cs, "ipv6addr")) {
940 * Sets ipv4/ipv6 address and prefix.
942 c->ipaddr = cl_ipaddr;
945 * Set the long name to be the result of a reverse lookup on the IP address.
947 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
948 c->longname = talloc_typed_strdup(c, buffer);
951 * Set the short name to the name2.
953 if (!c->shortname) c->shortname = talloc_typed_strdup(c, name2);
955 * No "ipaddr" or "ipv6addr", use old-style "client <ipaddr> {" syntax.
958 cf_log_err_cs(cs, "No 'ipaddr' or 'ipv4addr' or 'ipv6addr' configuration "
959 "directive found in client %s", name2);
963 c->proto = IPPROTO_UDP;
965 if (strcmp(hs_proto, "udp") == 0) {
969 } else if (strcmp(hs_proto, "tcp") == 0) {
971 c->proto = IPPROTO_TCP;
973 } else if (strcmp(hs_proto, "tls") == 0) {
975 c->proto = IPPROTO_TCP;
976 c->tls_required = true;
978 } else if (strcmp(hs_proto, "radsec") == 0) {
980 c->proto = IPPROTO_TCP;
981 c->tls_required = true;
983 } else if (strcmp(hs_proto, "*") == 0) {
985 c->proto = IPPROTO_IP; /* fake for dual */
988 cf_log_err_cs(cs, "Unknown proto \"%s\".", hs_proto);
994 * If a src_ipaddr is specified, when we send the return packet
995 * we will use this address instead of the src from the
999 #ifdef WITH_UDPFROMTO
1000 switch (c->ipaddr.af) {
1002 if (fr_pton4(&c->src_ipaddr, cl_srcipaddr, -1, true, false) < 0) {
1003 cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror());
1009 if (fr_pton6(&c->src_ipaddr, cl_srcipaddr, -1, true, false) < 0) {
1010 cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror());
1018 WARN("Server not built with udpfromto, ignoring client src_ipaddr");
1020 cl_srcipaddr = NULL;
1024 * A response_window of zero is OK, and means that it's
1025 * ignored by the rest of the server timers.
1027 if (timerisset(&c->response_window)) {
1028 FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, >=, 0, 1000);
1029 FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, 60, 0);
1030 FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, main_config.max_request_time, 0);
1033 #ifdef WITH_DYNAMIC_CLIENTS
1034 if (c->client_server) {
1035 c->secret = talloc_typed_strdup(c, "testing123");
1037 if (((c->ipaddr.af == AF_INET) && (c->ipaddr.prefix == 32)) ||
1038 ((c->ipaddr.af == AF_INET6) && (c->ipaddr.prefix == 128))) {
1039 cf_log_err_cs(cs, "Dynamic clients MUST be a network, not a single IP address");
1047 if (!c->secret || (c->secret[0] == '\0')) {
1049 char const *value = NULL;
1050 CONF_PAIR *cp = cf_pair_find(cs, "dhcp");
1052 if (cp) value = cf_pair_value(cp);
1055 * Secrets aren't needed for DHCP.
1057 if (value && (strcmp(value, "yes") == 0)) return c;
1062 * If the client is TLS only, the secret can be
1063 * omitted. When omitted, it's hard-coded to
1064 * "radsec". See RFC 6614.
1066 if (c->tls_required) {
1067 c->secret = talloc_typed_strdup(cs, "radsec");
1072 cf_log_err_cs(cs, "secret must be at least 1 character long");
1082 * Point the client to the home server pool, OR to the
1083 * home server. This gets around the problem of figuring
1084 * out which port to use.
1086 cp = cf_pair_find(cs, "coa_server");
1088 c->coa_name = cf_pair_value(cp);
1089 c->coa_pool = home_pool_byname(c->coa_name, HOME_TYPE_COA);
1091 c->coa_server = home_server_byname(c->coa_name, HOME_TYPE_COA);
1093 if (!c->coa_pool && !c->coa_server) {
1094 cf_log_err_cs(cs, "No such home_server or home_server_pool \"%s\"", c->coa_name);
1098 * If we're implicitly adding a CoA home server for
1099 * every client, or there's a server subsection,
1100 * create a home server CONF_SECTION and then parse
1101 * it into a home_server_t.
1103 } else if (with_coa || cf_section_sub_find(cs, "coa_server")) {
1104 CONF_SECTION *server;
1105 home_server_t *home;
1107 if (((c->ipaddr.af == AF_INET) && (c->ipaddr.prefix != 32)) ||
1108 ((c->ipaddr.af == AF_INET6) && (c->ipaddr.prefix != 128))) {
1109 WARN("Subnets not supported for home servers. "
1110 "Not adding client %s as home_server", name2);
1114 server = home_server_cs_afrom_client(cs);
1115 if (!server) goto error;
1118 * Must be allocated in the context of the client,
1119 * as allocating using the context of the
1120 * realm_config_t without a mutex, by one of the
1121 * workers, would be bad.
1123 home = home_server_afrom_cs(NULL, NULL, server);
1125 talloc_free(server);
1129 rad_assert(home->type == HOME_TYPE_COA);
1131 c->coa_server = home;
1132 c->defines_coa_server = true;
1139 if ((c->proto == IPPROTO_TCP) || (c->proto == IPPROTO_IP)) {
1140 if ((c->limit.idle_timeout > 0) && (c->limit.idle_timeout < 5))
1141 c->limit.idle_timeout = 5;
1142 if ((c->limit.lifetime > 0) && (c->limit.lifetime < 5))
1143 c->limit.lifetime = 5;
1144 if ((c->limit.lifetime > 0) && (c->limit.idle_timeout > c->limit.lifetime))
1145 c->limit.idle_timeout = 0;
1152 /** Add a client from a result set (SQL)
1154 * @todo This function should die. SQL should use client_afrom_cs.
1156 * @param ctx Talloc context.
1157 * @param identifier Client IP Address / IPv4 subnet / IPv6 subnet / FQDN.
1158 * @param secret Client secret.
1159 * @param shortname Client friendly name.
1160 * @param type NAS-Type.
1161 * @param server Virtual-Server to associate clients with.
1162 * @param require_ma If true all packets from client must include a message-authenticator.
1163 * @return The new client, or NULL on error.
1165 RADCLIENT *client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char const *secret,
1166 char const *shortname, char const *type, char const *server, bool require_ma)
1171 rad_assert(identifier);
1174 c = talloc_zero(ctx, RADCLIENT);
1176 if (fr_pton(&c->ipaddr, identifier, -1, true) < 0) {
1177 ERROR("%s", fr_strerror());
1183 #ifdef WITH_DYNAMIC_CLIENTS
1186 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
1187 c->longname = talloc_typed_strdup(c, buffer);
1190 * Other values (secret, shortname, nas_type, virtual_server)
1192 c->secret = talloc_typed_strdup(c, secret);
1193 if (shortname) c->shortname = talloc_typed_strdup(c, shortname);
1194 if (type) c->nas_type = talloc_typed_strdup(c, type);
1195 if (server) c->server = talloc_typed_strdup(c, server);
1196 c->message_authenticator = require_ma;
1201 /** Create a new client, consuming all attributes in the control list of the request
1203 * @param clients list to add new client to.
1204 * @param request Fake request.
1205 * @return a new client on success, else NULL on error.
1207 RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
1213 CONF_PAIR *cp = NULL;
1217 VALUE_PAIR *vp = NULL;
1219 if (!clients || !request) return NULL;
1221 snprintf(buffer, sizeof(buffer), "dynamic%i", cnt++);
1223 c = talloc_zero(clients, RADCLIENT);
1224 c->cs = cf_section_alloc(NULL, "client", buffer);
1225 talloc_steal(c, c->cs);
1226 c->ipaddr.af = AF_UNSPEC;
1227 c->src_ipaddr.af = AF_UNSPEC;
1229 fr_cursor_init(&cursor, &request->config);
1231 RDEBUG2("Converting control list to client fields");
1233 for (i = 0; dynamic_config[i].name != NULL; i++) {
1234 DICT_ATTR const *da;
1235 char *strvalue = NULL;
1237 da = dict_attrbyname(dynamic_config[i].name);
1239 RERROR("Cannot add client %s: attribute \"%s\" is not in the dictionary",
1240 ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)),
1241 dynamic_config[i].name);
1249 fr_cursor_first(&cursor);
1250 if (!fr_cursor_next_by_da(&cursor, da, TAG_ANY)) {
1252 * Not required. Skip it.
1254 if (!dynamic_config[i].dflt) continue;
1256 RERROR("Cannot add client %s: Required attribute \"%s\" is missing",
1257 ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)),
1258 dynamic_config[i].name);
1261 vp = fr_cursor_remove(&cursor);
1264 * Freed at the same time as the vp.
1266 strvalue = vp_aprints_value(vp, vp, '\'');
1268 switch (dynamic_config[i].type) {
1269 case PW_TYPE_IPV4_ADDR:
1270 if (da->attr == PW_FREERADIUS_CLIENT_IP_ADDRESS) {
1271 c->ipaddr.af = AF_INET;
1272 c->ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
1273 c->ipaddr.prefix = 32;
1274 cp = cf_pair_alloc(c->cs, "ipv4addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
1275 } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS) {
1276 #ifdef WITH_UDPFROMTO
1277 RDEBUG2("src_ipaddr = %s", strvalue);
1278 c->src_ipaddr.af = AF_INET;
1279 c->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
1280 c->src_ipaddr.prefix = 32;
1281 cp = cf_pair_alloc(c->cs, "src_ipaddr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
1283 RWARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IP-Address");
1289 case PW_TYPE_IPV6_ADDR:
1290 if (da->attr == PW_FREERADIUS_CLIENT_IPV6_ADDRESS) {
1291 c->ipaddr.af = AF_INET6;
1292 c->ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
1293 c->ipaddr.prefix = 128;
1294 cp = cf_pair_alloc(c->cs, "ipv6addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
1295 } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) {
1296 #ifdef WITH_UDPFROMTO
1297 c->src_ipaddr.af = AF_INET6;
1298 c->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
1299 c->src_ipaddr.prefix = 128;
1300 cp = cf_pair_alloc(c->cs, "src_addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
1302 RWARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IPv6-Address");
1308 case PW_TYPE_IPV4_PREFIX:
1309 if (da->attr == PW_FREERADIUS_CLIENT_IP_PREFIX) {
1310 c->ipaddr.af = AF_INET;
1311 memcpy(&c->ipaddr.ipaddr.ip4addr, &vp->vp_ipv4prefix[2],
1312 sizeof(c->ipaddr.ipaddr.ip4addr.s_addr));
1313 fr_ipaddr_mask(&c->ipaddr, (vp->vp_ipv4prefix[1] & 0x3f));
1314 cp = cf_pair_alloc(c->cs, "ipv4addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
1319 case PW_TYPE_IPV6_PREFIX:
1320 if (da->attr == PW_FREERADIUS_CLIENT_IPV6_PREFIX) {
1321 c->ipaddr.af = AF_INET6;
1322 memcpy(&c->ipaddr.ipaddr.ip6addr, &vp->vp_ipv6prefix[2],
1323 sizeof(c->ipaddr.ipaddr.ip6addr));
1324 fr_ipaddr_mask(&c->ipaddr, vp->vp_ipv6prefix[1]);
1325 cp = cf_pair_alloc(c->cs, "ipv6addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
1330 case PW_TYPE_STRING:
1332 CONF_PARSER const *parse;
1335 * Cache pointer to CONF_PAIR buffer in RADCLIENT struct
1337 p = (char **) ((char *) c + dynamic_config[i].offset);
1338 if (*p) TALLOC_FREE(*p);
1339 if (!vp->vp_strvalue[0]) break;
1342 * We could reuse the CONF_PAIR buff, this just keeps things
1343 * consistent between client_afrom_cs, and client_afrom_query.
1345 *p = talloc_strdup(c, strvalue);
1348 * This is fairly nasty... In order to figure out the CONF_PAIR
1349 * name associated with a field, find offsets that match between
1350 * the dynamic_config CONF_PARSER table, and the client_config
1351 * CONF_PARSER table.
1353 * This is so that things that expect to find CONF_PAIRs in the
1354 * client CONF_SECTION for fields like 'nas_type' can.
1356 for (parse = client_config; parse->name; parse++) {
1357 if (parse->offset == dynamic_config[i].offset) break;
1361 cp = cf_pair_alloc(c->cs, parse->name, strvalue, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
1365 case PW_TYPE_BOOLEAN:
1367 CONF_PARSER const *parse;
1369 pi = (int *) ((bool *) ((char *) c + dynamic_config[i].offset));
1370 *pi = vp->vp_integer;
1373 * Same nastiness as above.
1375 for (parse = client_config; parse->name; parse++) {
1376 if (parse->offset == dynamic_config[i].offset) break;
1380 cp = cf_pair_alloc(c->cs, parse->name, strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
1389 RERROR("Error creating equivalent conf pair for %s", vp->da->name);
1393 if (cf_pair_attr_type(cp) == T_SINGLE_QUOTED_STRING) {
1394 RDEBUG2("%s = '%s'", cf_pair_attr(cp), cf_pair_value(cp));
1396 RDEBUG2("%s = %s", cf_pair_attr(cp), cf_pair_value(cp));
1398 cf_pair_add(c->cs, cp);
1403 fr_cursor_first(&cursor);
1404 vp = fr_cursor_remove(&cursor);
1409 value = vp_aprints_value(vp, vp, '\'');
1411 ERROR("Failed stringifying value of &control:%s", vp->da->name);
1415 if (vp->da->type == PW_TYPE_STRING) {
1416 RDEBUG2("%s = '%s'", vp->da->name, value);
1417 cp = cf_pair_alloc(c->cs, vp->da->name, value, T_OP_SET,
1418 T_BARE_WORD, T_SINGLE_QUOTED_STRING);
1420 RDEBUG2("%s = %s", vp->da->name, value);
1421 cp = cf_pair_alloc(c->cs, vp->da->name, value, T_OP_SET,
1422 T_BARE_WORD, T_BARE_WORD);
1424 cf_pair_add(c->cs, cp);
1427 } while ((vp = fr_cursor_remove(&cursor)));
1431 if (c->ipaddr.af == AF_UNSPEC) {
1432 RERROR("Cannot add client %s: No IP address was specified.",
1433 ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)));
1442 * Need to apply the same mask as we set for the client
1443 * else clients created with FreeRADIUS-Client-IPv6-Prefix
1444 * or FreeRADIUS-Client-IPv4-Prefix will fail this check.
1446 addr = request->packet->src_ipaddr;
1447 fr_ipaddr_mask(&addr, c->ipaddr.prefix);
1448 if (fr_ipaddr_cmp(&addr, &c->ipaddr) != 0) {
1451 RERROR("Cannot add client %s: Not in specified subnet %s/%i",
1452 ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)),
1453 ip_ntoh(&c->ipaddr, buf2, sizeof(buf2)), c->ipaddr.prefix);
1458 if (!c->secret || !*c->secret) {
1459 RERROR("Cannot add client %s: No secret was specified",
1460 ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)));
1464 if (!client_add_dynamic(clients, request->client, c)) {
1468 if ((c->src_ipaddr.af != AF_UNSPEC) && (c->src_ipaddr.af != c->ipaddr.af)) {
1469 RERROR("Cannot add client %s: Client IP and src address are different IP version",
1470 ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)));
1479 * Read a client definition from the given filename.
1481 RADCLIENT *client_read(char const *filename, int in_server, int flag)
1488 if (!filename) return NULL;
1490 cs = cf_section_alloc(NULL, "main", NULL);
1491 if (!cs) return NULL;
1493 if (cf_file_read(cs, filename) < 0) {
1498 cs = cf_section_sub_find(cs, "client");
1500 ERROR("No \"client\" section found in client file");
1504 c = client_afrom_cs(cs, cs, in_server, false);
1505 if (!c) return NULL;
1507 p = strrchr(filename, FR_DIR_SEP);
1514 if (!flag) return c;
1517 * Additional validations
1519 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
1520 if (strcmp(p, buffer) != 0) {
1521 ERROR("Invalid client definition in %s: IP address %s does not match name %s", filename, buffer, p);