1 /*
2  * $Id: config.c,v 1.12 2005/07/21 07:55:56 sobomax Exp $
3  *
4  * Copyright (C) 1995,1996,1997 Lars Fenneberg
5  *
6  * Copyright 1992 Livingston Enterprises, Inc.
7  *
8  * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
9  * and Merit Network, Inc. All Rights Reserved
10  *
11  * See the file COPYRIGHT for the respective terms and conditions.
12  * If the file is missing contact me at lf@elemental.net
13  * and I'll send you a copy.
14  *
15  */
16 
17 #include <config.h>
18 #include <includes.h>
19 #include <radiusclient-ng.h>
20 #include <options.h>
21 
22 static int test_config(rc_handle *, char *);
23 
24 /*
25  * Function: find_option
26  *
27  * Purpose: find an option in the option list
28  *
29  * Returns: pointer to option on success, NULL otherwise
30  */
31 
find_option(rc_handle * rh,char * optname,unsigned int type)32 static OPTION *find_option(rc_handle *rh, char *optname, unsigned int type)
33 {
34 	int i;
35 
36 	/* there're so few options that a binary search seems not necessary */
37 	for (i = 0; i < NUM_OPTIONS; i++) {
38 		if (!strcmp(rh->config_options[i].name, optname) &&
39 		    (rh->config_options[i].type & type))
40 		    	return &rh->config_options[i];
41 	}
42 
43 	return NULL;
44 }
45 
46 /*
47  * Function: set_option_...
48  *
49  * Purpose: set a specific option doing type conversions
50  *
51  * Returns: 0 on success, -1 on failure
52  */
53 
set_option_str(char * filename,int line,OPTION * option,char * p)54 static int set_option_str(char *filename, int line, OPTION *option, char *p)
55 {
56 	if (p) {
57 		option->val = (void *) strdup(p);
58 		if (option->val == NULL) {
59 			rc_log(LOG_CRIT, "read_config: out of memory");
60 			return -1;
61 		}
62 	} else {
63 		option->val = NULL;
64 	}
65 
66 	return 0;
67 }
68 
set_option_int(char * filename,int line,OPTION * option,char * p)69 static int set_option_int(char *filename, int line, OPTION *option, char *p)
70 {
71 	int *iptr;
72 
73 	if (p == NULL) {
74 		rc_log(LOG_ERR, "%s: line %d: bogus option value", filename, line);
75 		return -1;
76 	}
77 
78 	if ((iptr = malloc(sizeof(*iptr))) == NULL) {
79 		rc_log(LOG_CRIT, "read_config: out of memory");
80 		return -1;
81 	}
82 
83 	*iptr = atoi(p);
84 	option->val = (void *) iptr;
85 
86 	return 0;
87 }
88 
set_option_srv(char * filename,int line,OPTION * option,char * p)89 static int set_option_srv(char *filename, int line, OPTION *option, char *p)
90 {
91 	SERVER *serv;
92 	char *q;
93 	struct servent *svp;
94 
95 	if (p == NULL) {
96 		rc_log(LOG_ERR, "%s: line %d: bogus option value", filename, line);
97 		return -1;
98 	}
99 
100 	serv = (SERVER *) option->val;
101 	if (serv == NULL) {
102 		serv = malloc(sizeof(*serv));
103 		if (serv == NULL) {
104 			rc_log(LOG_CRIT, "read_config: out of memory");
105 			return -1;
106 		}
107 		serv->max = 0;
108 	}
109 
110 	while ((p = strtok(p, ", \t")) != NULL) {
111 
112 		if ((q = strchr(p,':')) != NULL) {
113 			*q = '\0';
114 			q++;
115 			serv->port[serv->max] = atoi(q);
116 		} else {
117 			if (!strcmp(option->name,"authserver"))
118 				if ((svp = getservbyname ("radius", "udp")) == NULL)
119 					serv->port[serv->max] = PW_AUTH_UDP_PORT;
120 				else
121 					serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
122 			else if (!strcmp(option->name, "acctserver"))
123 				if ((svp = getservbyname ("radacct", "udp")) == NULL)
124 					serv->port[serv->max] = PW_ACCT_UDP_PORT;
125 				else
126 					serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
127 			else {
128 				rc_log(LOG_ERR, "%s: line %d: no default port for %s", filename, line, option->name);
129 				if (option->val == NULL)
130 					free(serv);
131 				return -1;
132 			}
133 		}
134 
135 		serv->name[serv->max] = strdup(p);
136 		if (serv->name[serv->max] == NULL) {
137 			rc_log(LOG_CRIT, "read_config: out of memory");
138 			if (option->val == NULL)
139 				free(serv);
140 			return -1;
141 		}
142 		serv->max++;
143 
144 		p = NULL;
145 	}
146 	if (option->val == NULL)
147 		option->val = (void *)serv;
148 
149 	return 0;
150 }
151 
set_option_auo(char * filename,int line,OPTION * option,char * p)152 static int set_option_auo(char *filename, int line, OPTION *option, char *p)
153 {
154 	int *iptr;
155 
156 	if (p == NULL) {
157 		rc_log(LOG_WARNING, "%s: line %d: bogus option value", filename, line);
158 		return -1;
159 	}
160 
161 	if ((iptr = malloc(sizeof(iptr))) == NULL) {
162 			rc_log(LOG_CRIT, "read_config: out of memory");
163 			return -1;
164 	}
165 
166 	*iptr = 0;
167 	p = strtok(p, ", \t");
168 
169 	if (!strncmp(p, "local", 5))
170 			*iptr = AUTH_LOCAL_FST;
171 	else if (!strncmp(p, "radius", 6))
172 			*iptr = AUTH_RADIUS_FST;
173 	else {
174 		rc_log(LOG_ERR,"%s: auth_order: unknown keyword: %s", filename, p);
175 		return -1;
176 	}
177 
178 	p = strtok(NULL, ", \t");
179 
180 	if (p && (*p != '\0')) {
181 		if ((*iptr & AUTH_RADIUS_FST) && !strcmp(p, "local"))
182 			*iptr = (*iptr) | AUTH_LOCAL_SND;
183 		else if ((*iptr & AUTH_LOCAL_FST) && !strcmp(p, "radius"))
184 			*iptr = (*iptr) | AUTH_RADIUS_SND;
185 		else {
186 			rc_log(LOG_ERR,"%s: auth_order: unknown or unexpected keyword: %s", filename, p);
187 			return -1;
188 		}
189 	}
190 
191 	option->val = (void *) iptr;
192 
193 	return 0;
194 }
195 
196 
197 /*
198  * Function: rc_read_config
199  *
200  * Purpose: read the global config file
201  *
202  * Returns: new rc_handle on success, NULL when failure
203  */
204 
205 rc_handle *
rc_read_config(char * filename)206 rc_read_config(char *filename)
207 {
208 	FILE *configfd;
209 	char buffer[512], *p;
210 	OPTION *option;
211 	int line, pos;
212 	rc_handle *rh;
213 
214 	rh = rc_new();
215 	if (rh == NULL)
216 		return NULL;
217 
218         rh->config_options = malloc(sizeof(config_options_default));
219         if (rh->config_options == NULL) {
220                 rc_log(LOG_CRIT, "rc_read_config: out of memory");
221 		rc_destroy(rh);
222                 return NULL;
223         }
224         memcpy(rh->config_options, &config_options_default, sizeof(config_options_default));
225 
226 	if ((configfd = fopen(filename,"r")) == NULL)
227 	{
228 		rc_log(LOG_ERR,"rc_read_config: can't open %s: %s", filename, strerror(errno));
229 		rc_destroy(rh);
230 		return NULL;
231 	}
232 
233 	line = 0;
234 	while ((fgets(buffer, sizeof(buffer), configfd) != NULL))
235 	{
236 		line++;
237 		p = buffer;
238 
239 		if ((*p == '\n') || (*p == '#') || (*p == '\0'))
240 			continue;
241 
242 		p[strlen(p)-1] = '\0';
243 
244 
245 		if ((pos = strcspn(p, "\t ")) == 0) {
246 			rc_log(LOG_ERR, "%s: line %d: bogus format: %s", filename, line, p);
247 			fclose(configfd);
248 			rc_destroy(rh);
249 			return NULL;
250 		}
251 
252 		p[pos] = '\0';
253 
254 		if ((option = find_option(rh, p, OT_ANY)) == NULL) {
255 			rc_log(LOG_ERR, "%s: line %d: unrecognized keyword: %s", filename, line, p);
256 			fclose(configfd);
257 			rc_destroy(rh);
258 			return NULL;
259 		}
260 
261 		if (option->status != ST_UNDEF) {
262 			rc_log(LOG_ERR, "%s: line %d: duplicate option line: %s", filename, line, p);
263 			fclose(configfd);
264 			rc_destroy(rh);
265 			return NULL;
266 		}
267 
268 		p += pos+1;
269 		while (isspace(*p))
270 			p++;
271 		pos = strlen(p) - 1;
272 		while(pos >= 0 && isspace(p[pos]))
273 			pos--;
274 		p[pos + 1] = '\0';
275 
276 		switch (option->type) {
277 			case OT_STR:
278 				if (set_option_str(filename, line, option, p) < 0) {
279 					fclose(configfd);
280 					rc_destroy(rh);
281 				 	return NULL;
282 				}
283 				break;
284 			case OT_INT:
285 				if (set_option_int(filename, line, option, p) < 0) {
286 					fclose(configfd);
287 					rc_destroy(rh);
288 				 	return NULL;
289 				}
290 				break;
291 			case OT_SRV:
292 				if (set_option_srv(filename, line, option, p) < 0) {
293 					fclose(configfd);
294 					rc_destroy(rh);
295 				 	return NULL;
296 				}
297 				break;
298 			case OT_AUO:
299 				if (set_option_auo(filename, line, option, p) < 0) {
300 					fclose(configfd);
301 					rc_destroy(rh);
302 				 	return NULL;
303 				}
304 				break;
305 			default:
306 				rc_log(LOG_CRIT, "rc_read_config: impossible case branch!");
307 				abort();
308 		}
309 	}
310 	fclose(configfd);
311 
312 	if (test_config(rh, filename) == -1) {
313 		rc_destroy(rh);
314 		return NULL;
315 	}
316 	return rh;
317 }
318 
319 /*
320  * Function: rc_conf_str, rc_conf_int, rc_conf_src
321  *
322  * Purpose: get the value of a config option
323  *
324  * Returns: config option value
325  */
326 
rc_conf_str(rc_handle * rh,char * optname)327 char *rc_conf_str(rc_handle *rh, char *optname)
328 {
329 	OPTION *option;
330 
331 	option = find_option(rh, optname, OT_STR);
332 
333 	if (option != NULL) {
334 		return (char *)option->val;
335 	} else {
336 		rc_log(LOG_CRIT, "rc_conf_str: unkown config option requested: %s", optname);
337 		abort();
338 	}
339 }
340 
rc_conf_int(rc_handle * rh,char * optname)341 int rc_conf_int(rc_handle *rh, char *optname)
342 {
343 	OPTION *option;
344 
345 	option = find_option(rh, optname, OT_INT|OT_AUO);
346 
347 	if (option != NULL) {
348 		return *((int *)option->val);
349 	} else {
350 		rc_log(LOG_CRIT, "rc_conf_int: unkown config option requested: %s", optname);
351 		abort();
352 	}
353 }
354 
rc_conf_srv(rc_handle * rh,char * optname)355 SERVER *rc_conf_srv(rc_handle *rh, char *optname)
356 {
357 	OPTION *option;
358 
359 	option = find_option(rh, optname, OT_SRV);
360 
361 	if (option != NULL) {
362 		return (SERVER *)option->val;
363 	} else {
364 		rc_log(LOG_CRIT, "rc_conf_srv: unkown config option requested: %s", optname);
365 		abort();
366 	}
367 }
368 
369 /*
370  * Function: test_config
371  *
372  * Purpose: test the configuration the user supplied
373  *
374  * Returns: 0 on success, -1 when failure
375  */
376 
test_config(rc_handle * rh,char * filename)377 static int test_config(rc_handle *rh, char *filename)
378 {
379 #if 0
380 	struct stat st;
381 	char	    *file;
382 #endif
383 
384 	if (!(rc_conf_srv(rh, "authserver")->max))
385 	{
386 		rc_log(LOG_ERR,"%s: no authserver specified", filename);
387 		return -1;
388 	}
389 	if (!(rc_conf_srv(rh, "acctserver")->max))
390 	{
391 		rc_log(LOG_ERR,"%s: no acctserver specified", filename);
392 		return -1;
393 	}
394 	if (!rc_conf_str(rh, "servers"))
395 	{
396 		rc_log(LOG_ERR,"%s: no servers file specified", filename);
397 		return -1;
398 	}
399 	if (!rc_conf_str(rh, "dictionary"))
400 	{
401 		rc_log(LOG_ERR,"%s: no dictionary specified", filename);
402 		return -1;
403 	}
404 
405 	if (rc_conf_int(rh, "radius_timeout") <= 0)
406 	{
407 		rc_log(LOG_ERR,"%s: radius_timeout <= 0 is illegal", filename);
408 		return -1;
409 	}
410 	if (rc_conf_int(rh, "radius_retries") <= 0)
411 	{
412 		rc_log(LOG_ERR,"%s: radius_retries <= 0 is illegal", filename);
413 		return -1;
414 	}
415 
416 #if 0
417 	file = rc_conf_str(rh, "login_local");
418 	if (stat(file, &st) == 0)
419 	{
420 		if (!S_ISREG(st.st_mode)) {
421 			rc_log(LOG_ERR,"%s: not a regular file: %s", filename, file);
422 			return -1;
423 		}
424 	} else {
425 		rc_log(LOG_ERR,"%s: file not found: %s", filename, file);
426 		return -1;
427 	}
428 	file = rc_conf_str(rh, "login_radius");
429 	if (stat(file, &st) == 0)
430 	{
431 		if (!S_ISREG(st.st_mode)) {
432 			rc_log(LOG_ERR,"%s: not a regular file: %s", filename, file);
433 			return -1;
434 		}
435 	} else {
436 		rc_log(LOG_ERR,"%s: file not found: %s", filename, file);
437 		return -1;
438 	}
439 #endif
440 
441 	if (rc_conf_int(rh, "login_tries") <= 0)
442 	{
443 		rc_log(LOG_ERR,"%s: login_tries <= 0 is illegal", filename);
444 		return -1;
445 	}
446 	if (rc_conf_str(rh, "seqfile") == NULL)
447 	{
448 		rc_log(LOG_ERR,"%s: seqfile not specified", filename);
449 		return -1;
450 	}
451 	if (rc_conf_int(rh, "login_timeout") <= 0)
452 	{
453 		rc_log(LOG_ERR,"%s: login_timeout <= 0 is illegal", filename);
454 		return -1;
455 	}
456 	if (rc_conf_str(rh, "mapfile") == NULL)
457 	{
458 		rc_log(LOG_ERR,"%s: mapfile not specified", filename);
459 		return -1;
460 	}
461 	if (rc_conf_str(rh, "nologin") == NULL)
462 	{
463 		rc_log(LOG_ERR,"%s: nologin not specified", filename);
464 		return -1;
465 	}
466 
467 	return 0;
468 }
469 
470 /*
471  * Function: rc_find_match
472  *
473  * Purpose: see if ip_addr is one of the ip addresses of hostname
474  *
475  * Returns: 0 on success, -1 when failure
476  *
477  */
478 
find_match(UINT4 * ip_addr,char * hostname)479 static int find_match (UINT4 *ip_addr, char *hostname)
480 {
481 	UINT4           addr;
482 	char          **paddr;
483 	struct hostent *hp;
484 
485 	if (rc_good_ipaddr (hostname) == 0)
486 	{
487 		if (*ip_addr == ntohl(inet_addr (hostname)))
488 		{
489 			return 0;
490 		}
491 	}
492 	else
493 	{
494 		if ((hp = gethostbyname (hostname)) == NULL)
495 		{
496 			return -1;
497 		}
498 		for (paddr = hp->h_addr_list; *paddr; paddr++)
499 		{
500 			addr = ** (UINT4 **) paddr;
501 			if (ntohl(addr) == *ip_addr)
502 			{
503 				return 0;
504 			}
505 		}
506 	}
507 	return -1;
508 }
509 
510 /*
511  * Function: rc_ipaddr_local
512  *
513  * Purpose: checks if provided address is local address
514  *
515  * Returns: 0 if local, 1 if not local, -1 on failure
516  *
517  */
518 
519 static int
rc_ipaddr_local(UINT4 ip_addr)520 rc_ipaddr_local(UINT4 ip_addr)
521 {
522 	int temp_sock, res, serrno;
523 	struct sockaddr_in sin;
524 
525 	temp_sock = socket(AF_INET, SOCK_DGRAM, 0);
526 	if (temp_sock == -1)
527 		return -1;
528 	memset(&sin, '\0', sizeof(sin));
529 	sin.sin_family = AF_INET;
530 	sin.sin_addr.s_addr = htonl(ip_addr);
531 	sin.sin_port = htons(0);
532 	res = bind(temp_sock, (struct sockaddr *)&sin, sizeof(sin));
533 	serrno = errno;
534 	close(temp_sock);
535 	if (res == 0)
536 		return 0;
537 	if (serrno == EADDRNOTAVAIL)
538 		return 1;
539 	return -1;
540 }
541 
542 /*
543  * Function: rc_is_myname
544  *
545  * Purpose: check if provided name refers to ourselves
546  *
547  * Returns: 0 if yes, 1 if no and -1 on failure
548  *
549  */
550 
551 static int
rc_is_myname(char * hostname)552 rc_is_myname(char *hostname)
553 {
554 	UINT4 addr;
555 	char **paddr;
556 	struct hostent *hp;
557 	int res;
558 
559 	if (rc_good_ipaddr(hostname) == 0)
560 		return rc_ipaddr_local(ntohl(inet_addr(hostname)));
561 
562 	if ((hp = gethostbyname (hostname)) == NULL)
563 		return -1;
564 	for (paddr = hp->h_addr_list; *paddr; paddr++) {
565 		addr = **(UINT4 **)paddr;
566 		res = rc_ipaddr_local(ntohl(addr));
567 		if (res == 0 || res == -1)
568 			return res;
569 	}
570 	return 1;
571 }
572 
573 /*
574  * Function: rc_find_server
575  *
576  * Purpose: search a server in the servers file
577  *
578  * Returns: 0 on success, -1 on failure
579  *
580  */
581 
rc_find_server(rc_handle * rh,char * server_name,UINT4 * ip_addr,char * secret)582 int rc_find_server (rc_handle *rh, char *server_name, UINT4 *ip_addr, char *secret)
583 {
584 	int             len;
585 	int             result;
586 	FILE           *clientfd;
587 	char           *h;
588 	char           *s;
589 	char           *host2;
590 	char            buffer[128];
591 	char            hostnm[AUTH_ID_LEN + 1];
592 
593 	/* Get the IP address of the authentication server */
594 	if ((*ip_addr = rc_get_ipaddr (server_name)) == (UINT4) 0)
595 		return -1;
596 
597 	if ((clientfd = fopen (rc_conf_str(rh, "servers"), "r")) == NULL)
598 	{
599 		rc_log(LOG_ERR, "rc_find_server: couldn't open file: %s: %s", strerror(errno), rc_conf_str(rh, "servers"));
600 		return -1;
601 	}
602 
603 	result = 0;
604 	while (fgets (buffer, sizeof (buffer), clientfd) != NULL)
605 	{
606 		if (*buffer == '#')
607 			continue;
608 
609 		if ((h = strtok (buffer, " \t\n")) == NULL) /* first hostname */
610 			continue;
611 
612 		memset (hostnm, '\0', AUTH_ID_LEN);
613 		len = strlen (h);
614 		if (len > AUTH_ID_LEN)
615 		{
616 			len = AUTH_ID_LEN;
617 		}
618 		strncpy (hostnm, h, (size_t) len);
619 		hostnm[AUTH_ID_LEN] = '\0';
620 
621 		if ((s = strtok (NULL, " \t\n")) == NULL) /* and secret field */
622 			continue;
623 
624 		memset (secret, '\0', MAX_SECRET_LENGTH);
625 		len = strlen (s);
626 		if (len > MAX_SECRET_LENGTH)
627 		{
628 			len = MAX_SECRET_LENGTH;
629 		}
630 		strncpy (secret, s, (size_t) len);
631 		secret[MAX_SECRET_LENGTH] = '\0';
632 
633 		if (!strchr (hostnm, '/')) /* If single name form */
634 		{
635 			if (find_match (ip_addr, hostnm) == 0)
636 			{
637 				result++;
638 				break;
639 			}
640 		}
641 		else /* <name1>/<name2> "paired" form */
642 		{
643 			strtok (hostnm, "/");
644 			if (rc_is_myname(hostnm) == 0)
645 			{	     /* If we're the 1st name, target is 2nd */
646 				host2 = strtok (NULL, " ");
647 				if (find_match (ip_addr, host2) == 0)
648 				{
649 					result++;
650 					break;
651 				}
652 			}
653 			else	/* If we were 2nd name, target is 1st name */
654 			{
655 				if (find_match (ip_addr, hostnm) == 0)
656 				{
657 					result++;
658 					break;
659 				}
660 			}
661 		}
662 	}
663 	fclose (clientfd);
664 	if (result == 0)
665 	{
666 		memset (buffer, '\0', sizeof (buffer));
667 		memset (secret, '\0', sizeof (secret));
668 		rc_log(LOG_ERR, "rc_find_server: couldn't find RADIUS server %s in %s",
669 			 server_name, rc_conf_str(rh, "servers"));
670 		return -1;
671 	}
672 	return 0;
673 }
674 
675 /*
676  * Function: rc_config_free
677  *
678  * Purpose: Free allocated config values
679  *
680  * Arguments: Radius Client handle
681  */
682 
683 void
rc_config_free(rc_handle * rh)684 rc_config_free(rc_handle *rh)
685 {
686 	int i, j;
687 	SERVER *serv;
688 
689 	if (rh->config_options == NULL)
690 		return;
691 
692 	for (i = 0; i < NUM_OPTIONS; i++) {
693 		if (rh->config_options[i].val == NULL)
694 			continue;
695 		if (rh->config_options[i].type == OT_SRV) {
696 		        serv = (SERVER *)rh->config_options[i].val;
697 			for (j = 0; j < serv->max; j++)
698 				free(serv->name[j]);
699 			free(serv);
700 		} else {
701 			free(rh->config_options[i].val);
702 		}
703 	}
704 	free(rh->config_options);
705 	rh->config_options = NULL;
706 }
707