xref: /freebsd/crypto/heimdal/lib/krb5/config_file.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*
2  * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "krb5_locl.h"
35 RCSID("$Id: config_file.c,v 1.42 2001/05/14 06:14:45 assar Exp $");
36 
37 #ifndef HAVE_NETINFO
38 
39 static krb5_error_code parse_section(char *p, krb5_config_section **s,
40 				     krb5_config_section **res,
41 				     char **error_message);
42 static krb5_error_code parse_binding(FILE *f, unsigned *lineno, char *p,
43 				     krb5_config_binding **b,
44 				     krb5_config_binding **parent,
45 				     char **error_message);
46 static krb5_error_code parse_list(FILE *f, unsigned *lineno,
47 				  krb5_config_binding **parent,
48 				  char **error_message);
49 
50 /*
51  * Parse a section:
52  *
53  * [section]
54  *	foo = bar
55  *	b = {
56  *		a
57  *	    }
58  * ...
59  *
60  * starting at the line in `p', storing the resulting structure in
61  * `s' and hooking it into `parent'.
62  * Store the error message in `error_message'.
63  */
64 
65 static krb5_error_code
66 parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
67 	      char **error_message)
68 {
69     char *p1;
70     krb5_config_section *tmp;
71 
72     p1 = strchr (p + 1, ']');
73     if (p1 == NULL) {
74 	*error_message = "missing ]";
75 	return KRB5_CONFIG_BADFORMAT;
76     }
77     *p1 = '\0';
78     tmp = malloc(sizeof(*tmp));
79     if (tmp == NULL) {
80 	*error_message = "out of memory";
81 	return KRB5_CONFIG_BADFORMAT;
82     }
83     tmp->name = strdup(p+1);
84     if (tmp->name == NULL) {
85 	*error_message = "out of memory";
86 	return KRB5_CONFIG_BADFORMAT;
87     }
88     tmp->type = krb5_config_list;
89     tmp->u.list = NULL;
90     tmp->next = NULL;
91     if (*s)
92 	(*s)->next = tmp;
93     else
94 	*parent = tmp;
95     *s = tmp;
96     return 0;
97 }
98 
99 /*
100  * Parse a brace-enclosed list from `f', hooking in the structure at
101  * `parent'.
102  * Store the error message in `error_message'.
103  */
104 
105 static int
106 parse_list(FILE *f, unsigned *lineno, krb5_config_binding **parent,
107 	   char **error_message)
108 {
109     char buf[BUFSIZ];
110     int ret;
111     krb5_config_binding *b = NULL;
112     unsigned beg_lineno = *lineno;
113 
114     while(fgets(buf, sizeof(buf), f) != NULL) {
115 	char *p;
116 
117 	++*lineno;
118 	if (buf[strlen(buf) - 1] == '\n')
119 	    buf[strlen(buf) - 1] = '\0';
120 	p = buf;
121 	while(isspace((unsigned char)*p))
122 	    ++p;
123 	if (*p == '#' || *p == ';' || *p == '\0')
124 	    continue;
125 	while(isspace((unsigned char)*p))
126 	    ++p;
127 	if (*p == '}')
128 	    return 0;
129 	if (*p == '\0')
130 	    continue;
131 	ret = parse_binding (f, lineno, p, &b, parent, error_message);
132 	if (ret)
133 	    return ret;
134     }
135     *lineno = beg_lineno;
136     *error_message = "unclosed {";
137     return KRB5_CONFIG_BADFORMAT;
138 }
139 
140 /*
141  *
142  */
143 
144 static int
145 parse_binding(FILE *f, unsigned *lineno, char *p,
146 	      krb5_config_binding **b, krb5_config_binding **parent,
147 	      char **error_message)
148 {
149     krb5_config_binding *tmp;
150     char *p1, *p2;
151     int ret = 0;
152 
153     p1 = p;
154     while (*p && *p != '=' && !isspace((unsigned char)*p))
155 	++p;
156     if (*p == '\0') {
157 	*error_message = "no =";
158 	return KRB5_CONFIG_BADFORMAT;
159     }
160     p2 = p;
161     while (isspace((unsigned char)*p))
162 	++p;
163     if (*p != '=') {
164 	*error_message = "no =";
165 	return KRB5_CONFIG_BADFORMAT;
166     }
167     ++p;
168     while(isspace((unsigned char)*p))
169 	++p;
170     tmp = malloc(sizeof(*tmp));
171     if (tmp == NULL) {
172 	*error_message = "out of memory";
173 	return KRB5_CONFIG_BADFORMAT;
174     }
175     *p2 = '\0';
176     tmp->name = strdup(p1);
177     tmp->next = NULL;
178     if (*p == '{') {
179 	tmp->type = krb5_config_list;
180 	tmp->u.list = NULL;
181 	ret = parse_list (f, lineno, &tmp->u.list, error_message);
182     } else {
183 	p1 = p;
184 	p = p1 + strlen(p1);
185 	while(p > p1 && isspace((unsigned char)*(p-1)))
186 	    --p;
187 	*p = '\0';
188 	tmp->type = krb5_config_string;
189 	tmp->u.string = strdup(p1);
190     }
191     if (*b)
192 	(*b)->next = tmp;
193     else
194 	*parent = tmp;
195     *b = tmp;
196     return ret;
197 }
198 
199 /*
200  * Parse the config file `fname', generating the structures into `res'
201  * returning error messages in `error_message'
202  */
203 
204 static krb5_error_code
205 krb5_config_parse_file_debug (const char *fname,
206 			      krb5_config_section **res,
207 			      unsigned *lineno,
208 			      char **error_message)
209 {
210     FILE *f;
211     krb5_config_section *s;
212     krb5_config_binding *b;
213     char buf[BUFSIZ];
214     krb5_error_code ret = 0;
215 
216     s = NULL;
217     b = NULL;
218     *lineno = 0;
219     f = fopen (fname, "r");
220     if (f == NULL) {
221 	*error_message = "cannot open file";
222 	return ENOENT;
223     }
224     *res = NULL;
225     while (fgets(buf, sizeof(buf), f) != NULL) {
226 	char *p;
227 
228 	++*lineno;
229 	if(buf[strlen(buf) - 1] == '\n')
230 	    buf[strlen(buf) - 1] = '\0';
231 	p = buf;
232 	while(isspace((unsigned char)*p))
233 	    ++p;
234 	if (*p == '#' || *p == ';')
235 	    continue;
236 	if (*p == '[') {
237 	    ret = parse_section(p, &s, res, error_message);
238 	    if (ret) {
239 		goto out;
240 	    }
241 	    b = NULL;
242 	} else if (*p == '}') {
243 	    *error_message = "unmatched }";
244 	    ret = EINVAL;	/* XXX */
245 	    goto out;
246 	} else if(*p != '\0') {
247 	    ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message);
248 	    if (ret)
249 		goto out;
250 	}
251     }
252 out:
253     fclose (f);
254     return ret;
255 }
256 
257 krb5_error_code
258 krb5_config_parse_file (krb5_context context,
259 			const char *fname,
260 			krb5_config_section **res)
261 {
262     char *str;
263     unsigned lineno;
264     krb5_error_code ret;
265 
266     ret = krb5_config_parse_file_debug (fname, res, &lineno, &str);
267     if (ret) {
268 	krb5_set_error_string (context, "%s:%u: %s", fname, lineno, str);
269 	return ret;
270     }
271     return 0;
272 }
273 
274 #endif /* !HAVE_NETINFO */
275 
276 static void
277 free_binding (krb5_context context, krb5_config_binding *b)
278 {
279     krb5_config_binding *next_b;
280 
281     while (b) {
282 	free (b->name);
283 	if (b->type == krb5_config_string)
284 	    free (b->u.string);
285 	else if (b->type == krb5_config_list)
286 	    free_binding (context, b->u.list);
287 	else
288 	    krb5_abortx(context, "unknown binding type (%d) in free_binding",
289 			b->type);
290 	next_b = b->next;
291 	free (b);
292 	b = next_b;
293     }
294 }
295 
296 krb5_error_code
297 krb5_config_file_free (krb5_context context, krb5_config_section *s)
298 {
299     free_binding (context, s);
300     return 0;
301 }
302 
303 const void *
304 krb5_config_get_next (krb5_context context,
305 		      krb5_config_section *c,
306 		      krb5_config_binding **pointer,
307 		      int type,
308 		      ...)
309 {
310     const char *ret;
311     va_list args;
312 
313     va_start(args, type);
314     ret = krb5_config_vget_next (context, c, pointer, type, args);
315     va_end(args);
316     return ret;
317 }
318 
319 const void *
320 krb5_config_vget_next (krb5_context context,
321 		       krb5_config_section *c,
322 		       krb5_config_binding **pointer,
323 		       int type,
324 		       va_list args)
325 {
326     krb5_config_binding *b;
327     const char *p;
328 
329     if(c == NULL)
330 	c = context->cf;
331 
332     if (c == NULL)
333 	return NULL;
334 
335     if (*pointer == NULL) {
336 	b = (c != NULL) ? c : context->cf;
337 	p = va_arg(args, const char *);
338 	if (p == NULL)
339 	    return NULL;
340     } else {
341 	b = *pointer;
342 	p = b->name;
343 	b = b->next;
344     }
345 
346     while (b) {
347 	if (strcmp (b->name, p) == 0) {
348 	    if (*pointer == NULL)
349 		p = va_arg(args, const char *);
350 	    else
351 		p = NULL;
352 	    if (type == b->type && p == NULL) {
353 		*pointer = b;
354 		return b->u.generic;
355 	    } else if(b->type == krb5_config_list && p != NULL) {
356 		b = b->u.list;
357 	    } else {
358 		return NULL;
359 	    }
360 	} else {
361 	    b = b->next;
362 	}
363     }
364     return NULL;
365 }
366 
367 const void *
368 krb5_config_get (krb5_context context,
369 		 krb5_config_section *c,
370 		 int type,
371 		 ...)
372 {
373     const void *ret;
374     va_list args;
375 
376     va_start(args, type);
377     ret = krb5_config_vget (context, c, type, args);
378     va_end(args);
379     return ret;
380 }
381 
382 const void *
383 krb5_config_vget (krb5_context context,
384 		  krb5_config_section *c,
385 		  int type,
386 		  va_list args)
387 {
388     krb5_config_binding *foo = NULL;
389 
390     return krb5_config_vget_next (context, c, &foo, type, args);
391 }
392 
393 const krb5_config_binding *
394 krb5_config_get_list (krb5_context context,
395 		      krb5_config_section *c,
396 		      ...)
397 {
398     const krb5_config_binding *ret;
399     va_list args;
400 
401     va_start(args, c);
402     ret = krb5_config_vget_list (context, c, args);
403     va_end(args);
404     return ret;
405 }
406 
407 const krb5_config_binding *
408 krb5_config_vget_list (krb5_context context,
409 		       krb5_config_section *c,
410 		       va_list args)
411 {
412     return krb5_config_vget (context, c, krb5_config_list, args);
413 }
414 
415 const char *
416 krb5_config_get_string (krb5_context context,
417 			krb5_config_section *c,
418 			...)
419 {
420     const char *ret;
421     va_list args;
422 
423     va_start(args, c);
424     ret = krb5_config_vget_string (context, c, args);
425     va_end(args);
426     return ret;
427 }
428 
429 const char *
430 krb5_config_vget_string (krb5_context context,
431 			 krb5_config_section *c,
432 			 va_list args)
433 {
434     return krb5_config_vget (context, c, krb5_config_string, args);
435 }
436 
437 const char *
438 krb5_config_vget_string_default (krb5_context context,
439 				 krb5_config_section *c,
440 				 const char *def_value,
441 				 va_list args)
442 {
443     const char *ret;
444 
445     ret = krb5_config_vget_string (context, c, args);
446     if (ret == NULL)
447 	ret = def_value;
448     return ret;
449 }
450 
451 const char *
452 krb5_config_get_string_default (krb5_context context,
453 				krb5_config_section *c,
454 				const char *def_value,
455 				...)
456 {
457     const char *ret;
458     va_list args;
459 
460     va_start(args, def_value);
461     ret = krb5_config_vget_string_default (context, c, def_value, args);
462     va_end(args);
463     return ret;
464 }
465 
466 char **
467 krb5_config_vget_strings(krb5_context context,
468 			 krb5_config_section *c,
469 			 va_list args)
470 {
471     char **strings = NULL;
472     int nstr = 0;
473     krb5_config_binding *b = NULL;
474     const char *p;
475 
476     while((p = krb5_config_vget_next(context, c, &b,
477 				     krb5_config_string, args))) {
478 	char *tmp = strdup(p);
479 	char *pos = NULL;
480 	char *s;
481 	if(tmp == NULL)
482 	    goto cleanup;
483 	s = strtok_r(tmp, " \t", &pos);
484 	while(s){
485 	    char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
486 	    if(tmp == NULL)
487 		goto cleanup;
488 	    strings = tmp;
489 	    strings[nstr] = strdup(s);
490 	    nstr++;
491 	    if(strings[nstr-1] == NULL)
492 		goto cleanup;
493 	    s = strtok_r(NULL, " \t", &pos);
494 	}
495 	free(tmp);
496     }
497     if(nstr){
498 	char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
499 	if(strings == NULL)
500 	    goto cleanup;
501 	strings = tmp;
502 	strings[nstr] = NULL;
503     }
504     return strings;
505 cleanup:
506     while(nstr--)
507 	free(strings[nstr]);
508     free(strings);
509     return NULL;
510 
511 }
512 
513 char**
514 krb5_config_get_strings(krb5_context context,
515 			krb5_config_section *c,
516 			...)
517 {
518     va_list ap;
519     char **ret;
520     va_start(ap, c);
521     ret = krb5_config_vget_strings(context, c, ap);
522     va_end(ap);
523     return ret;
524 }
525 
526 void
527 krb5_config_free_strings(char **strings)
528 {
529     char **s = strings;
530     while(s && *s){
531 	free(*s);
532 	s++;
533     }
534     free(strings);
535 }
536 
537 krb5_boolean
538 krb5_config_vget_bool_default (krb5_context context,
539 			       krb5_config_section *c,
540 			       krb5_boolean def_value,
541 			       va_list args)
542 {
543     const char *str;
544     str = krb5_config_vget_string (context, c, args);
545     if(str == NULL)
546 	return def_value;
547     if(strcasecmp(str, "yes") == 0 ||
548        strcasecmp(str, "true") == 0 ||
549        atoi(str)) return TRUE;
550     return FALSE;
551 }
552 
553 krb5_boolean
554 krb5_config_vget_bool  (krb5_context context,
555 			krb5_config_section *c,
556 			va_list args)
557 {
558     return krb5_config_vget_bool_default (context, c, FALSE, args);
559 }
560 
561 krb5_boolean
562 krb5_config_get_bool_default (krb5_context context,
563 			      krb5_config_section *c,
564 			      krb5_boolean def_value,
565 			      ...)
566 {
567     va_list ap;
568     krb5_boolean ret;
569     va_start(ap, def_value);
570     ret = krb5_config_vget_bool_default(context, c, def_value, ap);
571     va_end(ap);
572     return ret;
573 }
574 
575 krb5_boolean
576 krb5_config_get_bool (krb5_context context,
577 		      krb5_config_section *c,
578 		      ...)
579 {
580     va_list ap;
581     krb5_boolean ret;
582     va_start(ap, c);
583     ret = krb5_config_vget_bool (context, c, ap);
584     va_end(ap);
585     return ret;
586 }
587 
588 int
589 krb5_config_vget_time_default (krb5_context context,
590 			       krb5_config_section *c,
591 			       int def_value,
592 			       va_list args)
593 {
594     const char *str;
595     str = krb5_config_vget_string (context, c, args);
596     if(str == NULL)
597 	return def_value;
598     return parse_time (str, NULL);
599 }
600 
601 int
602 krb5_config_vget_time  (krb5_context context,
603 			krb5_config_section *c,
604 			va_list args)
605 {
606     return krb5_config_vget_time_default (context, c, -1, args);
607 }
608 
609 int
610 krb5_config_get_time_default (krb5_context context,
611 			      krb5_config_section *c,
612 			      int def_value,
613 			      ...)
614 {
615     va_list ap;
616     int ret;
617     va_start(ap, def_value);
618     ret = krb5_config_vget_time_default(context, c, def_value, ap);
619     va_end(ap);
620     return ret;
621 }
622 
623 int
624 krb5_config_get_time (krb5_context context,
625 		      krb5_config_section *c,
626 		      ...)
627 {
628     va_list ap;
629     int ret;
630     va_start(ap, c);
631     ret = krb5_config_vget_time (context, c, ap);
632     va_end(ap);
633     return ret;
634 }
635 
636 
637 int
638 krb5_config_vget_int_default (krb5_context context,
639 			      krb5_config_section *c,
640 			      int def_value,
641 			      va_list args)
642 {
643     const char *str;
644     str = krb5_config_vget_string (context, c, args);
645     if(str == NULL)
646 	return def_value;
647     else {
648 	char *endptr;
649 	long l;
650 	l = strtol(str, &endptr, 0);
651 	if (endptr == str)
652 	    return def_value;
653 	else
654 	    return l;
655     }
656 }
657 
658 int
659 krb5_config_vget_int  (krb5_context context,
660 		       krb5_config_section *c,
661 		       va_list args)
662 {
663     return krb5_config_vget_int_default (context, c, -1, args);
664 }
665 
666 int
667 krb5_config_get_int_default (krb5_context context,
668 			     krb5_config_section *c,
669 			     int def_value,
670 			     ...)
671 {
672     va_list ap;
673     int ret;
674     va_start(ap, def_value);
675     ret = krb5_config_vget_int_default(context, c, def_value, ap);
676     va_end(ap);
677     return ret;
678 }
679 
680 int
681 krb5_config_get_int (krb5_context context,
682 		     krb5_config_section *c,
683 		     ...)
684 {
685     va_list ap;
686     int ret;
687     va_start(ap, c);
688     ret = krb5_config_vget_int (context, c, ap);
689     va_end(ap);
690     return ret;
691 }
692 
693 #ifdef TEST
694 
695 static int print_list (krb5_context context, FILE *f,
696 		       krb5_config_binding *l, unsigned level);
697 static int print_binding (krb5_context context, FILE *f,
698 			  krb5_config_binding *b, unsigned level);
699 static int print_section (krb5_context context, FILE *f,
700 			  krb5_config_section *s, unsigned level);
701 static int print_config (krb5_context context, FILE *f,
702 			 krb5_config_section *c);
703 
704 static void
705 tab (FILE *f, unsigned count)
706 {
707     while(count--)
708 	fprintf (f, "\t");
709 }
710 
711 static int
712 print_list (krb5_context context,
713 	    FILE *f,
714 	    krb5_config_binding *l,
715 	    unsigned level)
716 {
717     while(l) {
718 	print_binding (context, f, l, level);
719 	l = l->next;
720     }
721     return 0;
722 }
723 
724 static int
725 print_binding (krb5_context context,
726 	       FILE *f,
727 	       krb5_config_binding *b,
728 	       unsigned level)
729 {
730     tab (f, level);
731     fprintf (f, "%s = ", b->name);
732     if (b->type == krb5_config_string)
733 	fprintf (f, "%s\n", b->u.string);
734     else if (b->type == krb5_config_list) {
735 	fprintf (f, "{\n");
736 	print_list (f, b->u.list, level + 1);
737 	tab (f, level);
738 	fprintf (f, "}\n");
739     } else
740 	krb5_abortx(context, "unknown binding type (%d) in print_binding",
741 		    b->type);
742     return 0;
743 }
744 
745 static int
746 print_section (FILE *f, krb5_config_section *s, unsigned level)
747 {
748     fprintf (f, "[%s]\n", s->name);
749     print_list (f, s->u.list, level + 1);
750     return 0;
751 }
752 
753 static int
754 print_config (FILE *f, krb5_config_section *c)
755 {
756     while (c) {
757 	print_section (f, c, 0);
758 	c = c->next;
759     }
760     return 0;
761 }
762 
763 
764 int
765 main(void)
766 {
767     krb5_config_section *c;
768 
769     printf ("%d\n", krb5_config_parse_file ("/etc/krb5.conf", &c));
770     print_config (stdout, c);
771     printf ("[libdefaults]ticket_lifetime = %s\n",
772 	    krb5_config_get_string (context, c,
773 			       "libdefaults",
774 			       "ticket_lifetime",
775 			       NULL));
776     printf ("[realms]foo = %s\n",
777 	    krb5_config_get_string (context, c,
778 			       "realms",
779 			       "foo",
780 			       NULL));
781     printf ("[realms]ATHENA.MIT.EDU/v4_instance_convert/lithium = %s\n",
782 	    krb5_config_get_string (context, c,
783 			       "realms",
784 			       "ATHENA.MIT.EDU",
785 			       "v4_instance_convert",
786 			       "lithium",
787 			       NULL));
788     return 0;
789 }
790 
791 #endif /* TEST */
792