1/*
2 * $Id: config.c,v 1.1.1.1 2008/10/15 03:30:50 james26_jang 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.h>
20#include <options.h>
21
22static int test_config(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
32static OPTION *find_option(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(config_options[i].name, optname) &&
39		    (config_options[i].type & type))
40			return &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
54static int set_option_str(char *filename, int line, OPTION *option, char *p)
55{
56	if (p)
57		option->val = (void *) strdup(p);
58	else
59		option->val = NULL;
60
61	return 0;
62}
63
64static int set_option_int(char *filename, int line, OPTION *option, char *p)
65{
66	int *iptr;
67
68	if (p == NULL) {
69		rc_log(LOG_ERR, "%s: line %d: bogus option value", filename, line);
70		return (-1);
71	}
72
73	if ((iptr = (int *) malloc(sizeof(iptr))) == NULL) {
74		rc_log(LOG_CRIT, "read_config: out of memory");
75		return (-1);
76	}
77
78	*iptr = atoi(p);
79	option->val = (void *) iptr;
80
81	return 0;
82}
83
84static int set_option_srv(char *filename, int line, OPTION *option, char *p)
85{
86	SERVER *serv;
87	char *q;
88	struct servent *svp;
89	int i;
90
91	if (p == NULL) {
92		rc_log(LOG_ERR, "%s: line %d: bogus option value", filename, line);
93		return (-1);
94	}
95
96	serv = (SERVER *) option->val;
97
98	for (i = 0; i < serv->max; i++) {
99		free(serv->name[i]);
100	}
101	serv->max = 0;
102
103	while ((p = strtok(p, ", \t")) != NULL) {
104
105		if ((q = strchr(p,':')) != NULL) {
106			*q = '\0';
107			q++;
108			serv->port[serv->max] = atoi(q);
109		} else {
110			if (!strcmp(option->name,"authserver"))
111				if ((svp = getservbyname ("radius", "udp")) == NULL)
112					serv->port[serv->max] = PW_AUTH_UDP_PORT;
113				else
114					serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
115			else if (!strcmp(option->name, "acctserver"))
116				if ((svp = getservbyname ("radacct", "udp")) == NULL)
117					serv->port[serv->max] = PW_ACCT_UDP_PORT;
118				else
119					serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
120			else {
121				rc_log(LOG_ERR, "%s: line %d: no default port for %s", filename, line, option->name);
122				return (-1);
123			}
124		}
125
126		serv->name[serv->max++] = strdup(p);
127
128		p = NULL;
129	}
130
131	return 0;
132}
133
134static int set_option_auo(char *filename, int line, OPTION *option, char *p)
135{
136	int *iptr;
137
138	if (p == NULL) {
139		rc_log(LOG_WARNING, "%s: line %d: bogus option value", filename, line);
140		return (-1);
141	}
142
143	if ((iptr = (int *) malloc(sizeof(iptr))) == NULL) {
144			rc_log(LOG_CRIT, "read_config: out of memory");
145			return (-1);
146	}
147
148	*iptr = 0;
149	p = strtok(p, ", \t");
150
151	if (!strncmp(p, "local", 5))
152			*iptr = AUTH_LOCAL_FST;
153	else if (!strncmp(p, "radius", 6))
154			*iptr = AUTH_RADIUS_FST;
155	else {
156		rc_log(LOG_ERR,"%s: auth_order: unknown keyword: %s", filename, p);
157		return (-1);
158	}
159
160	p = strtok(NULL, ", \t");
161
162	if (p && (*p != '\0')) {
163		if ((*iptr & AUTH_RADIUS_FST) && !strcmp(p, "local"))
164			*iptr = (*iptr) | AUTH_LOCAL_SND;
165		else if ((*iptr & AUTH_LOCAL_FST) && !strcmp(p, "radius"))
166			*iptr = (*iptr) | AUTH_RADIUS_SND;
167		else {
168			rc_log(LOG_ERR,"%s: auth_order: unknown or unexpected keyword: %s", filename, p);
169			return (-1);
170		}
171	}
172
173	option->val = (void *) iptr;
174
175	return 0;
176}
177
178
179/*
180 * Function: rc_read_config
181 *
182 * Purpose: read the global config file
183 *
184 * Returns: 0 on success, -1 when failure
185 */
186
187int rc_read_config(char *filename)
188{
189	FILE *configfd;
190	char buffer[512], *p;
191	OPTION *option;
192	int line, pos;
193
194	if ((configfd = fopen(filename,"r")) == NULL)
195	{
196		rc_log(LOG_ERR,"rc_read_config: can't open %s: %s", filename, strerror(errno));
197		return (-1);
198	}
199
200	line = 0;
201	while ((fgets(buffer, sizeof(buffer), configfd) != NULL))
202	{
203		line++;
204		p = buffer;
205
206		if ((*p == '\n') || (*p == '#') || (*p == '\0'))
207			continue;
208
209		p[strlen(p)-1] = '\0';
210
211
212		if ((pos = strcspn(p, "\t ")) == 0) {
213			rc_log(LOG_ERR, "%s: line %d: bogus format: %s", filename, line, p);
214			return (-1);
215		}
216
217		p[pos] = '\0';
218
219		if ((option = find_option(p, OT_ANY)) == NULL) {
220			rc_log(LOG_ERR, "%s: line %d: unrecognized keyword: %s", filename, line, p);
221			return (-1);
222		}
223
224		if (option->status != ST_UNDEF) {
225			rc_log(LOG_ERR, "%s: line %d: duplicate option line: %s", filename, line, p);
226			return (-1);
227		}
228
229		p += pos+1;
230		while (isspace(*p))
231			p++;
232
233		switch (option->type) {
234			case OT_STR:
235				 if (set_option_str(filename, line, option, p) < 0)
236					return (-1);
237				break;
238			case OT_INT:
239				 if (set_option_int(filename, line, option, p) < 0)
240					return (-1);
241				break;
242			case OT_SRV:
243				 if (set_option_srv(filename, line, option, p) < 0)
244					return (-1);
245				break;
246			case OT_AUO:
247				 if (set_option_auo(filename, line, option, p) < 0)
248					return (-1);
249				break;
250			default:
251				rc_log(LOG_CRIT, "rc_read_config: impossible case branch!");
252				abort();
253		}
254	}
255	fclose(configfd);
256
257	return test_config(filename);
258}
259
260/*
261 * Function: rc_conf_str, rc_conf_int, rc_conf_src
262 *
263 * Purpose: get the value of a config option
264 *
265 * Returns: config option value
266 */
267
268char *rc_conf_str(char *optname)
269{
270	OPTION *option;
271
272	option = find_option(optname, OT_STR);
273
274	if (option != NULL) {
275		return (char *)option->val;
276	} else {
277		rc_log(LOG_CRIT, "rc_conf_str: unkown config option requested: %s", optname);
278		abort();
279	}
280}
281
282int rc_conf_int(char *optname)
283{
284	OPTION *option;
285
286	option = find_option(optname, OT_INT|OT_AUO);
287
288	if (option != NULL) {
289		return *((int *)option->val);
290	} else {
291		rc_log(LOG_CRIT, "rc_conf_int: unkown config option requested: %s", optname);
292		abort();
293	}
294}
295
296SERVER *rc_conf_srv(char *optname)
297{
298	OPTION *option;
299
300	option = find_option(optname, OT_SRV);
301
302	if (option != NULL) {
303		return (SERVER *)option->val;
304	} else {
305		rc_log(LOG_CRIT, "rc_conf_srv: unkown config option requested: %s", optname);
306		abort();
307	}
308}
309
310/*
311 * Function: test_config
312 *
313 * Purpose: test the configuration the user supplied
314 *
315 * Returns: 0 on success, -1 when failure
316 */
317
318static int test_config(char *filename)
319{
320#if 0
321	struct stat st;
322	char	    *file;
323#endif
324
325	if (!(rc_conf_srv("authserver")->max))
326	{
327		rc_log(LOG_ERR,"%s: no authserver specified", filename);
328		return (-1);
329	}
330	if (!(rc_conf_srv("acctserver")->max))
331	{
332		rc_log(LOG_ERR,"%s: no acctserver specified", filename);
333		return (-1);
334	}
335	if (!rc_conf_str("servers"))
336	{
337		rc_log(LOG_ERR,"%s: no servers file specified", filename);
338		return (-1);
339	}
340	if (!rc_conf_str("dictionary"))
341	{
342		rc_log(LOG_ERR,"%s: no dictionary specified", filename);
343		return (-1);
344	}
345
346	if (rc_conf_int("radius_timeout") <= 0)
347	{
348		rc_log(LOG_ERR,"%s: radius_timeout <= 0 is illegal", filename);
349		return (-1);
350	}
351	if (rc_conf_int("radius_retries") <= 0)
352	{
353		rc_log(LOG_ERR,"%s: radius_retries <= 0 is illegal", filename);
354		return (-1);
355	}
356
357#if 0
358	file = rc_conf_str("login_local");
359	if (stat(file, &st) == 0)
360	{
361		if (!S_ISREG(st.st_mode)) {
362			rc_log(LOG_ERR,"%s: not a regular file: %s", filename, file);
363			return (-1);
364		}
365	} else {
366		rc_log(LOG_ERR,"%s: file not found: %s", filename, file);
367		return (-1);
368	}
369	file = rc_conf_str("login_radius");
370	if (stat(file, &st) == 0)
371	{
372		if (!S_ISREG(st.st_mode)) {
373			rc_log(LOG_ERR,"%s: not a regular file: %s", filename, file);
374			return (-1);
375		}
376	} else {
377		rc_log(LOG_ERR,"%s: file not found: %s", filename, file);
378		return (-1);
379	}
380#endif
381
382	if (rc_conf_int("login_tries") <= 0)
383	{
384		rc_log(LOG_ERR,"%s: login_tries <= 0 is illegal", filename);
385		return (-1);
386	}
387	if (rc_conf_str("seqfile") == NULL)
388	{
389		rc_log(LOG_ERR,"%s: seqfile not specified", filename);
390		return (-1);
391	}
392	if (rc_conf_int("login_timeout") <= 0)
393	{
394		rc_log(LOG_ERR,"%s: login_timeout <= 0 is illegal", filename);
395		return (-1);
396	}
397	if (rc_conf_str("mapfile") == NULL)
398	{
399		rc_log(LOG_ERR,"%s: mapfile not specified", filename);
400		return (-1);
401	}
402	if (rc_conf_str("nologin") == NULL)
403	{
404		rc_log(LOG_ERR,"%s: nologin not specified", filename);
405		return (-1);
406	}
407
408	return 0;
409}
410
411/*
412 * Function: rc_find_match
413 *
414 * Purpose: see if ip_addr is one of the ip addresses of hostname
415 *
416 * Returns: 0 on success, -1 when failure
417 *
418 */
419
420static int find_match (UINT4 *ip_addr, char *hostname)
421{
422	UINT4           addr;
423	char          **paddr;
424	struct hostent *hp;
425
426	if (rc_good_ipaddr (hostname) == 0)
427	{
428		if (*ip_addr == ntohl(inet_addr (hostname)))
429		{
430			return (0);
431		}
432	}
433	else
434	{
435		if ((hp = gethostbyname (hostname)) == (struct hostent *) NULL)
436		{
437			return (-1);
438		}
439		for (paddr = hp->h_addr_list; *paddr; paddr++)
440		{
441			addr = ** (UINT4 **) paddr;
442			if (ntohl(addr) == *ip_addr)
443			{
444				return (0);
445			}
446		}
447	}
448	return (-1);
449}
450
451/*
452 * Function: rc_find_server
453 *
454 * Purpose: search a server in the servers file
455 *
456 * Returns: 0 on success, -1 on failure
457 *
458 */
459
460int rc_find_server (char *server_name, UINT4 *ip_addr, char *secret)
461{
462	UINT4	myipaddr = 0;
463	int             len;
464	int             result;
465	FILE           *clientfd;
466	char           *h;
467	char           *s;
468	char           *host2;
469	char            buffer[128];
470	char            hostnm[AUTH_ID_LEN + 1];
471
472	/* Get the IP address of the authentication server */
473	if ((*ip_addr = rc_get_ipaddr (server_name)) == (UINT4) 0)
474		return (-1);
475
476	if ((clientfd = fopen (rc_conf_str("servers"), "r")) == (FILE *) NULL)
477	{
478		rc_log(LOG_ERR, "rc_find_server: couldn't open file: %s: %s", strerror(errno), rc_conf_str("servers"));
479		return (-1);
480	}
481
482	myipaddr = rc_own_ipaddress();
483
484	result = 0;
485	while (fgets (buffer, sizeof (buffer), clientfd) != (char *) NULL)
486	{
487		if (*buffer == '#')
488			continue;
489
490		if ((h = strtok (buffer, " \t\n")) == NULL) /* first hostname */
491			continue;
492
493		memset (hostnm, '\0', AUTH_ID_LEN);
494		len = strlen (h);
495		if (len > AUTH_ID_LEN)
496		{
497			len = AUTH_ID_LEN;
498		}
499		strncpy (hostnm, h, (size_t) len);
500		hostnm[AUTH_ID_LEN] = '\0';
501
502		if ((s = strtok (NULL, " \t\n")) == NULL) /* and secret field */
503			continue;
504
505		memset (secret, '\0', MAX_SECRET_LENGTH);
506		len = strlen (s);
507		if (len > MAX_SECRET_LENGTH)
508		{
509			len = MAX_SECRET_LENGTH;
510		}
511		strncpy (secret, s, (size_t) len);
512		secret[MAX_SECRET_LENGTH] = '\0';
513
514		if (!strchr (hostnm, '/')) /* If single name form */
515		{
516			if (find_match (ip_addr, hostnm) == 0)
517			{
518				result++;
519				break;
520			}
521		}
522		else /* <name1>/<name2> "paired" form */
523		{
524			strtok (hostnm, "/");
525			if (find_match (&myipaddr, hostnm) == 0)
526			{	     /* If we're the 1st name, target is 2nd */
527				host2 = strtok (NULL, " ");
528				if (find_match (ip_addr, host2) == 0)
529				{
530					result++;
531					break;
532				}
533			}
534			else	/* If we were 2nd name, target is 1st name */
535			{
536				if (find_match (ip_addr, hostnm) == 0)
537				{
538					result++;
539					break;
540				}
541			}
542		}
543	}
544	fclose (clientfd);
545	if (result == 0)
546	{
547		memset (buffer, '\0', sizeof (buffer));
548		memset (secret, '\0', sizeof (secret));
549		rc_log(LOG_ERR, "rc_find_server: couldn't find RADIUS server %s in %s",
550			 server_name, rc_conf_str("servers"));
551		return (-1);
552	}
553	return 0;
554}
555