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
15 /**
16 * @defgroup radcli-api Main API
17 * @brief Main API Functions
18 *
19 * @{
20 */
21
22 #include <config.h>
23 #include <includes.h>
24 #include <radcli/radcli.h>
25 #include <options.h>
26 #include "util.h"
27 #include "tls.h"
28
29 #ifndef TRUE
30 #define TRUE 1
31 #define FALSE 0
32 #endif
33
34 static int rc_conf_int_2(rc_handle const *rh, char const *optname, int complain);
35
36 /** Find an option in the option list
37 *
38 * @param rh a handle to parsed configuration.
39 * @param optname the name of the option.
40 * @param type the option type.
41 * @return pointer to option on success, NULL otherwise.
42 */
find_option(rc_handle const * rh,char const * optname,unsigned int type)43 static OPTION *find_option(rc_handle const *rh, char const *optname, unsigned int type)
44 {
45 int i;
46
47 /* there're so few options that a binary search seems not necessary */
48 for (i = 0; i < NUM_OPTIONS; i++) {
49 if (!strcmp(rh->config_options[i].name, optname) &&
50 (rh->config_options[i].type & type))
51 {
52 return &rh->config_options[i];
53 }
54 }
55
56 return NULL;
57 }
58
59 /** Set a specific option doing type conversions
60 *
61 * @param filename the name of the config file (for logging purposes).
62 * @param line the line number in the file.
63 * @param option option to set.
64 * @param p Value.
65 * @return 0 on success, -1 on failure.
66 */
set_option_str(char const * filename,int line,OPTION * option,char const * p)67 static int set_option_str(char const *filename, int line, OPTION *option, char const *p)
68 {
69 if (p) {
70 option->val = (void *) strdup(p);
71 if (option->val == NULL) {
72 rc_log(LOG_CRIT, "read_config: out of memory");
73 return -1;
74 }
75 } else {
76 option->val = NULL;
77 }
78
79 return 0;
80 }
81
set_option_int(char const * filename,int line,OPTION * option,char const * p)82 static int set_option_int(char const *filename, int line, OPTION *option, char const *p)
83 {
84 int *iptr;
85
86 if (p == NULL) {
87 rc_log(LOG_ERR, "%s: line %d: bogus option value", filename, line);
88 return -1;
89 }
90
91 if ((iptr = malloc(sizeof(*iptr))) == NULL) {
92 rc_log(LOG_CRIT, "read_config: out of memory");
93 return -1;
94 }
95
96 *iptr = atoi(p);
97 option->val = (void *) iptr;
98
99 return 0;
100 }
101
set_option_srv(char const * filename,int line,OPTION * option,char const * p)102 static int set_option_srv(char const *filename, int line, OPTION *option, char const *p)
103 {
104 SERVER *serv;
105 char *p_pointer;
106 char *p_dupe;
107 char *p_save;
108 char *q;
109 char *s;
110 struct servent *svp;
111
112 p_dupe = strdup(p);
113
114 if (p_dupe == NULL) {
115 rc_log(LOG_ERR, "%s: line %d: Invalid option or memory failure", filename, line);
116 return -1;
117 }
118
119 serv = (SERVER *) option->val;
120 if (serv == NULL) {
121 serv = calloc(1, sizeof(*serv));
122 if (serv == NULL) {
123 rc_log(LOG_CRIT, "read_config: out of memory");
124 free(p_dupe);
125 return -1;
126 }
127 serv->max = 0;
128 }
129
130 p_pointer = strtok_r(p_dupe, ", \t", &p_save);
131
132 while(p_pointer != NULL) {
133 if (serv->max > SERVER_MAX) {
134 DEBUG(LOG_ERR, "cannot set more than %d servers", SERVER_MAX);
135 goto fail;
136 }
137
138 DEBUG(LOG_ERR, "processing server: %s", p_pointer);
139 /* check to see for '[IPv6]:port' syntax */
140 if ((q = strchr(p_pointer,'[')) != NULL) {
141 *q = '\0';
142 q++;
143 p_pointer = q;
144
145 q = strchr(p_pointer, ']');
146 if (q == NULL) {
147 rc_log(LOG_CRIT, "read_config: IPv6 parse error");
148 goto fail;
149 }
150 *q = '\0';
151 q++;
152
153 if (q[0] == ':') {
154 q++;
155 }
156
157 /* Check to see if we have '[IPv6]:port:secret' syntax */
158 if((s=strchr(q, ':')) != NULL) {
159 *s = '\0';
160 s++;
161 serv->secret[serv->max] = strdup(s);
162 if (serv->secret[serv->max] == NULL) {
163 rc_log(LOG_CRIT, "read_config: out of memory");
164 goto fail;
165 }
166 }
167
168 } else /* Check to see if we have 'servername:port' syntax */
169 if ((q = strchr(p_pointer,':')) != NULL) {
170 *q = '\0';
171 q++;
172
173 /* Check to see if we have 'servername:port:secret' syntax */
174 if((s = strchr(q,':')) != NULL) {
175 *s = '\0';
176 s++;
177 serv->secret[serv->max] = strdup(s);
178 if (serv->secret[serv->max] == NULL) {
179 rc_log(LOG_CRIT, "read_config: out of memory");
180 goto fail;
181 }
182 }
183 }
184
185 if(q && strlen(q) > 0) {
186 serv->port[serv->max] = atoi(q);
187 } else {
188 if (!strcmp(option->name,"authserver"))
189 if ((svp = getservbyname ("radius", "udp")) == NULL)
190 serv->port[serv->max] = PW_AUTH_UDP_PORT;
191 else
192 serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
193 else if (!strcmp(option->name, "acctserver"))
194 if ((svp = getservbyname ("radacct", "udp")) == NULL)
195 serv->port[serv->max] = PW_ACCT_UDP_PORT;
196 else
197 serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
198 else {
199 rc_log(LOG_ERR, "%s: line %d: no default port for %s", filename, line, option->name);
200 goto fail;
201 }
202 }
203
204 serv->name[serv->max] = strdup(p_pointer);
205 if (serv->name[serv->max] == NULL) {
206 rc_log(LOG_CRIT, "read_config: out of memory");
207 goto fail;
208 }
209
210 serv->max++;
211 p_pointer = strtok_r(NULL, ", \t", &p_save);
212 }
213
214 free(p_dupe);
215 if (option->val == NULL)
216 option->val = (void *)serv;
217
218 return 0;
219 fail:
220 free(p_dupe);
221 if (option->val == NULL)
222 free(serv);
223 return -1;
224
225 }
226
set_option_auo(char const * filename,int line,OPTION * option,char const * p)227 static int set_option_auo(char const *filename, int line, OPTION *option, char const *p)
228 {
229 int *iptr;
230 char *p_dupe = NULL;
231 char *p_pointer = NULL;
232 char *p_save = NULL;
233
234 p_dupe = strdup(p);
235
236 if (p_dupe == NULL) {
237 rc_log(LOG_WARNING, "%s: line %d: bogus option value", filename, line);
238 return -1;
239 }
240
241 if ((iptr = malloc(sizeof(*iptr))) == NULL) {
242 rc_log(LOG_CRIT, "read_config: out of memory");
243 free(p_dupe);
244 return -1;
245 }
246
247 *iptr = 0;
248 p_pointer = strtok_r(p_dupe, ", \t", &p_save);
249
250 if (!strncmp(p_pointer, "local", 5))
251 *iptr = AUTH_LOCAL_FST;
252 else if (!strncmp(p_pointer, "radius", 6))
253 *iptr = AUTH_RADIUS_FST;
254 else {
255 rc_log(LOG_ERR,"%s: auth_order: unknown keyword: %s", filename, p);
256 free(iptr);
257 free(p_dupe);
258 return -1;
259 }
260
261 p_pointer = strtok_r(NULL, ", \t", &p_save);
262
263 if (p_pointer && (*p_pointer != '\0')) {
264 if ((*iptr & AUTH_RADIUS_FST) && !strcmp(p_pointer, "local"))
265 *iptr = (*iptr) | AUTH_LOCAL_SND;
266 else if ((*iptr & AUTH_LOCAL_FST) && !strcmp(p_pointer, "radius"))
267 *iptr = (*iptr) | AUTH_RADIUS_SND;
268 else {
269 rc_log(LOG_ERR,"%s: auth_order: unknown or unexpected keyword: %s", filename, p);
270 free(iptr);
271 free(p_dupe);
272 return -1;
273 }
274 }
275
276 option->val = (void *) iptr;
277
278 free(p_dupe);
279 return 0;
280 }
281
282 /** Allow a config option to be added to rc_handle from inside a program.
283 *
284 * That allows programs to setup a handle without loading a configuration
285 * file.
286 *
287 * @param rh a handle to parsed configuration.
288 * @param option_name the name of the option.
289 * @param option_val the value to be added.
290 * @param source typically should be __FILE__ or __func__ for logging purposes.
291 * @param line __LINE__ for logging purposes.
292 * @return 0 on success, -1 on failure.
293 */
rc_add_config(rc_handle * rh,char const * option_name,char const * option_val,char const * source,int line)294 int rc_add_config(rc_handle *rh, char const *option_name, char const *option_val, char const *source, int line)
295 {
296 OPTION *option;
297
298 if ((option = find_option(rh, option_name, OT_ANY)) == NULL)
299 {
300 rc_log(LOG_ERR, "ERROR: unrecognized option: %s", option_name);
301 return -1;
302 }
303
304 if (option->status != ST_UNDEF)
305 {
306 rc_log(LOG_ERR, "ERROR: duplicate option: %s", option_name);
307 return -1;
308 }
309
310 switch (option->type) {
311 case OT_STR:
312 if (set_option_str(source, line, option, option_val) < 0) {
313 return -1;
314 }
315 break;
316 case OT_INT:
317 if (set_option_int(source, line, option, option_val) < 0) {
318 return -1;
319 }
320 break;
321 case OT_SRV:
322 if (set_option_srv(source, line, option, option_val) < 0) {
323 return -1;
324 }
325 break;
326 case OT_AUO:
327 if (set_option_auo(source, line, option, option_val) < 0) {
328 return -1;
329 }
330 break;
331 default:
332 rc_log(LOG_CRIT, "rc_add_config: impossible case branch!");
333 abort();
334 }
335
336 return 0;
337 }
338
339 /** Initialise a configuration structure
340 *
341 * Initialize the configuration structure from an external program. For use when not
342 * running a standalone client that reads from a config file.
343 *
344 * The provided handled must have been allocated using rc_new().
345 *
346 * @param rh a handle to parsed configuration.
347 * @return rc_handle on success, NULL on failure.
348 */
rc_config_init(rc_handle * rh)349 rc_handle *rc_config_init(rc_handle *rh)
350 {
351 SERVER *authservers = NULL;
352 SERVER *acctservers;
353 OPTION *acct;
354 OPTION *auth;
355
356 rh->config_options = malloc(sizeof(config_options_default));
357 if (rh->config_options == NULL)
358 {
359 rc_log(LOG_CRIT, "rc_config_init: out of memory");
360 rc_destroy(rh);
361 return NULL;
362 }
363 memcpy(rh->config_options, &config_options_default, sizeof(config_options_default));
364
365 auth = find_option(rh, "authserver", OT_ANY);
366 if (auth) {
367 authservers = calloc(1, sizeof(SERVER));
368 if(authservers == NULL) {
369 rc_log(LOG_CRIT, "rc_config_init: error initializing server structs");
370 rc_destroy(rh);
371 return NULL;
372 }
373 auth->val = authservers;
374 }
375
376 acct = find_option(rh, "acctserver", OT_ANY);
377 if (acct) {
378 acctservers = calloc(1, sizeof(SERVER));
379 if(acctservers == NULL) {
380 rc_log(LOG_CRIT, "rc_config_init: error initializing server structs");
381 rc_destroy(rh);
382 if(authservers) free(authservers);
383 return NULL;
384 }
385 acct->val = acctservers;
386 }
387
388 return rh;
389 }
390
plain_sendto(void * ptr,int sockfd,const void * buf,size_t len,int flags,const struct sockaddr * dest_addr,socklen_t addrlen)391 static ssize_t plain_sendto(void *ptr, int sockfd,
392 const void *buf, size_t len, int flags,
393 const struct sockaddr *dest_addr, socklen_t addrlen)
394 {
395 return sendto(sockfd, buf, len, flags, dest_addr, addrlen);
396 }
397
plain_tcp_sendto(void * ptr,int sockfd,const void * buf,size_t len,int flags,const struct sockaddr * dest_addr,socklen_t addrlen)398 static ssize_t plain_tcp_sendto(void *ptr, int sockfd,
399 const void *buf, size_t len, int flags,
400 const struct sockaddr *dest_addr, socklen_t addrlen)
401 {
402 if((connect(sockfd, dest_addr, addrlen)) != 0){
403 rc_log(LOG_ERR, "%s: Connect Call Failed : %s", __FUNCTION__, strerror(errno));
404 return -1;
405 }
406 return sendto(sockfd, buf, len, flags, dest_addr, addrlen);
407 }
408
plain_recvfrom(void * ptr,int sockfd,void * buf,size_t len,int flags,struct sockaddr * src_addr,socklen_t * addrlen)409 static ssize_t plain_recvfrom(void *ptr, int sockfd,
410 void *buf, size_t len, int flags,
411 struct sockaddr *src_addr, socklen_t * addrlen)
412 {
413 return recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
414 }
415
plain_close_fd(int fd)416 static void plain_close_fd(int fd)
417 {
418 close(fd);
419 }
420
plain_get_fd(void * ptr,struct sockaddr * our_sockaddr)421 static int plain_get_fd(void *ptr, struct sockaddr *our_sockaddr)
422 {
423 int sockfd;
424
425 sockfd = socket(our_sockaddr->sa_family, SOCK_DGRAM, 0);
426 if (sockfd < 0) {
427 return -1;
428 }
429
430 if (our_sockaddr->sa_family == AF_INET)
431 ((struct sockaddr_in *)our_sockaddr)->sin_port = 0;
432 else
433 ((struct sockaddr_in6 *)our_sockaddr)->sin6_port = 0;
434
435 if (bind(sockfd, SA(our_sockaddr), SA_LEN(our_sockaddr)) < 0) {
436 close(sockfd);
437 return -1;
438 }
439 return sockfd;
440 }
441
plain_tcp_get_fd(void * ptr,struct sockaddr * our_sockaddr)442 static int plain_tcp_get_fd(void *ptr, struct sockaddr *our_sockaddr)
443 {
444 int sockfd;
445
446 sockfd = socket(our_sockaddr->sa_family, SOCK_STREAM, 0);
447 if (sockfd < 0) {
448 return -1;
449 }
450
451 if (our_sockaddr->sa_family == AF_INET)
452 ((struct sockaddr_in *)our_sockaddr)->sin_port = 0;
453 else
454 ((struct sockaddr_in6 *)our_sockaddr)->sin6_port = 0;
455
456 if (bind(sockfd, SA(our_sockaddr), SA_LEN(our_sockaddr)) < 0) {
457 close(sockfd);
458 return -1;
459 }
460 return sockfd;
461 }
462
463 static const rc_sockets_override default_socket_funcs = {
464 .get_fd = plain_get_fd,
465 .close_fd = plain_close_fd,
466 .sendto = plain_sendto,
467 .recvfrom = plain_recvfrom
468 };
469
470 static const rc_sockets_override default_tcp_socket_funcs = {
471 .get_fd = plain_tcp_get_fd,
472 .close_fd = plain_close_fd,
473 .sendto = plain_tcp_sendto,
474 .recvfrom = plain_recvfrom
475 };
476
set_addr(struct sockaddr_storage * ss,const char * ip)477 static int set_addr(struct sockaddr_storage *ss, const char *ip)
478 {
479 memset(ss, 0, sizeof(*ss));
480 if (inet_pton(AF_INET, ip, &((struct sockaddr_in *)ss)->sin_addr) == 1) {
481 ss->ss_family = AF_INET;
482 } else if (inet_pton(AF_INET6, ip, &((struct sockaddr_in6 *)ss)->sin6_addr) == 1) {
483 ss->ss_family = AF_INET6;
484 } else {
485 rc_log(LOG_CRIT, "invalid IP address for nas-ip: %s", ip);
486 return -1;
487 }
488 return 0;
489 }
490
491 /** Applies and initializes any parameters from the radcli configuration
492 *
493 * When no configuration file is provided and the configuration
494 * is provided via rc_add_config(), radcli requires the call of this function
495 * in order to initialize items for the connection.
496 *
497 * @param rh a handle to parsed configuration.
498 * @return 0 on success, -1 when failure.
499 */
rc_apply_config(rc_handle * rh)500 int rc_apply_config(rc_handle *rh)
501 {
502 const char *txt;
503 int ret;
504
505 memset(&rh->own_bind_addr, 0, sizeof(rh->own_bind_addr));
506 rh->own_bind_addr_set = 0;
507 rc_own_bind_addr(rh, &rh->own_bind_addr);
508 rh->own_bind_addr_set = 1;
509
510 txt = rc_conf_str(rh, "nas-ip");
511 if (txt != NULL) {
512 if (set_addr(&rh->nas_addr, txt) < 0)
513 return -1;
514 rh->nas_addr_set = 1;
515 }
516
517 txt = rc_conf_str(rh, "serv-type");
518 if (txt == NULL)
519 txt = rc_conf_str(rh, "serv-auth-type");
520
521 if (txt == NULL)
522 txt = "udp";
523
524 if (strcasecmp(txt, "udp") == 0) {
525 memset(&rh->so, 0, sizeof(rh->so));
526 rh->so_type = RC_SOCKET_UDP;
527 memcpy(&rh->so, &default_socket_funcs, sizeof(rh->so));
528 ret = 0;
529 } else if (strcasecmp(txt, "tcp") == 0) {
530 memset(&rh->so, 0, sizeof(rh->so));
531 rh->so_type = RC_SOCKET_TCP;
532 memcpy(&rh->so, &default_tcp_socket_funcs, sizeof(rh->so));
533 ret = 0;
534 #ifdef HAVE_GNUTLS
535 } else if (strcasecmp(txt, "dtls") == 0) {
536 ret = rc_init_tls(rh, SEC_FLAG_DTLS);
537 } else if (strcasecmp(txt, "tls") == 0) {
538 ret = rc_init_tls(rh, 0);
539 #endif
540 } else {
541 rc_log(LOG_CRIT, "unknown server type: %s", txt);
542 return -1;
543 }
544
545 if (ret < 0) {
546 rc_log(LOG_CRIT, "error initializing %s", txt);
547 return -1;
548 }
549
550 return 0;
551
552 }
553
554 /** Read the global config file
555 *
556 * This function will load the provided configuration file, and
557 * any other files such as the dictionary. This is the most common
558 * mode of use of this library. The configuration format is compatible
559 * with the radiusclient-ng and freeradius-client formats.
560 *
561 * Note: To preserve compatibility with libraries of the same API
562 * which don't load the dictionary care is taken not to reload the
563 * same filename twice even if instructed to.
564 *
565 * @param filename a name of a file.
566 * @return new rc_handle on success, NULL when failure.
567 */
rc_read_config(char const * filename)568 rc_handle *rc_read_config(char const *filename)
569 {
570 FILE *configfd;
571 char buffer[512], *p;
572 OPTION *option;
573 int line;
574 size_t pos;
575 rc_handle *rh;
576
577
578 rh = rc_new();
579 if (rh == NULL)
580 return NULL;
581
582 rh->config_options = malloc(sizeof(config_options_default));
583 if (rh->config_options == NULL) {
584 rc_log(LOG_CRIT, "rc_read_config: out of memory");
585 rc_destroy(rh);
586 return NULL;
587 }
588 memcpy(rh->config_options, &config_options_default, sizeof(config_options_default));
589
590 if ((configfd = fopen(filename,"r")) == NULL)
591 {
592 rc_log(LOG_ERR,"rc_read_config: can't open %s: %s", filename, strerror(errno));
593 rc_destroy(rh);
594 return NULL;
595 }
596
597 line = 0;
598 while ((fgets(buffer, sizeof(buffer), configfd) != NULL))
599 {
600 line++;
601 p = buffer;
602
603 if ((*p == '\n') || (*p == '#') || (*p == '\0'))
604 continue;
605
606 p[strlen(p)-1] = '\0';
607
608
609 if ((pos = strcspn(p, "\t ")) == 0) {
610 rc_log(LOG_ERR, "%s: line %d: bogus format: %s", filename, line, p);
611 fclose(configfd);
612 rc_destroy(rh);
613 return NULL;
614 }
615
616 p[pos] = '\0';
617
618 if ((option = find_option(rh, p, OT_ANY)) == NULL) {
619 rc_log(LOG_ERR, "%s: line %d: unrecognized keyword: %s", filename, line, p);
620 fclose(configfd);
621 rc_destroy(rh);
622 return NULL;
623 }
624
625 if (option->status != ST_UNDEF) {
626 rc_log(LOG_ERR, "%s: line %d: duplicate option line: %s", filename, line, p);
627 fclose(configfd);
628 rc_destroy(rh);
629 return NULL;
630 }
631
632 p += pos+1;
633 while (isspace(*p))
634 p++;
635 pos = strlen(p) - 1;
636 while(pos != 0 && isspace(p[pos]))
637 pos--;
638 p[pos + 1] = '\0';
639
640 switch (option->type) {
641 case OT_STR:
642 if (set_option_str(filename, line, option, p) < 0) {
643 fclose(configfd);
644 rc_destroy(rh);
645 return NULL;
646 }
647 break;
648 case OT_INT:
649 if (set_option_int(filename, line, option, p) < 0) {
650 fclose(configfd);
651 rc_destroy(rh);
652 return NULL;
653 }
654 break;
655 case OT_SRV:
656 if (set_option_srv(filename, line, option, p) < 0) {
657 fclose(configfd);
658 rc_destroy(rh);
659 return NULL;
660 }
661 break;
662 case OT_AUO:
663 if (set_option_auo(filename, line, option, p) < 0) {
664 fclose(configfd);
665 rc_destroy(rh);
666 return NULL;
667 }
668 break;
669 default:
670 rc_log(LOG_CRIT, "rc_read_config: impossible case branch!");
671 abort();
672 }
673 }
674 fclose(configfd);
675
676 if (rc_test_config(rh, filename) == -1) {
677 rc_destroy(rh);
678 return NULL;
679 }
680
681 {
682 int clientdebug = rc_conf_int_2(rh, "clientdebug", FALSE);
683 if(clientdebug > 0) {
684 radcli_debug = clientdebug;
685 }
686 }
687
688 p = rc_conf_str(rh, "dictionary");
689 if (p != NULL) {
690 if (rc_read_dictionary(rh, p) != 0) {
691 rc_log(LOG_CRIT, "could not load dictionary");
692 rc_destroy(rh);
693 return NULL;
694 }
695 } else {
696 rc_log(LOG_INFO, "rc_read_config: no dictionary was specified");
697 }
698
699 return rh;
700 }
701
702 /** Get the value of a config option
703 *
704 * @param rh a handle to parsed configuration.
705 * @param optname the name of an option.
706 * @return config option value.
707 */
rc_conf_str(rc_handle const * rh,char const * optname)708 char *rc_conf_str(rc_handle const *rh, char const *optname)
709 {
710 OPTION *option;
711
712 option = find_option(rh, optname, OT_STR);
713
714 if (option != NULL) {
715 return (char *)option->val;
716 } else {
717 rc_log(LOG_CRIT, "rc_conf_str: unkown config option requested: %s", optname);
718 return NULL;
719 }
720 }
721
722 /*- Get the value of a config option
723 *
724 * @param rh a handle to parsed configuration.
725 * @param optname the name of an option.
726 * @return config option value.
727 */
rc_conf_int_2(rc_handle const * rh,char const * optname,int complain)728 static int rc_conf_int_2(rc_handle const *rh, char const *optname, int complain)
729 {
730 OPTION *option;
731
732 option = find_option(rh, optname, OT_INT|OT_AUO);
733
734 if (option != NULL) {
735 if (option->val) {
736 return *((int *)option->val);
737 } else if(complain) {
738 rc_log(LOG_ERR, "rc_conf_int: config option %s was not set", optname);
739 }
740 return 0;
741 } else {
742 rc_log(LOG_CRIT, "rc_conf_int: unkown config option requested: %s", optname);
743 return 0;
744 }
745 }
746
rc_conf_int(rc_handle const * rh,char const * optname)747 int rc_conf_int(rc_handle const *rh, char const *optname)
748 {
749 return rc_conf_int_2(rh, optname, TRUE);
750 }
751
752 /** Get the value of a config option
753 *
754 * @param rh a handle to parsed configuration.
755 * @param optname the name of an option.
756 * @return config option value.
757 */
rc_conf_srv(rc_handle const * rh,char const * optname)758 SERVER *rc_conf_srv(rc_handle const *rh, char const *optname)
759 {
760 OPTION *option;
761
762 option = find_option(rh, optname, OT_SRV);
763
764 if (option != NULL) {
765 return (SERVER *)option->val;
766 } else {
767 rc_log(LOG_CRIT, "rc_conf_srv: unkown config option requested: %s", optname);
768 return NULL;
769 }
770 }
771
772 /** Tests the configuration the user supplied
773 *
774 * @param rh a handle to parsed configuration.
775 * @param filename a name of a configuration file.
776 * @return 0 on success, -1 when failure.
777 */
rc_test_config(rc_handle * rh,char const * filename)778 int rc_test_config(rc_handle *rh, char const *filename)
779 {
780 SERVER *srv;
781
782 srv = rc_conf_srv(rh, "authserver");
783 if (!srv || !srv->max)
784 {
785 rc_log(LOG_ERR,"%s: no authserver specified", filename);
786 return -1;
787 }
788
789 srv = rc_conf_srv(rh, "acctserver");
790 if (!srv || !srv->max)
791 {
792 /* it is allowed not to have acct servers */
793 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
794 rc_log(LOG_DEBUG,"%s: no acctserver specified", filename);
795 }
796 if (!rc_conf_str(rh, "dictionary"))
797 {
798 rc_log(LOG_ERR,"%s: no dictionary specified", filename);
799 return -1;
800 }
801
802 if (rc_conf_int(rh, "radius_timeout") <= 0)
803 {
804 rc_log(LOG_ERR,"%s: radius_timeout <= 0 is illegal", filename);
805 return -1;
806 }
807 if (rc_conf_int(rh, "radius_retries") <= 0)
808 {
809 rc_log(LOG_ERR,"%s: radius_retries <= 0 is illegal", filename);
810 return -1;
811 }
812
813 if (rc_apply_config(rh) == -1) {
814 return -1;
815 }
816
817 return 0;
818 }
819
820 /** See if info matches hostname
821 *
822 * @param addr a struct addrinfo
823 * @param hostname the name of the host.
824 * @return 0 on success, -1 when failure.
825 */
find_match(const struct addrinfo * addr,const struct addrinfo * hostname)826 static int find_match (const struct addrinfo* addr, const struct addrinfo *hostname)
827 {
828 const struct addrinfo *ptr, *ptr2;
829 unsigned len1, len2;
830
831 ptr = addr;
832 while(ptr) {
833 ptr2 = hostname;
834 while(ptr2) {
835 len1 = SA_GET_INLEN(ptr->ai_addr);
836 len2 = SA_GET_INLEN(ptr2->ai_addr);
837
838 if (len1 > 0 &&
839 len1 == len2 &&
840 memcmp(SA_GET_INADDR(ptr->ai_addr), SA_GET_INADDR(ptr2->ai_addr), len1) == 0) {
841 return 0;
842 }
843 ptr2 = ptr2->ai_next;
844 }
845 ptr = ptr->ai_next;
846 }
847 return -1;
848 }
849
850 /** Checks if provided address is local address
851 *
852 * @param addr an %AF_INET or %AF_INET6 address
853 * @return 0 if local, 1 if not local, -1 on failure.
854 */
rc_ipaddr_local(const struct sockaddr * addr)855 static int rc_ipaddr_local(const struct sockaddr *addr)
856 {
857 int temp_sock, res, serrno;
858 struct sockaddr_storage tmpaddr;
859
860 memcpy(&tmpaddr, addr, SA_LEN(addr));
861
862 temp_sock = socket(addr->sa_family, SOCK_DGRAM, 0);
863 if (temp_sock == -1)
864 return -1;
865
866 if (addr->sa_family == AF_INET) {
867 ((struct sockaddr_in*)&tmpaddr)->sin_port = 0;
868 } else {
869 ((struct sockaddr_in6*)&tmpaddr)->sin6_port = 0;
870 }
871 res = bind(temp_sock, SA(&tmpaddr), SS_LEN(&tmpaddr));
872 serrno = errno;
873 close(temp_sock);
874 if (res == 0)
875 return 0;
876 if (serrno == EADDRNOTAVAIL)
877 return 1;
878 return -1;
879 }
880
881 /** Checks if provided name refers to ourselves
882 *
883 * @param info an addrinfo of the host to check
884 * @return 0 if yes, 1 if no and -1 on failure.
885 */
rc_is_myname(const struct addrinfo * info)886 static int rc_is_myname(const struct addrinfo *info)
887 {
888 const struct addrinfo *p;
889 int res;
890
891 p = info;
892 while(p != NULL) {
893 res = rc_ipaddr_local(p->ai_addr);
894 if (res == 0 || res == -1) {
895 return res;
896 }
897 p = p->ai_next;
898 }
899 return 1;
900 }
901
902 /** Locate a server in the rh config or if not found, check for a servers file
903 *
904 * @param rh a handle to parsed configuration.
905 * @param server_name the name of the server.
906 * @param info: will hold a pointer to addrinfo
907 * @param secret will hold the server's secret (of %MAX_SECRET_LENGTH).
908 * @param type %AUTH or %ACCT
909
910 * @return 0 on success, -1 on failure.
911 */
rc_find_server_addr(rc_handle const * rh,char const * server_name,struct addrinfo ** info,char * secret,rc_type type)912 int rc_find_server_addr (rc_handle const *rh, char const *server_name,
913 struct addrinfo** info, char *secret, rc_type type)
914 {
915 int result = 0;
916 FILE *clientfd;
917 char *h;
918 char *s;
919 char buffer[128];
920 char hostnm[AUTH_ID_LEN + 1];
921 char *buffer_save;
922 char *hostnm_save;
923 SERVER *servers;
924 struct addrinfo *tmpinfo = NULL;
925 const char *fservers;
926 char const *optname;
927
928 /* Lookup the IP address of the radius server */
929 if ((*info = rc_getaddrinfo (server_name, type==AUTH?PW_AI_AUTH:PW_AI_ACCT)) == NULL)
930 return -1;
931
932 switch (type)
933 {
934 case AUTH: optname = "authserver"; break;
935 case ACCT: optname = "acctserver"; break;
936 default: optname = NULL;
937 }
938
939 if ( (optname != NULL) &&
940 ((servers = rc_conf_srv(rh, optname)) != NULL) )
941 {
942 /* Check to see if the server secret is defined in the rh config */
943 unsigned servernum;
944 size_t server_name_len = strlen(server_name);
945 for (servernum = 0; servernum < servers->max; servernum++)
946 {
947 if( (strncmp(server_name, servers->name[servernum], server_name_len) == 0) &&
948 (servers->secret[servernum] != NULL) )
949 {
950 memset(secret, '\0', MAX_SECRET_LENGTH);
951 strlcpy(secret, servers->secret[servernum], MAX_SECRET_LENGTH);
952 return 0;
953 }
954 }
955 }
956
957 /* We didn't find it in the rh_config or the servername is too long so look for a
958 * servers file to define the secret(s)
959 */
960
961 fservers = rc_conf_str(rh, "servers");
962 if (fservers != NULL) {
963 if ((clientfd = fopen (fservers, "r")) == NULL)
964 {
965 rc_log(LOG_ERR, "rc_find_server: couldn't open file: %s: %s", strerror(errno), rc_conf_str(rh, "servers"));
966 goto fail;
967 }
968
969 while (fgets (buffer, sizeof (buffer), clientfd) != NULL)
970 {
971 if (*buffer == '#')
972 continue;
973
974 if ((h = strtok_r(buffer, " \t\n", &buffer_save)) == NULL) /* first hostname */
975 continue;
976
977 strlcpy (hostnm, h, AUTH_ID_LEN);
978
979 if ((s = strtok_r (NULL, " \t\n", &buffer_save)) == NULL) /* and secret field */
980 continue;
981
982 strlcpy (secret, s, MAX_SECRET_LENGTH);
983
984 if (!strchr (hostnm, '/')) /* If single name form */
985 {
986 tmpinfo = rc_getaddrinfo(hostnm, 0);
987 if (tmpinfo)
988 {
989 result = find_match (*info, tmpinfo);
990 if (result == 0)
991 {
992 result++;
993 break;
994 }
995
996 freeaddrinfo(tmpinfo);
997 tmpinfo = NULL;
998 }
999 }
1000 else /* <name1>/<name2> "paired" form */
1001 {
1002 strtok_r(hostnm, "/", &hostnm_save);
1003 tmpinfo = rc_getaddrinfo(hostnm, 0);
1004 if (tmpinfo)
1005 {
1006 if (rc_is_myname(tmpinfo) == 0)
1007 { /* If we're the 1st name, target is 2nd */
1008 if (find_match (*info, tmpinfo) == 0)
1009 {
1010 result++;
1011 break;
1012 }
1013 }
1014 else /* If we were 2nd name, target is 1st name */
1015 {
1016 if (find_match (*info, tmpinfo) == 0)
1017 {
1018 result++;
1019 break;
1020 }
1021 }
1022 freeaddrinfo(tmpinfo);
1023 tmpinfo = NULL;
1024 }
1025 }
1026 }
1027 fclose (clientfd);
1028 }
1029 if (result == 0)
1030 {
1031 memset (buffer, '\0', sizeof (buffer));
1032 memset (secret, '\0', MAX_SECRET_LENGTH);
1033 rc_log(LOG_ERR, "rc_find_server: couldn't find RADIUS server %s in %s",
1034 server_name, rc_conf_str(rh, "servers"));
1035 goto fail;
1036 }
1037
1038 result = 0;
1039 goto cleanup;
1040
1041 fail:
1042 freeaddrinfo(*info);
1043 result = -1;
1044
1045 cleanup:
1046 if (tmpinfo)
1047 freeaddrinfo(tmpinfo);
1048
1049 return result;
1050 }
1051
1052 /**
1053 * rc_config_free:
1054 * @param rh a handle to parsed configuration
1055 *
1056 * Free allocated config values. For legacy compatibility
1057 * reasons this will not release any dictionary entries.
1058 * To release all memory from the handle use rc_destroy()
1059 * instead.
1060 *
1061 */
rc_config_free(rc_handle * rh)1062 void rc_config_free(rc_handle *rh)
1063 {
1064 int i;
1065 SERVER *serv;
1066
1067 if (rh->config_options == NULL)
1068 return;
1069
1070 for (i = 0; i < NUM_OPTIONS; i++) {
1071 if (rh->config_options[i].val == NULL)
1072 continue;
1073 if (rh->config_options[i].type == OT_SRV) {
1074 serv = (SERVER *)rh->config_options[i].val;
1075 free(serv->name[0]);
1076 if(serv->secret[0]) free(serv->secret[0]);
1077 free(serv);
1078 } else {
1079 free(rh->config_options[i].val);
1080 }
1081 }
1082 free(rh->config_options);
1083 free(rh->first_dict_read);
1084 rh->config_options = NULL;
1085 rh->first_dict_read = NULL;
1086 }
1087
1088 static int _initialized = 0;
1089
1090 /** Initialises new Radius Client handle
1091 *
1092 * @return a new rc_handle (free with rc_destroy).
1093 */
rc_new(void)1094 rc_handle *rc_new(void)
1095 {
1096 rc_handle *rh;
1097
1098 if (_initialized == 0) {
1099 #if defined(HAVE_GNUTLS) && GNUTLS_VERSION_NUMBER < 0x030300
1100 int ret;
1101 ret = gnutls_global_init();
1102 if (ret < 0) {
1103 rc_log(LOG_ERR,
1104 "%s: error initializing gnutls: %s",
1105 __func__, gnutls_strerror(ret));
1106 return NULL;
1107 }
1108 #endif
1109 srandom((unsigned int)(time(NULL)+getpid()));
1110 }
1111 _initialized++;
1112
1113 rh = calloc(1, sizeof(*rh));
1114 if (rh == NULL) {
1115 rc_log(LOG_CRIT, "rc_new: out of memory");
1116 return NULL;
1117 }
1118 return rh;
1119 }
1120
1121 /** Destroys Radius Client handle reclaiming all memory
1122 *
1123 * @param rh The Radius client handle to free.
1124 */
rc_destroy(rc_handle * rh)1125 void rc_destroy(rc_handle *rh)
1126 {
1127 rc_dict_free(rh);
1128 rc_config_free(rh);
1129 free(rh);
1130
1131 #if defined(HAVE_GNUTLS) && GNUTLS_VERSION_NUMBER < 0x030300
1132 _initialized--;
1133 if (_initialized == 0) {
1134 gnutls_global_deinit();
1135 }
1136 #endif
1137 }
1138
1139 /** Returns the type of the socket used
1140 *
1141 * That indicates the type of connection used with the radius
1142 * server, and can be UDP, TLS or DTLS.
1143 *
1144 * @return the type of the socket
1145 */
rc_get_socket_type(rc_handle * rh)1146 rc_socket_type rc_get_socket_type(rc_handle *rh)
1147 {
1148 return rh->so_type;
1149 }
1150
1151 /** @} */
1152 /*
1153 * Local Variables:
1154 * c-basic-offset:8
1155 * c-style: whitesmith
1156 * End:
1157 */
1158