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