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