/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 | } |