Radcli library 2.0.0
A simple radius library -- legacy API reference
Loading...
Searching...
No Matches
config.c
1/*
2 * Copyright (C) 1995,1996,1997 Lars Fenneberg
3 *
4 * Copyright 1992 Livingston Enterprises, Inc.
5 *
6 * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
7 * and Merit Network, Inc. All Rights Reserved
8 *
9 * See the file COPYRIGHT for the respective terms and conditions.
10 * If the file is missing contact me at lf@elemental.net
11 * and I'll send you a copy.
12 *
13 */
14
22
23#include <config.h>
24#include <includes.h>
25#include <radcli/radcli.h>
26#include <options.h>
27#include "util.h"
28#include "tls.h"
29#include "dict_rfc_gen.h"
30
31#ifndef TRUE
32#define TRUE 1
33#define FALSE 0
34#endif
35
36static int rc_conf_int_2(rc_handle const *rh, char const *optname, int complain);
37
38/* The template every rc_handle's config_options[] is memcpy()'d from
39 * (lib/config.c's radcli2_priv_new()/radcli2_priv_read_config()); the per-id enum in
40 * lib/options.h's RC_OPTION_TABLE indexes straight into it/its copies. */
41static OPTION config_options_default[] = {
42#define RADCLI_OPT_ENTRY(id, name, type) {name, type, ST_UNDEF, NULL},
43 RC_OPTION_TABLE
44#undef RADCLI_OPT_ENTRY
45};
46
47#define NUM_OPTIONS ((sizeof(config_options_default))/(sizeof(config_options_default[0])))
48
49/* Find an option in the option list
50 *
51 * @param rh a handle to parsed configuration.
52 * @param optname the name of the option.
53 * @param type the option type.
54 * @return pointer to option on success, NULL otherwise.
55 */
56/*- Look up optname's OPTION entry, scanning linearly (few enough options
57 * that a binary search isn't worthwhile).
58 *
59 * @param rh a handle to parsed configuration.
60 * @param optname the option name to look up.
61 * @param type a bitmask of acceptable RADCLI_OPT_TYPE_* types for the match.
62 * @return the option's entry, or NULL if optname is unknown or its type
63 * doesn't match type.
64 -*/
65static OPTION *find_option(rc_handle const *rh, char const *optname, unsigned int type)
66{
67 int i;
68
69 /* there're so few options that a binary search seems not necessary */
70 for (i = 0; i < NUM_OPTIONS; i++) {
71 if (!strcmp(rh->config_options[i].name, optname) &&
72 (rh->config_options[i].type & type))
73 {
74 return &rh->config_options[i];
75 }
76 }
77
78 return NULL;
79}
80
81/*- Report whether name is one of RC_IGNORED_OPTION_TABLE's legacy option
82 * names -- accepted for backward compatibility with existing config
83 * files, but never stored (see lib/options.h's RC_IGNORED_OPTION_TABLE
84 * comment).
85 *
86 * @param name the option name to check.
87 * @return nonzero if name is an ignored legacy option, zero otherwise.
88 -*/
89static int rc_ignored_option(char const *name)
90{
91#define X(n) if (!strcmp(name, n)) return 1;
92 RC_IGNORED_OPTION_TABLE
93#undef X
94 return 0;
95}
96
97/* Index-based option lookup for internal callers that already know which
98 * option they want at compile time (an OPT_* from lib/options.h's
99 * RC_OPTION_TABLE) -- skips find_option()'s string scan entirely. Only
100 * ever called with a literal OPT_* id, so id/type pairing is fixed at
101 * compile time; the assert()s below catch a future miswired call site. */
102/*- Look up id's OPTION entry directly by index.
103 *
104 * @param rh a handle to parsed configuration.
105 * @param id the option's compile-time index.
106 * @return the option's entry; never NULL (asserts rh/id are valid).
107 -*/
108static inline OPTION *rc_option_by_id(rc_handle const *rh, rc_option_id id)
109{
110 assert(rh != NULL && rh->config_options != NULL);
111 assert((unsigned)id < NUM_OPTIONS);
112 return &rh->config_options[id];
113}
114
115/*- Get the value of an integer-typed config option by compile-time index.
116 *
117 * @param rh a handle to parsed configuration.
118 * @param id the option's compile-time index; must be RADCLI_OPT_TYPE_INT.
119 * @return config option value, or 0 if unset.
120 -*/
121int rc_conf_int_id(rc_handle const *rh, rc_option_id id)
122{
123 OPTION *option = rc_option_by_id(rh, id);
124
125 assert(option->type & RADCLI_OPT_TYPE_INT);
126
127 if (option->val)
128 return *((int *)option->val);
129
130 rc_log(LOG_INFO, "radcli2_priv_conf_int: config option %s was not set", option->name);
131 return 0;
132}
133
134/*- Get the value of a string-typed config option by compile-time index.
135 *
136 * @param rh a handle to parsed configuration.
137 * @param id the option's compile-time index; must be RADCLI_OPT_TYPE_STR.
138 * @return config option value.
139 -*/
140char *rc_conf_str_id(rc_handle const *rh, rc_option_id id)
141{
142 OPTION *option = rc_option_by_id(rh, id);
143
144 assert(option->type & RADCLI_OPT_TYPE_STR);
145
146 return (char *)option->val;
147}
148
149/*- Set a string-typed option's value, duplicating p.
150 *
151 * @param filename the name of the config file (for logging purposes).
152 * @param line the line number in the file (for logging purposes).
153 * @param option the option to set.
154 * @param p the value, or NULL to clear it.
155 * @return 0 on success, -1 on allocation failure.
156 -*/
157static int set_option_str(char const *filename, int line, OPTION *option, char const *p)
158{
159 if (p) {
160 option->val = (void *) strdup(p);
161 if (option->val == NULL) {
162 rc_log(LOG_CRIT, "read_config: out of memory");
163 return -1;
164 }
165 } else {
166 option->val = NULL;
167 }
168
169 return 0;
170}
171
172/*- Set an integer-typed option's value, parsing p.
173 *
174 * watchdog-interval carries a floor below which it is refused outright (0
175 * still disables it, unchanged): 1-5 would just mean radcli spends nearly
176 * all its time reconnecting/sending Status-Server packets when
177 * REQ-WATCHDOG-NET-003's 2.5x-interval dead-peer threshold is this close to the
178 * interval itself, RFC 3539 SS3.4's watchdog algorithm assumes Tw is chosen
179 * with meaningful headroom, and TCP-level connection churn that fast is not
180 * useful "liveness" checking. Enforced here, the single choke point both
181 * the config-file reader and radcli_ctx_set_opt_int() (lib/config2.c)
182 * funnel through, so both paths agree.
183 *
184 * @param filename the name of the config file (for logging purposes).
185 * @param line the line number in the file (for logging purposes).
186 * @param option the option to set.
187 * @param p the value text to parse; must be non-NULL.
188 * @return 0 on success, -1 if p is NULL, out of range for option, or on
189 * allocation failure.
190 -*/
191static int set_option_int(char const *filename, int line, OPTION *option, char const *p)
192{
193 int *iptr;
194 int val;
195
196 if (p == NULL) {
197 rc_log(LOG_ERR, "%s: line %d: bogus option value", filename, line);
198 return -1;
199 }
200
201 val = atoi(p);
202
203 if (strcmp(option->name, "watchdog-interval") == 0 && val >= 1 && val <= 5) {
204 rc_log(LOG_ERR, "%s: line %d: watchdog-interval must be 0 (disabled) "
205 "or at least 6 seconds, not %d", filename, line, val);
206 return -1;
207 }
208
209 if ((iptr = malloc(sizeof(*iptr))) == NULL) {
210 rc_log(LOG_CRIT, "read_config: out of memory");
211 return -1;
212 }
213
214 *iptr = val;
215 option->val = (void *) iptr;
216
217 return 0;
218}
219
220/* Shared by set_option_srv()'s parse-failure cleanup and
221 * radcli2_priv_config_free(), which both need to release the same
222 * per-entry allocations. */
223/*- Free serv->name[i]/secret[i] for i in [from, to), nulling each
224 * pointer afterwards.
225 *
226 * @param serv the server list to free entries from.
227 * @param from the first index to free.
228 * @param to one past the last index to free.
229 -*/
230static void server_free_entries(SERVER *serv, unsigned from, unsigned to)
231{
232 unsigned i;
233
234 for (i = from; i < to; i++) {
235 free(serv->name[i]);
236 free(serv->secret[i]);
237 serv->name[i] = NULL;
238 serv->secret[i] = NULL;
239 }
240}
241
242/*- Parse and append server-list entries onto a server-typed option,
243 * accepting a comma-separated "host[:port[:secret]]" list.
244 *
245 * @param rh a handle to parsed configuration.
246 * @param filename the name of the config file (for logging purposes).
247 * @param line the line number in the file (for logging purposes).
248 * @param option the RADCLI_OPT_TYPE_SRV option to append entries to.
249 * @param p the comma-separated server list text.
250 * @return 0 on success, -1 on a parse error or allocation failure.
251 -*/
252static int set_option_srv(rc_handle *rh, char const *filename, int line, OPTION *option, char const *p)
253{
254 SERVER *serv;
255 char *p_pointer;
256 char *p_dupe;
257 char *p_save;
258 char *q;
259 char *s;
260 struct servent *svp;
261 unsigned start_max;
262
263 p_dupe = strdup(p);
264
265 if (p_dupe == NULL) {
266 rc_log(LOG_ERR, "%s: line %d: Invalid option or memory failure", filename, line);
267 return -1;
268 }
269
270 serv = (SERVER *) option->val;
271 if (serv == NULL) {
272 serv = calloc(1, sizeof(*serv));
273 if (serv == NULL) {
274 rc_log(LOG_CRIT, "read_config: out of memory");
275 free(p_dupe);
276 return -1;
277 }
278 serv->max = 0;
279 }
280 start_max = serv->max;
281
282 p_pointer = strtok_r(p_dupe, ", \t", &p_save);
283
284 while(p_pointer != NULL) {
285 if (serv->max >= RC_SERVER_MAX) {
286 DEBUG(rh, LOG_ERR, "cannot set more than %d servers", RC_SERVER_MAX);
287 goto fail;
288 }
289
290 DEBUG(rh, LOG_ERR, "processing server: %s", p_pointer);
291 /* check to see for '[IPv6]:port' syntax */
292 if ((q = strchr(p_pointer,'[')) != NULL) {
293 *q = '\0';
294 q++;
295 p_pointer = q;
296
297 q = strchr(p_pointer, ']');
298 if (q == NULL) {
299 rc_log(LOG_CRIT, "read_config: IPv6 parse error");
300 goto fail;
301 }
302 *q = '\0';
303 q++;
304
305 if (q[0] == ':') {
306 q++;
307 }
308
309 /* Check to see if we have '[IPv6]:port:secret' syntax */
310 if((s=strchr(q, ':')) != NULL) {
311 *s = '\0';
312 s++;
313 serv->secret[serv->max] = strdup(s);
314 if (serv->secret[serv->max] == NULL) {
315 rc_log(LOG_CRIT, "read_config: out of memory");
316 goto fail;
317 }
318 }
319
320 } else /* Check to see if we have 'servername:port' syntax */
321 if ((q = strchr(p_pointer,':')) != NULL) {
322 *q = '\0';
323 q++;
324
325 /* Check to see if we have 'servername:port:secret' syntax */
326 if((s = strchr(q,':')) != NULL) {
327 *s = '\0';
328 s++;
329 serv->secret[serv->max] = strdup(s);
330 if (serv->secret[serv->max] == NULL) {
331 rc_log(LOG_CRIT, "read_config: out of memory");
332 goto fail;
333 }
334 }
335 }
336
337 if(q && strlen(q) > 0) {
338 serv->port[serv->max] = atoi(q);
339 } else {
340 if (!strcmp(option->name,"authserver"))
341 if ((svp = getservbyname ("radius", "udp")) == NULL)
342 serv->port[serv->max] = PW_AUTH_UDP_PORT;
343 else
344 serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
345 else if (!strcmp(option->name, "acctserver"))
346 if ((svp = getservbyname ("radacct", "udp")) == NULL)
347 serv->port[serv->max] = PW_ACCT_UDP_PORT;
348 else
349 serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
350 else {
351 rc_log(LOG_ERR, "%s: line %d: no default port for %s", filename, line, option->name);
352 goto fail;
353 }
354 }
355
356 serv->name[serv->max] = strdup(p_pointer);
357 if (serv->name[serv->max] == NULL) {
358 rc_log(LOG_CRIT, "read_config: out of memory");
359 goto fail;
360 }
361
362 serv->max++;
363 p_pointer = strtok_r(NULL, ", \t", &p_save);
364 }
365
366 free(p_dupe);
367 if (option->val == NULL)
368 option->val = (void *)serv;
369
370 return 0;
371 fail:
372 free(p_dupe);
373 /* Release whatever this call already committed (start_max..max),
374 * plus the in-progress entry's secret if it was parsed before the
375 * failure (name[] is only ever set last, right before max++, so
376 * it never needs freeing here). Without this, a config line that
377 * fails partway through (e.g. more than RC_SERVER_MAX servers)
378 * leaks every entry already parsed. */
379 server_free_entries(serv, start_max, serv->max);
380 serv->max = start_max;
381 if (serv->max < RC_SERVER_MAX) {
382 free(serv->secret[serv->max]);
383 serv->secret[serv->max] = NULL;
384 }
385 if (option->val == NULL)
386 free(serv);
387 return -1;
388
389}
390
391/*- Add a config option to rc_handle from inside a program, letting a
392 * program set up a handle without loading a configuration file.
393 *
394 * @param rh a handle to parsed configuration.
395 * @param option_name the name of the option.
396 * @param option_val the value to be added.
397 * @param source typically should be __FILE__ or __func__ for logging purposes.
398 * @param line __LINE__ for logging purposes.
399 * @return 0 on success, -1 on failure.
400 -*/
401int radcli2_priv_add_config(rc_handle *rh, char const *option_name, char const *option_val, char const *source, int line)
402{
403 OPTION *option;
404
405 if ((option = find_option(rh, option_name, OT_ANY)) == NULL)
406 {
407 if (rc_ignored_option(option_name))
408 return 0;
409 rc_log(LOG_ERR, "ERROR: unrecognized option: %s", option_name);
410 return -1;
411 }
412
413 if (option->status != ST_UNDEF)
414 {
415 rc_log(LOG_ERR, "ERROR: duplicate option: %s", option_name);
416 return -1;
417 }
418
419 switch (option->type) {
420 case RADCLI_OPT_TYPE_STR:
421 if (set_option_str(source, line, option, option_val) < 0) {
422 return -1;
423 }
424 break;
425 case RADCLI_OPT_TYPE_INT:
426 if (set_option_int(source, line, option, option_val) < 0) {
427 return -1;
428 }
429 break;
430 case RADCLI_OPT_TYPE_SRV:
431 if (set_option_srv(rh, source, line, option, option_val) < 0) {
432 return -1;
433 }
434 break;
435 default:
436 rc_log(LOG_CRIT, "radcli2_priv_add_config: impossible case branch!");
437 abort();
438 }
439
440 return 0;
441}
442
443/*- Initialise a configuration structure for programmatic configuration.
444 *
445 * Use this when configuring radcli from code rather than from a file. The
446 * full call sequence: radcli2_priv_new(), this function,
447 * radcli2_priv_add_config() per option, then radcli2_priv_apply_config()
448 * to activate.
449 *
450 * @param rh a handle allocated by radcli2_priv_new().
451 * @return rh on success, NULL on failure (rh is freed on failure).
452 -*/
453rc_handle *radcli2_priv_config_init(rc_handle *rh)
454{
455 SERVER *authservers = NULL;
456 SERVER *acctservers;
457 OPTION *acct;
458 OPTION *auth;
459
460 rh->config_options = malloc(sizeof(config_options_default));
461 if (rh->config_options == NULL)
462 {
463 rc_log(LOG_CRIT, "radcli2_priv_config_init: out of memory");
464 radcli2_priv_destroy(rh);
465 return NULL;
466 }
467 memcpy(rh->config_options, &config_options_default, sizeof(config_options_default));
468
469 auth = find_option(rh, "authserver", OT_ANY);
470 if (auth) {
471 authservers = calloc(1, sizeof(SERVER));
472 if(authservers == NULL) {
473 rc_log(LOG_CRIT, "radcli2_priv_config_init: error initializing server structs");
474 radcli2_priv_destroy(rh);
475 return NULL;
476 }
477 auth->val = authservers;
478 }
479
480 acct = find_option(rh, "acctserver", OT_ANY);
481 if (acct) {
482 acctservers = calloc(1, sizeof(SERVER));
483 if(acctservers == NULL) {
484 rc_log(LOG_CRIT, "radcli2_priv_config_init: error initializing server structs");
485 radcli2_priv_destroy(rh);
486 if(authservers) free(authservers);
487 return NULL;
488 }
489 acct->val = acctservers;
490 }
491
492 return rh;
493}
494
495/*- rc_sockets_override.sendto for the plain UDP transport: a thin
496 * sendto(2) wrapper.
497 -*/
498static ssize_t plain_sendto(void *ptr, int sockfd,
499 const void *buf, size_t len, int flags,
500 const struct sockaddr *dest_addr, socklen_t addrlen)
501{
502 return sendto(sockfd, buf, len, flags, dest_addr, addrlen);
503}
504
505/*- rc_sockets_override.sendto for the plain TCP transport: connect(2)
506 * then sendto(2).
507 -*/
508static ssize_t plain_tcp_sendto(void *ptr, int sockfd,
509 const void *buf, size_t len, int flags,
510 const struct sockaddr *dest_addr, socklen_t addrlen)
511{
512 if((connect(sockfd, dest_addr, addrlen)) != 0){
513 rc_log(LOG_ERR, "%s: Connect Call Failed : %s", __FUNCTION__, strerror(errno));
514 return -1;
515 }
516 return sendto(sockfd, buf, len, flags, dest_addr, addrlen);
517}
518
519/*- rc_sockets_override.recvfrom for the plain UDP/TCP transports: a thin
520 * recvfrom(2) wrapper.
521 -*/
522static ssize_t plain_recvfrom(void *ptr, int sockfd,
523 void *buf, size_t len, int flags,
524 struct sockaddr *src_addr, socklen_t * addrlen)
525{
526 return recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
527}
528
529/*- rc_sockets_override.close_fd for the plain UDP/TCP transports: a thin
530 * close(2) wrapper.
531 -*/
532static void plain_close_fd(int fd)
533{
534 close(fd);
535}
536
537/*- rc_sockets_override.get_fd for the plain UDP transport: open and bind
538 * a UDP socket at an ephemeral port on our_sockaddr's address/family.
539 *
540 * @param ptr unused; part of the get_fd calling convention.
541 * @param our_sockaddr the local address to bind to (port overwritten with 0).
542 * @return the new socket, or -1 on failure.
543 -*/
544static int plain_get_fd(void *ptr, struct sockaddr *our_sockaddr)
545{
546 int sockfd;
547
548 sockfd = socket(our_sockaddr->sa_family, SOCK_DGRAM, 0);
549 if (sockfd < 0) {
550 return -1;
551 }
552
553 if (our_sockaddr->sa_family == AF_INET)
554 ((struct sockaddr_in *)our_sockaddr)->sin_port = 0;
555 else
556 ((struct sockaddr_in6 *)our_sockaddr)->sin6_port = 0;
557
558 if (bind(sockfd, SA(our_sockaddr), SA_LEN(our_sockaddr)) < 0) {
559 close(sockfd);
560 return -1;
561 }
562 return sockfd;
563}
564
565/*- rc_sockets_override.get_fd for the plain TCP transport: open and bind
566 * a TCP socket at an ephemeral port on our_sockaddr's address/family.
567 *
568 * @param ptr unused; part of the get_fd calling convention.
569 * @param our_sockaddr the local address to bind to (port overwritten with 0).
570 * @return the new socket, or -1 on failure.
571 -*/
572static int plain_tcp_get_fd(void *ptr, struct sockaddr *our_sockaddr)
573{
574 int sockfd;
575
576 sockfd = socket(our_sockaddr->sa_family, SOCK_STREAM, 0);
577 if (sockfd < 0) {
578 return -1;
579 }
580
581 if (our_sockaddr->sa_family == AF_INET)
582 ((struct sockaddr_in *)our_sockaddr)->sin_port = 0;
583 else
584 ((struct sockaddr_in6 *)our_sockaddr)->sin6_port = 0;
585
586 if (bind(sockfd, SA(our_sockaddr), SA_LEN(our_sockaddr)) < 0) {
587 close(sockfd);
588 return -1;
589 }
590 return sockfd;
591}
592
593static const rc_sockets_override default_socket_funcs = {
594 .get_fd = plain_get_fd,
595 .close_fd = plain_close_fd,
596 .sendto = plain_sendto,
597 .recvfrom = plain_recvfrom
598};
599
600static const rc_sockets_override default_tcp_socket_funcs = {
601 .get_fd = plain_tcp_get_fd,
602 .close_fd = plain_close_fd,
603 .sendto = plain_tcp_sendto,
604 .recvfrom = plain_recvfrom
605};
606
607/*- Parse ip (IPv4 or IPv6 text) into ss.
608 *
609 * @param ss set to the parsed address on success.
610 * @param ip the address text to parse.
611 * @return 0 on success, -1 if ip is neither a valid IPv4 nor IPv6 address.
612 -*/
613static int set_addr(struct sockaddr_storage *ss, const char *ip)
614{
615 memset(ss, 0, sizeof(*ss));
616 if (inet_pton(AF_INET, ip, &((struct sockaddr_in *)ss)->sin_addr) == 1) {
617 ss->ss_family = AF_INET;
618 } else if (inet_pton(AF_INET6, ip, &((struct sockaddr_in6 *)ss)->sin6_addr) == 1) {
619 ss->ss_family = AF_INET6;
620 } else {
621 rc_log(LOG_CRIT, "invalid IP address for nas-ip: %s", ip);
622 return -1;
623 }
624 return 0;
625}
626
627/* Fills in an authserver/acctserver SERVER's secret[0] from the "secret"
628 * option, but only when that entry has no secret of its own yet -- an
629 * inline host:port:secret (or a legacy "servers" file entry, resolved
630 * later at send time) always takes priority. This is the config-file
631 * counterpart of radcli2.h's radcli_ctx_set_secret(), which instead
632 * overwrites secret[0] unconditionally on an explicit call.
633 *
634 * Harmless but pointless under TLS/DTLS: radcli2_priv_find_server_addr()
635 * doesn't need secret[0] there in the first place (rh->so_type ==
636 * RC_SOCKET_TLS/_DTLS branch), and radcli_transport_exchange()
637 * (lib/sendserver.c) overwrites it with the RFC 6614/7360 fixed secret
638 * before it would ever be used regardless. */
639/*- Fill in optname's first server entry's secret from the "secret"
640 * option, but only when that entry has no secret of its own yet.
641 *
642 * @param rh a handle to parsed configuration.
643 * @param optname "authserver" or "acctserver".
644 * @param secret the fallback secret to apply.
645 -*/
646static void apply_secret_fallback_one(rc_handle *rh, const char *optname, const char *secret)
647{
648 SERVER *serv = radcli2_priv_conf_srv(rh, optname);
649 char *dup;
650
651 if (serv == NULL || serv->max == 0)
652 return;
653 if (serv->secret[0] != NULL && serv->secret[0][0] != '\0')
654 return;
655
656 dup = strdup(secret);
657 if (dup == NULL)
658 return;
659
660 free(serv->secret[0]);
661 serv->secret[0] = dup;
662}
663
664/*- Apply the "secret" option as a fallback to authserver/acctserver's
665 * first entry, when they don't already carry their own secret.
666 *
667 * @param rh a handle to parsed configuration.
668 -*/
669static void apply_secret_fallback(rc_handle *rh)
670{
671 const char *secret = rc_conf_str_id(rh, OPT_SECRET);
672
673 if (secret == NULL || secret[0] == '\0')
674 return;
675
676 apply_secret_fallback_one(rh, "authserver", secret);
677 apply_secret_fallback_one(rh, "acctserver", secret);
678}
679
680/*- Materialize optname's default into rh's config table if it was never
681 * explicitly set, so every internal reader can use the default-free,
682 * compile-time-indexed rc_conf_int_id() uniformly afterward (REQ-GEN-STYLE-011)
683 * instead of a runtime string-keyed lookup with an inline default.
684 *
685 * @param rh a handle to parsed configuration.
686 * @param optname the option's name.
687 * @param def the default to materialize if unset.
688 -*/
689static void apply_int_default(rc_handle *rh, char const *optname, int def)
690{
691 OPTION *option = find_option(rh, optname, RADCLI_OPT_TYPE_INT);
692 int *val;
693
694 if (option == NULL || option->val != NULL)
695 return;
696
697 val = malloc(sizeof(*val));
698 if (val == NULL)
699 return;
700
701 *val = def;
702 option->val = val;
703}
704
705/*- Validate the options required for a working request/response cycle or
706 * DAE listener: either an authserver or a configured DAE listener, and
707 * sane radius_timeout/radius_retries. Shared by radcli2_priv_test_config()
708 * (the config-file path, which additionally knows filename for its log
709 * messages) and radcli2_priv_apply_config() itself, so radcli_ctx_apply()
710 * (lib/config2.c) enforces the exact same requirements as
711 * radcli_ctx_read_config() -- per REQ-CONFIG2-CFG-004, the two are meant to
712 * differ only in where the options come from (a file vs.
713 * radcli_ctx_set_opt_*() calls), not in what they require.
714 *
715 * @param rh a handle to parsed configuration.
716 * @param source a label for this handle's origin, used only in log
717 * messages (e.g. the config filename, or the calling function's name).
718 * @return 0 on success, -1 on failure.
719 -*/
720static int radcli2_priv_check_config(rc_handle *rh, char const *source)
721{
722 SERVER *srv;
723
724 /* An RFC 5176 DAE listener (radcli_dae_new(), lib/dae.c) is a
725 * server-side role, independent of ever sending an Access-/
726 * Accounting-Request as a client -- a NAS handling only CoA/
727 * Disconnect need not configure an authserver at all. dae-accept
728 * being set at all (even to "no", or an invalid value later rejected
729 * by radcli_dae_new() itself) is enough to signal that intent here;
730 * this function only gates the cheap, universal requirement, not
731 * dae-accept's own grammar. */
732 srv = radcli2_priv_conf_srv(rh, "authserver");
733 if (!srv || !srv->max)
734 {
735 if (rc_conf_str_id(rh, OPT_DAE_ACCEPT) == NULL)
736 {
737 rc_log(LOG_ERR,"%s: no authserver or DAE listener specified", source);
738 return -1;
739 }
740 /* No authserver: a DAE-only context, per the check above. There is
741 * no client role here at all, so "no acctserver specified" (a
742 * client-only concern) would be noise -- skip that check entirely
743 * rather than logging it for a context that was never going to
744 * have one. */
745 }
746 else
747 {
748 srv = radcli2_priv_conf_srv(rh, "acctserver");
749 if (!srv || !srv->max)
750 {
751 /* it is allowed not to have acct servers under TLS/DTLS. rh->so_type
752 * isn't set until radcli2_priv_apply_config() runs, so check the
753 * configured serv-type string directly rather than the not-yet-
754 * initialized transport state. */
755 const char *stype = rc_conf_str_id(rh, OPT_SERV_TYPE);
756 if (stype == NULL)
757 stype = rc_conf_str_id(rh, OPT_SERV_AUTH_TYPE);
758 if (stype == NULL ||
759 (strcasecmp(stype, "tls") != 0 && strcasecmp(stype, "dtls") != 0))
760 rc_log(LOG_DEBUG,"%s: no acctserver specified", source);
761 }
762 }
763
764 if (rc_conf_int_id(rh, OPT_RADIUS_TIMEOUT) <= 0)
765 {
766 rc_log(LOG_ERR,"%s: radius_timeout <= 0 is illegal", source);
767 return -1;
768 }
769 if (rc_conf_int_id(rh, OPT_RADIUS_RETRIES) < 0)
770 {
771 rc_log(LOG_ERR,"%s: radius_retries < 0 is illegal", source);
772 return -1;
773 }
774
775 return 0;
776}
777
778/*- Apply configuration and initialise the transport.
779 *
780 * Must be called after all radcli2_priv_add_config() calls when using
781 * programmatic configuration (i.e., without a config file). Validates the
782 * same mandatory options radcli2_priv_test_config() does (REQ-CONFIG2-CFG-004),
783 * then initialises the transport selected by the serv-type option, including
784 * the TLS/DTLS handshake for TLS and DTLS transports. radcli2_priv_read_config()
785 * calls this internally; do not call it again after radcli2_priv_read_config().
786 *
787 * @param rh a handle to parsed configuration.
788 * @return 0 on success, -1 on failure.
789 -*/
790int radcli2_priv_apply_config(rc_handle *rh)
791{
792 const char *txt;
793 int ret;
794
795 if (radcli2_priv_check_config(rh, "radcli2_priv_apply_config") == -1)
796 return -1;
797
798 apply_secret_fallback(rh);
799 apply_int_default(rh, "watchdog-interval", 15);
800 apply_int_default(rh, "dae-max-clock-skew", 300);
801
802 memset(&rh->own_bind_addr, 0, sizeof(rh->own_bind_addr));
803 rh->own_bind_addr_set = 0;
804 rc_own_bind_addr(rh, &rh->own_bind_addr);
805 rh->own_bind_addr_set = 1;
806
807 txt = rc_conf_str_id(rh, OPT_NAS_IP);
808 if (txt != NULL) {
809 if (set_addr(&rh->nas_addr, txt) < 0)
810 return -1;
811 rh->nas_addr_set = 1;
812 }
813
814 txt = rc_conf_str_id(rh, OPT_SERV_TYPE);
815 if (txt == NULL)
816 txt = rc_conf_str_id(rh, OPT_SERV_AUTH_TYPE);
817
818 if (txt == NULL)
819 txt = "udp";
820
821 if (strcasecmp(txt, "udp") == 0) {
822 memset(&rh->so, 0, sizeof(rh->so));
823 rh->so_type = RC_SOCKET_UDP;
824 memcpy(&rh->so, &default_socket_funcs, sizeof(rh->so));
825 ret = 0;
826 } else if (strcasecmp(txt, "tcp") == 0) {
827 memset(&rh->so, 0, sizeof(rh->so));
828 rh->so_type = RC_SOCKET_TCP;
829 memcpy(&rh->so, &default_tcp_socket_funcs, sizeof(rh->so));
830 ret = 0;
831#ifdef HAVE_GNUTLS
832 } else if (strcasecmp(txt, "dtls") == 0) {
833 ret = rc_init_tls(rh, SEC_FLAG_DTLS);
834 } else if (strcasecmp(txt, "tls") == 0) {
835 ret = rc_init_tls(rh, 0);
836#endif
837 } else {
838 rc_log(LOG_CRIT, "unknown server type: %s", txt);
839 return -1;
840 }
841
842 if (ret < 0) {
843 rc_log(LOG_CRIT, "error initializing %s", txt);
844 return -1;
845 }
846
847 return 0;
848
849}
850
851/*- Load the built-in RFC 2865/2866/2869 dictionary into rh. Shared by
852 * radcli2_priv_read_config() and radcli_ctx_new() (lib/config2.c) so both
853 * entry points agree on what "the built-in dictionary" is.
854 *
855 * @param rh a handle allocated by radcli2_priv_new().
856 * @return 0 on success, -1 on failure (rh is left as-is; the caller is
857 * responsible for destroying it).
858 -*/
859int radcli2_priv_load_builtin_dict(rc_handle *rh)
860{
861 if (radcli2_priv_read_dictionary_from_buffer(rh, rc_rfc_dictionary,
862 sizeof(rc_rfc_dictionary) - 1) != 0) {
863 rc_log(LOG_CRIT, "radcli2_priv_load_builtin_dict: failed to load built-in RFC dictionary");
864 return -1;
865 }
866 return 0;
867}
868
869/*- Read the global config file. The full recognised-option reference lives
870 * on the public rc_read_config()'s doc comment (lib/legacy/compat.c),
871 * which this implements.
872 *
873 * @param filename path to the configuration file.
874 * @param skip_builtin_dict nonzero to skip loading the built-in RFC
875 * 2865/2866/2869 dictionary (radcli_ctx_read_config()'s
876 * #RADCLI_CTX_NO_BUILTIN_DICT, translated by lib/config2.c so this
877 * legacy-shared function need not know about radcli2.h's flags enum).
878 * @return new rc_handle on success, NULL on failure.
879 -*/
880rc_handle *radcli2_priv_read_config(char const *filename, int skip_builtin_dict)
881{
882 FILE *configfd;
883 char *buffer = NULL, *p;
884 size_t bufsize = 0;
885 ssize_t nread;
886 OPTION *option;
887 int line;
888 size_t pos;
889 rc_handle *rh;
890
891
892 rh = radcli2_priv_new();
893 if (rh == NULL)
894 return NULL;
895
896 rh->config_options = malloc(sizeof(config_options_default));
897 if (rh->config_options == NULL) {
898 rc_log(LOG_CRIT, "radcli2_priv_read_config: out of memory");
899 radcli2_priv_destroy(rh);
900 return NULL;
901 }
902 memcpy(rh->config_options, &config_options_default, sizeof(config_options_default));
903
904 if ((configfd = fopen(filename,"r")) == NULL)
905 {
906 rc_log(LOG_ERR,"radcli2_priv_read_config: can't open %s: %s", filename, strerror(errno));
907 radcli2_priv_destroy(rh);
908 return NULL;
909 }
910
911 line = 0;
912 while ((nread = getline(&buffer, &bufsize, configfd)) != -1)
913 {
914 line++;
915
916 if (nread > 0 && buffer[nread-1] == '\n')
917 buffer[--nread] = '\0';
918
919 p = buffer;
920
921 if ((*p == '#') || (*p == '\0'))
922 continue;
923
924 if ((pos = strcspn(p, "\t ")) == 0) {
925 rc_log(LOG_ERR, "%s: line %d: bogus format: %s", filename, line, p);
926 goto error;
927 }
928
929 p[pos] = '\0';
930
931 if ((option = find_option(rh, p, OT_ANY)) == NULL) {
932 if (rc_ignored_option(p)) {
933 rc_log(LOG_INFO, "%s: line %d: option '%s' is no longer used, ignoring", filename, line, p);
934 continue;
935 }
936 rc_log(LOG_ERR, "%s: line %d: unrecognized keyword: %s", filename, line, p);
937 goto error;
938 }
939
940 if (option->status != ST_UNDEF) {
941 rc_log(LOG_ERR, "%s: line %d: duplicate option line: %s", filename, line, p);
942 goto error;
943 }
944
945 p += pos+1;
946 while (isspace(*p))
947 p++;
948 if (*p != '\0') {
949 pos = strlen(p) - 1;
950 while (pos != 0 && isspace(p[pos]))
951 pos--;
952 p[pos + 1] = '\0';
953 }
954
955 switch (option->type) {
956 case RADCLI_OPT_TYPE_STR:
957 if (set_option_str(filename, line, option, p) < 0)
958 goto error;
959 break;
960 case RADCLI_OPT_TYPE_INT:
961 if (set_option_int(filename, line, option, p) < 0)
962 goto error;
963 break;
964 case RADCLI_OPT_TYPE_SRV:
965 if (set_option_srv(rh, filename, line, option, p) < 0)
966 goto error;
967 break;
968 default:
969 rc_log(LOG_CRIT, "radcli2_priv_read_config: impossible case branch!");
970 abort();
971 }
972 }
973 free(buffer);
974 fclose(configfd);
975
976 if (radcli2_priv_test_config(rh, filename) == -1) {
977 radcli2_priv_destroy(rh);
978 return NULL;
979 }
980
981 {
982 int clientdebug = rc_conf_int_2(rh, "clientdebug", FALSE);
983 if(clientdebug > 0) {
984 rh->debug = clientdebug;
985 }
986 }
987
988 /* Load the built-in RFC 2865/2866/2869 dictionary first so that
989 * applications need not ship a dictionary file for standard attributes,
990 * unless the caller asked to skip it. */
991 if (!skip_builtin_dict && radcli2_priv_load_builtin_dict(rh) != 0) {
992 radcli2_priv_destroy(rh);
993 return NULL;
994 }
995
996 p = rc_conf_str_id(rh, OPT_DICTIONARY);
997 if (p != NULL) {
998 if (radcli2_priv_read_dictionary(rh, p) != 0) {
999 rc_log(LOG_CRIT, "could not load dictionary");
1000 radcli2_priv_destroy(rh);
1001 return NULL;
1002 }
1003 }
1004
1005 return rh;
1006
1007error:
1008 free(buffer);
1009 fclose(configfd);
1010 radcli2_priv_destroy(rh);
1011 return NULL;
1012}
1013
1014/*- Get the value of a string-typed config option.
1015 *
1016 * @param rh a handle to parsed configuration.
1017 * @param optname the name of an option.
1018 * @return config option value.
1019 -*/
1020char *radcli2_priv_conf_str(rc_handle const *rh, char const *optname)
1021{
1022 OPTION *option;
1023
1024 option = find_option(rh, optname, RADCLI_OPT_TYPE_STR);
1025
1026 if (option != NULL) {
1027 return (char *)option->val;
1028 } else {
1029 rc_log(LOG_CRIT, "radcli2_priv_conf_str: unknown config option requested: %s", optname);
1030 return NULL;
1031 }
1032}
1033
1034/*- Get the value of an integer-typed config option, optionally logging
1035 * when it was never set.
1036 *
1037 * @param rh a handle to parsed configuration.
1038 * @param optname the name of an option.
1039 * @param complain nonzero to log an error when optname was not set.
1040 * @return config option value, or 0 if not found, not an integer, or unset.
1041 -*/
1042static int rc_conf_int_2(rc_handle const *rh, char const *optname, int complain)
1043{
1044 OPTION *option;
1045
1046 option = find_option(rh, optname, RADCLI_OPT_TYPE_INT);
1047
1048 if (option != NULL) {
1049 if (option->val) {
1050 return *((int *)option->val);
1051 } else if(complain) {
1052 rc_log(LOG_ERR, "radcli2_priv_conf_int: config option %s was not set", optname);
1053 }
1054 return 0;
1055 } else {
1056 rc_log(LOG_CRIT, "radcli2_priv_conf_int: unknown config option requested: %s", optname);
1057 return 0;
1058 }
1059}
1060
1061/*- Get the value of an integer-typed config option.
1062 *
1063 * @param rh a handle to parsed configuration.
1064 * @param optname the name of an option.
1065 * @return config option value, or 0 if not found or not an integer.
1066 -*/
1067int radcli2_priv_conf_int(rc_handle const *rh, char const *optname)
1068{
1069 return rc_conf_int_2(rh, optname, TRUE);
1070}
1071
1072/*- Get the value of a server-list-typed config option.
1073 *
1074 * @param rh a handle to parsed configuration.
1075 * @param optname the name of an option.
1076 * @return config option value.
1077 -*/
1078SERVER *radcli2_priv_conf_srv(rc_handle const *rh, char const *optname)
1079{
1080 OPTION *option;
1081
1082 option = find_option(rh, optname, RADCLI_OPT_TYPE_SRV);
1083
1084 if (option != NULL) {
1085 return (SERVER *)option->val;
1086 } else {
1087 rc_log(LOG_CRIT, "radcli2_priv_conf_srv: unknown config option requested: %s", optname);
1088 return NULL;
1089 }
1090}
1091
1092/*- Test the configuration the user supplied.
1093 *
1094 * @param rh a handle to parsed configuration.
1095 * @param filename a name of a configuration file.
1096 * @return 0 on success, -1 when failure.
1097 -*/
1098int radcli2_priv_test_config(rc_handle *rh, char const *filename)
1099{
1100 if (radcli2_priv_check_config(rh, filename) == -1)
1101 return -1;
1102
1103 if (radcli2_priv_apply_config(rh) == -1) {
1104 return -1;
1105 }
1106
1107 return 0;
1108}
1109
1110/*- Report whether any address in addr matches any address in hostname.
1111 *
1112 * @param addr a struct addrinfo chain.
1113 * @param hostname a struct addrinfo chain to compare against.
1114 * @return 0 if a match is found, -1 otherwise.
1115 -*/
1116static int find_match (const struct addrinfo* addr, const struct addrinfo *hostname)
1117{
1118 const struct addrinfo *ptr, *ptr2;
1119 unsigned len1, len2;
1120
1121 ptr = addr;
1122 while(ptr) {
1123 ptr2 = hostname;
1124 while(ptr2) {
1125 len1 = SA_GET_INLEN(ptr->ai_addr);
1126 len2 = SA_GET_INLEN(ptr2->ai_addr);
1127
1128 if (len1 > 0 &&
1129 len1 == len2 &&
1130 memcmp(SA_GET_INADDR(ptr->ai_addr), SA_GET_INADDR(ptr2->ai_addr), len1) == 0) {
1131 return 0;
1132 }
1133 ptr2 = ptr2->ai_next;
1134 }
1135 ptr = ptr->ai_next;
1136 }
1137 return -1;
1138}
1139
1140/*- Report whether addr is a local address, by attempting to bind it.
1141 *
1142 * @param addr an AF_INET or AF_INET6 address.
1143 * @return 0 if local, 1 if not local, -1 on failure.
1144 -*/
1145static int rc_ipaddr_local(const struct sockaddr *addr)
1146{
1147 int temp_sock, res, serrno;
1148 struct sockaddr_storage tmpaddr;
1149
1150 memcpy(&tmpaddr, addr, SA_LEN(addr));
1151
1152 temp_sock = socket(addr->sa_family, SOCK_DGRAM, 0);
1153 if (temp_sock == -1)
1154 return -1;
1155
1156 if (addr->sa_family == AF_INET) {
1157 ((struct sockaddr_in*)&tmpaddr)->sin_port = 0;
1158 } else {
1159 ((struct sockaddr_in6*)&tmpaddr)->sin6_port = 0;
1160 }
1161 res = bind(temp_sock, SA(&tmpaddr), SS_LEN(&tmpaddr));
1162 serrno = errno;
1163 close(temp_sock);
1164 if (res == 0)
1165 return 0;
1166 if (serrno == EADDRNOTAVAIL)
1167 return 1;
1168 return -1;
1169}
1170
1171/*- Report whether info refers to one of our own local addresses.
1172 *
1173 * @param info a struct addrinfo chain of the host to check.
1174 * @return 0 if yes, 1 if no, -1 on failure.
1175 -*/
1176static int rc_is_myname(const struct addrinfo *info)
1177{
1178 const struct addrinfo *p;
1179 int res;
1180
1181 p = info;
1182 while(p != NULL) {
1183 res = rc_ipaddr_local(p->ai_addr);
1184 if (res == 0 || res == -1) {
1185 return res;
1186 }
1187 p = p->ai_next;
1188 }
1189 return 1;
1190}
1191
1192/*- Locate a server in the rh config or, if not found, check for a
1193 * servers file.
1194 *
1195 * @param rh a handle to parsed configuration.
1196 * @param server_name the name of the server.
1197 * @param info set to a pointer to the resolved addrinfo.
1198 * @param secret set to the server's secret (buffer of MAX_SECRET_LENGTH).
1199 * @param type AUTH or ACCT.
1200 * @return 0 on success, -1 on failure.
1201 -*/
1202int radcli2_priv_find_server_addr (rc_handle const *rh, char const *server_name,
1203 struct addrinfo** info, char *secret, rc_type type)
1204{
1205 int result = 0;
1206 FILE *clientfd;
1207 char *h;
1208 char *s;
1209 char *buffer = NULL;
1210 size_t bufsize = 0;
1211 char hostnm[AUTH_ID_LEN + 1];
1212 char *buffer_save;
1213 char *hostnm_save;
1214 SERVER *servers;
1215 struct addrinfo *tmpinfo = NULL;
1216 const char *fservers;
1217 char const *optname;
1218
1219 /* Lookup the IP address of the radius server */
1220 if ((*info = rc_getaddrinfo (server_name, type==AUTH?PW_AI_AUTH:PW_AI_ACCT)) == NULL)
1221 return -1;
1222
1223 switch (type)
1224 {
1225 case AUTH: optname = "authserver"; break;
1226 case ACCT: optname = "acctserver"; break;
1227 default: optname = NULL;
1228 }
1229
1230 if ( (optname != NULL) &&
1231 ((servers = radcli2_priv_conf_srv(rh, optname)) != NULL) )
1232 {
1233 /* Check to see if the server secret is defined in the rh config */
1234 unsigned servernum;
1235 for (servernum = 0; servernum < servers->max; servernum++)
1236 {
1237 if( (strcmp(server_name, servers->name[servernum]) == 0) &&
1238 (servers->secret[servernum] != NULL) )
1239 {
1240 memset(secret, '\0', MAX_SECRET_LENGTH);
1241 strlcpy(secret, servers->secret[servernum], MAX_SECRET_LENGTH);
1242 return 0;
1243 }
1244 }
1245 }
1246
1247 /* We didn't find it in the rh_config or the servername is too long so look for a
1248 * servers file to define the secret(s)
1249 */
1250
1251 fservers = rc_conf_str_id(rh, OPT_SERVERS);
1252 if (fservers != NULL) {
1253 if ((clientfd = fopen (fservers, "r")) == NULL)
1254 {
1255 rc_log(LOG_ERR, "rc_find_server: couldn't open file: %s: %s", strerror(errno), fservers);
1256 goto fail;
1257 }
1258
1259 while (getline (&buffer, &bufsize, clientfd) != -1)
1260 {
1261 if (*buffer == '#')
1262 continue;
1263
1264 if ((h = strtok_r(buffer, " \t\n", &buffer_save)) == NULL) /* first hostname */
1265 continue;
1266
1267 strlcpy (hostnm, h, AUTH_ID_LEN);
1268
1269 if ((s = strtok_r (NULL, " \t\n", &buffer_save)) == NULL) /* and secret field */
1270 continue;
1271
1272 strlcpy (secret, s, MAX_SECRET_LENGTH);
1273
1274 if (!strchr (hostnm, '/')) /* If single name form */
1275 {
1276 tmpinfo = rc_getaddrinfo(hostnm, 0);
1277 if (tmpinfo)
1278 {
1279 result = find_match (*info, tmpinfo);
1280 if (result == 0)
1281 {
1282 result++;
1283 break;
1284 }
1285
1286 freeaddrinfo(tmpinfo);
1287 tmpinfo = NULL;
1288 }
1289 }
1290 else /* <name1>/<name2> "paired" form */
1291 {
1292 strtok_r(hostnm, "/", &hostnm_save);
1293 tmpinfo = rc_getaddrinfo(hostnm, 0);
1294 if (tmpinfo)
1295 {
1296 if (rc_is_myname(tmpinfo) == 0)
1297 { /* If we're the 1st name, target is 2nd */
1298 if (find_match (*info, tmpinfo) == 0)
1299 {
1300 result++;
1301 break;
1302 }
1303 }
1304 else /* If we were 2nd name, target is 1st name */
1305 {
1306 if (find_match (*info, tmpinfo) == 0)
1307 {
1308 result++;
1309 break;
1310 }
1311 }
1312 freeaddrinfo(tmpinfo);
1313 tmpinfo = NULL;
1314 }
1315 }
1316 }
1317 fclose (clientfd);
1318 }
1319 if (result == 0)
1320 {
1321 /* Under TLS/DTLS, this function's whole secret-lookup half (the
1322 * server's :secret suffix, or a legacy "servers" file) is moot:
1323 * radcli_transport_exchange() (lib/sendserver.c) unconditionally
1324 * overwrites whatever secret is returned here with the RFC 6614/
1325 * 7360 fixed string (rh->so.static_secret) immediately after
1326 * calling this function, since the shared secret for that
1327 * transport is a protocol constant, not something an operator
1328 * configures per server. Requiring a real secret to be found
1329 * here anyway made every ordinary request over a TLS/DTLS
1330 * authserver configured the normal way (no inline :secret, since
1331 * none is needed) fail outright before ever reaching that
1332 * override -- the address above was already resolved
1333 * successfully, which is all this transport actually needs from
1334 * this function. */
1335 if (rh->so_type == RC_SOCKET_TLS || rh->so_type == RC_SOCKET_DTLS) {
1336 memset(secret, '\0', MAX_SECRET_LENGTH);
1337 result = 0;
1338 goto cleanup;
1339 }
1340 memset (secret, '\0', MAX_SECRET_LENGTH);
1341 rc_log(LOG_ERR, "rc_find_server: couldn't find RADIUS server %s in %s",
1342 server_name, rc_conf_str_id(rh, OPT_SERVERS));
1343 goto fail;
1344 }
1345
1346 result = 0;
1347 goto cleanup;
1348
1349 fail:
1350 freeaddrinfo(*info);
1351 result = -1;
1352
1353 cleanup:
1354 if (tmpinfo)
1355 freeaddrinfo(tmpinfo);
1356 free(buffer);
1357
1358 return result;
1359}
1360
1361/*- Free allocated config values. For legacy compatibility reasons this
1362 * will not release any dictionary entries -- use radcli2_priv_destroy()
1363 * to release all memory from the handle.
1364 *
1365 * @param rh a handle to parsed configuration.
1366 -*/
1367void radcli2_priv_config_free(rc_handle *rh)
1368{
1369 int i;
1370 SERVER *serv;
1371
1372 if (rh->config_options == NULL)
1373 return;
1374
1375 for (i = 0; i < NUM_OPTIONS; i++) {
1376 if (rh->config_options[i].val == NULL)
1377 continue;
1378 if (rh->config_options[i].type == RADCLI_OPT_TYPE_SRV) {
1379 serv = (SERVER *)rh->config_options[i].val;
1380 server_free_entries(serv, 0, serv->max);
1381 free(serv);
1382 } else {
1383 free(rh->config_options[i].val);
1384 }
1385 }
1386 free(rh->config_options);
1387 free(rh->first_dict_read);
1388 rh->config_options = NULL;
1389 rh->first_dict_read = NULL;
1390}
1391
1392static int _initialized = 0;
1393
1394/*- Initialise a new Radius client handle.
1395 *
1396 * @return a new rc_handle (free with radcli2_priv_destroy()), or NULL on
1397 * allocation failure.
1398 -*/
1399rc_handle *radcli2_priv_new(void)
1400{
1401 rc_handle *rh;
1402
1403 if (_initialized == 0) {
1404#if defined(HAVE_GNUTLS) && GNUTLS_VERSION_NUMBER < 0x030300
1405 int ret;
1406 ret = gnutls_global_init();
1407 if (ret < 0) {
1408 rc_log(LOG_ERR,
1409 "%s: error initializing gnutls: %s",
1410 __func__, gnutls_strerror(ret));
1411 return NULL;
1412 }
1413#endif
1414 }
1415 _initialized++;
1416
1417 rh = calloc(1, sizeof(*rh));
1418 if (rh == NULL) {
1419 rc_log(LOG_CRIT, "radcli2_priv_new: out of memory");
1420 return NULL;
1421 }
1422 rh->req_fd = -1; /* REQ-NET2-SEND-016: 0 (calloc's default) is a valid
1423 * fd (stdin) -- must not be mistaken for "unset". */
1424 pthread_mutex_init(&rh->reqreg_init_lock, NULL);
1425 return rh;
1426}
1427
1428/*- Destroy a Radius client handle, reclaiming all memory.
1429 *
1430 * @param rh the handle to free.
1431 -*/
1432void radcli2_priv_destroy(rc_handle *rh)
1433{
1434 radcli2_priv_dict_free(rh);
1435#ifdef HAVE_GNUTLS
1436 rc_deinit_tls(rh);
1437#endif
1438 radcli2_priv_config_free(rh);
1439 /* REQ-NET2-SEND-016: ctx's persistent request socket/registry, never
1440 * touched by radcli2_priv_config_free() (which only frees config
1441 * state) -- closed/freed only here, at ctx's own end of life. */
1442 if (rh->req_fd != -1 && rh->so.close_fd)
1443 rh->so.close_fd(rh->req_fd);
1444 if (rh->reqreg != NULL) {
1445 pthread_mutex_destroy(&rh->reqreg->lock);
1446 free(rh->reqreg);
1447 }
1448 pthread_mutex_destroy(&rh->reqreg_init_lock);
1449 free(rh->tls_psk_identity);
1450 free(rh->tls_psk_key);
1451 free(rh);
1452
1453#if defined(HAVE_GNUTLS) && GNUTLS_VERSION_NUMBER < 0x030300
1454 _initialized--;
1455 if (_initialized == 0) {
1456 gnutls_global_deinit();
1457 }
1458#endif
1459}
1460
1461 /*
1462 * Local Variables:
1463 * c-basic-offset:8
1464 * c-style: whitesmith
1465 * End:
1466 */
rc_type
Definition radcli.h:81
@ ACCT
Request for accounting server.
Definition radcli.h:83
@ AUTH
Request for authentication server.
Definition radcli.h:82
@ RC_SOCKET_UDP
Plain UDP socket.
Definition radcli.h:113
@ RC_SOCKET_TCP
Plain TCP socket.
Definition radcli.h:116
@ RC_SOCKET_DTLS
DTLS socket.
Definition radcli.h:115
@ RC_SOCKET_TLS
TLS socket.
Definition radcli.h:114