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