xref: /freebsd/crypto/heimdal/lib/krb5/config_file.c (revision 3642298923e528d795e3a30ec165d2b469e28b40)
1 /*
2  * Copyright (c) 1997 - 2003 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.46.4.2 2003/10/13 13:46:10 lha 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 				     const 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 				     const char **error_message);
46 static krb5_error_code parse_list(FILE *f, unsigned *lineno,
47 				  krb5_config_binding **parent,
48 				  const char **error_message);
49 
50 static krb5_config_section *
51 get_entry(krb5_config_section **parent, const char *name, int type)
52 {
53     krb5_config_section **q;
54 
55     for(q = parent; *q != NULL; q = &(*q)->next)
56 	if(type == krb5_config_list &&
57 	   type == (*q)->type &&
58 	   strcmp(name, (*q)->name) == 0)
59 	    return *q;
60     *q = calloc(1, sizeof(**q));
61     if(*q == NULL)
62 	return NULL;
63     (*q)->name = strdup(name);
64     (*q)->type = type;
65     if((*q)->name == NULL) {
66 	free(*q);
67 	*q = NULL;
68 	return NULL;
69     }
70     return *q;
71 }
72 
73 /*
74  * Parse a section:
75  *
76  * [section]
77  *	foo = bar
78  *	b = {
79  *		a
80  *	    }
81  * ...
82  *
83  * starting at the line in `p', storing the resulting structure in
84  * `s' and hooking it into `parent'.
85  * Store the error message in `error_message'.
86  */
87 
88 static krb5_error_code
89 parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
90 	      const char **error_message)
91 {
92     char *p1;
93     krb5_config_section *tmp;
94 
95     p1 = strchr (p + 1, ']');
96     if (p1 == NULL) {
97 	*error_message = "missing ]";
98 	return KRB5_CONFIG_BADFORMAT;
99     }
100     *p1 = '\0';
101     tmp = get_entry(parent, p + 1, krb5_config_list);
102     if(tmp == NULL) {
103 	*error_message = "out of memory";
104 	return KRB5_CONFIG_BADFORMAT;
105     }
106     *s = tmp;
107     return 0;
108 }
109 
110 /*
111  * Parse a brace-enclosed list from `f', hooking in the structure at
112  * `parent'.
113  * Store the error message in `error_message'.
114  */
115 
116 static krb5_error_code
117 parse_list(FILE *f, unsigned *lineno, krb5_config_binding **parent,
118 	   const char **error_message)
119 {
120     char buf[BUFSIZ];
121     krb5_error_code ret;
122     krb5_config_binding *b = NULL;
123     unsigned beg_lineno = *lineno;
124 
125     while(fgets(buf, sizeof(buf), f) != NULL) {
126 	char *p;
127 
128 	++*lineno;
129 	if (buf[strlen(buf) - 1] == '\n')
130 	    buf[strlen(buf) - 1] = '\0';
131 	p = buf;
132 	while(isspace((unsigned char)*p))
133 	    ++p;
134 	if (*p == '#' || *p == ';' || *p == '\0')
135 	    continue;
136 	while(isspace((unsigned char)*p))
137 	    ++p;
138 	if (*p == '}')
139 	    return 0;
140 	if (*p == '\0')
141 	    continue;
142 	ret = parse_binding (f, lineno, p, &b, parent, error_message);
143 	if (ret)
144 	    return ret;
145     }
146     *lineno = beg_lineno;
147     *error_message = "unclosed {";
148     return KRB5_CONFIG_BADFORMAT;
149 }
150 
151 /*
152  *
153  */
154 
155 static krb5_error_code
156 parse_binding(FILE *f, unsigned *lineno, char *p,
157 	      krb5_config_binding **b, krb5_config_binding **parent,
158 	      const char **error_message)
159 {
160     krb5_config_binding *tmp;
161     char *p1, *p2;
162     krb5_error_code ret = 0;
163 
164     p1 = p;
165     while (*p && *p != '=' && !isspace((unsigned char)*p))
166 	++p;
167     if (*p == '\0') {
168 	*error_message = "missing =";
169 	return KRB5_CONFIG_BADFORMAT;
170     }
171     p2 = p;
172     while (isspace((unsigned char)*p))
173 	++p;
174     if (*p != '=') {
175 	*error_message = "missing =";
176 	return KRB5_CONFIG_BADFORMAT;
177     }
178     ++p;
179     while(isspace((unsigned char)*p))
180 	++p;
181     *p2 = '\0';
182     if (*p == '{') {
183 	tmp = get_entry(parent, p1, krb5_config_list);
184 	if (tmp == NULL) {
185 	    *error_message = "out of memory";
186 	    return KRB5_CONFIG_BADFORMAT;
187 	}
188 	ret = parse_list (f, lineno, &tmp->u.list, error_message);
189     } else {
190 	tmp = get_entry(parent, p1, krb5_config_string);
191 	if (tmp == NULL) {
192 	    *error_message = "out of memory";
193 	    return KRB5_CONFIG_BADFORMAT;
194 	}
195 	p1 = p;
196 	p = p1 + strlen(p1);
197 	while(p > p1 && isspace((unsigned char)*(p-1)))
198 	    --p;
199 	*p = '\0';
200 	tmp->u.string = strdup(p1);
201     }
202     *b = tmp;
203     return ret;
204 }
205 
206 /*
207  * Parse the config file `fname', generating the structures into `res'
208  * returning error messages in `error_message'
209  */
210 
211 static krb5_error_code
212 krb5_config_parse_file_debug (const char *fname,
213 			      krb5_config_section **res,
214 			      unsigned *lineno,
215 			      const char **error_message)
216 {
217     FILE *f;
218     krb5_config_section *s;
219     krb5_config_binding *b;
220     char buf[BUFSIZ];
221     krb5_error_code ret = 0;
222 
223     s = NULL;
224     b = NULL;
225     *lineno = 0;
226     f = fopen (fname, "r");
227     if (f == NULL) {
228 	*error_message = "cannot open file";
229 	return ENOENT;
230     }
231     while (fgets(buf, sizeof(buf), f) != NULL) {
232 	char *p;
233 
234 	++*lineno;
235 	if(buf[strlen(buf) - 1] == '\n')
236 	    buf[strlen(buf) - 1] = '\0';
237 	p = buf;
238 	while(isspace((unsigned char)*p))
239 	    ++p;
240 	if (*p == '#' || *p == ';')
241 	    continue;
242 	if (*p == '[') {
243 	    ret = parse_section(p, &s, res, error_message);
244 	    if (ret) {
245 		goto out;
246 	    }
247 	    b = NULL;
248 	} else if (*p == '}') {
249 	    *error_message = "unmatched }";
250 	    ret = EINVAL;	/* XXX */
251 	    goto out;
252 	} else if(*p != '\0') {
253 	    if (s == NULL) {
254 		*error_message = "binding before section";
255 		ret = EINVAL;
256 		goto out;
257 	    }
258 	    ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message);
259 	    if (ret)
260 		goto out;
261 	}
262     }
263 out:
264     fclose (f);
265     return ret;
266 }
267 
268 krb5_error_code
269 krb5_config_parse_file_multi (krb5_context context,
270 			      const char *fname,
271 			      krb5_config_section **res)
272 {
273     const char *str;
274     unsigned lineno;
275     krb5_error_code ret;
276 
277     ret = krb5_config_parse_file_debug (fname, res, &lineno, &str);
278     if (ret) {
279 	krb5_set_error_string (context, "%s:%u: %s", fname, lineno, str);
280 	return ret;
281     }
282     return 0;
283 }
284 
285 krb5_error_code
286 krb5_config_parse_file (krb5_context context,
287 			const char *fname,
288 			krb5_config_section **res)
289 {
290     *res = NULL;
291     return krb5_config_parse_file_multi(context, fname, res);
292 }
293 
294 #endif /* !HAVE_NETINFO */
295 
296 static void
297 free_binding (krb5_context context, krb5_config_binding *b)
298 {
299     krb5_config_binding *next_b;
300 
301     while (b) {
302 	free (b->name);
303 	if (b->type == krb5_config_string)
304 	    free (b->u.string);
305 	else if (b->type == krb5_config_list)
306 	    free_binding (context, b->u.list);
307 	else
308 	    krb5_abortx(context, "unknown binding type (%d) in free_binding",
309 			b->type);
310 	next_b = b->next;
311 	free (b);
312 	b = next_b;
313     }
314 }
315 
316 krb5_error_code
317 krb5_config_file_free (krb5_context context, krb5_config_section *s)
318 {
319     free_binding (context, s);
320     return 0;
321 }
322 
323 const void *
324 krb5_config_get_next (krb5_context context,
325 		      const krb5_config_section *c,
326 		      const krb5_config_binding **pointer,
327 		      int type,
328 		      ...)
329 {
330     const char *ret;
331     va_list args;
332 
333     va_start(args, type);
334     ret = krb5_config_vget_next (context, c, pointer, type, args);
335     va_end(args);
336     return ret;
337 }
338 
339 static const void *
340 vget_next(krb5_context context,
341 	  const krb5_config_binding *b,
342 	  const krb5_config_binding **pointer,
343 	  int type,
344 	  const char *name,
345 	  va_list args)
346 {
347     const char *p = va_arg(args, const char *);
348     while(b != NULL) {
349 	if(strcmp(b->name, name) == 0) {
350 	    if(b->type == type && p == NULL) {
351 		*pointer = b;
352 		return b->u.generic;
353 	    } else if(b->type == krb5_config_list && p != NULL) {
354 		return vget_next(context, b->u.list, pointer, type, p, args);
355 	    }
356 	}
357 	b = b->next;
358     }
359     return NULL;
360 }
361 
362 const void *
363 krb5_config_vget_next (krb5_context context,
364 		       const krb5_config_section *c,
365 		       const krb5_config_binding **pointer,
366 		       int type,
367 		       va_list args)
368 {
369     const krb5_config_binding *b;
370     const char *p;
371 
372     if(c == NULL)
373 	c = context->cf;
374 
375     if (c == NULL)
376 	return NULL;
377 
378     if (*pointer == NULL) {
379 	/* first time here, walk down the tree looking for the right
380            section */
381 	p = va_arg(args, const char *);
382 	if (p == NULL)
383 	    return NULL;
384 	return vget_next(context, c, pointer, type, p, args);
385     }
386 
387     /* we were called again, so just look for more entries with the
388        same name and type */
389     for (b = (*pointer)->next; b != NULL; b = b->next) {
390 	if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) {
391 	    *pointer = b;
392 	    return b->u.generic;
393 	}
394     }
395     return NULL;
396 }
397 
398 const void *
399 krb5_config_get (krb5_context context,
400 		 const krb5_config_section *c,
401 		 int type,
402 		 ...)
403 {
404     const void *ret;
405     va_list args;
406 
407     va_start(args, type);
408     ret = krb5_config_vget (context, c, type, args);
409     va_end(args);
410     return ret;
411 }
412 
413 const void *
414 krb5_config_vget (krb5_context context,
415 		  const krb5_config_section *c,
416 		  int type,
417 		  va_list args)
418 {
419     const krb5_config_binding *foo = NULL;
420 
421     return krb5_config_vget_next (context, c, &foo, type, args);
422 }
423 
424 const krb5_config_binding *
425 krb5_config_get_list (krb5_context context,
426 		      const krb5_config_section *c,
427 		      ...)
428 {
429     const krb5_config_binding *ret;
430     va_list args;
431 
432     va_start(args, c);
433     ret = krb5_config_vget_list (context, c, args);
434     va_end(args);
435     return ret;
436 }
437 
438 const krb5_config_binding *
439 krb5_config_vget_list (krb5_context context,
440 		       const krb5_config_section *c,
441 		       va_list args)
442 {
443     return krb5_config_vget (context, c, krb5_config_list, args);
444 }
445 
446 const char *
447 krb5_config_get_string (krb5_context context,
448 			const krb5_config_section *c,
449 			...)
450 {
451     const char *ret;
452     va_list args;
453 
454     va_start(args, c);
455     ret = krb5_config_vget_string (context, c, args);
456     va_end(args);
457     return ret;
458 }
459 
460 const char *
461 krb5_config_vget_string (krb5_context context,
462 			 const krb5_config_section *c,
463 			 va_list args)
464 {
465     return krb5_config_vget (context, c, krb5_config_string, args);
466 }
467 
468 const char *
469 krb5_config_vget_string_default (krb5_context context,
470 				 const krb5_config_section *c,
471 				 const char *def_value,
472 				 va_list args)
473 {
474     const char *ret;
475 
476     ret = krb5_config_vget_string (context, c, args);
477     if (ret == NULL)
478 	ret = def_value;
479     return ret;
480 }
481 
482 const char *
483 krb5_config_get_string_default (krb5_context context,
484 				const krb5_config_section *c,
485 				const char *def_value,
486 				...)
487 {
488     const char *ret;
489     va_list args;
490 
491     va_start(args, def_value);
492     ret = krb5_config_vget_string_default (context, c, def_value, args);
493     va_end(args);
494     return ret;
495 }
496 
497 char **
498 krb5_config_vget_strings(krb5_context context,
499 			 const krb5_config_section *c,
500 			 va_list args)
501 {
502     char **strings = NULL;
503     int nstr = 0;
504     const krb5_config_binding *b = NULL;
505     const char *p;
506 
507     while((p = krb5_config_vget_next(context, c, &b,
508 				     krb5_config_string, args))) {
509 	char *tmp = strdup(p);
510 	char *pos = NULL;
511 	char *s;
512 	if(tmp == NULL)
513 	    goto cleanup;
514 	s = strtok_r(tmp, " \t", &pos);
515 	while(s){
516 	    char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
517 	    if(tmp == NULL)
518 		goto cleanup;
519 	    strings = tmp;
520 	    strings[nstr] = strdup(s);
521 	    nstr++;
522 	    if(strings[nstr-1] == NULL)
523 		goto cleanup;
524 	    s = strtok_r(NULL, " \t", &pos);
525 	}
526 	free(tmp);
527     }
528     if(nstr){
529 	char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
530 	if(strings == NULL)
531 	    goto cleanup;
532 	strings = tmp;
533 	strings[nstr] = NULL;
534     }
535     return strings;
536 cleanup:
537     while(nstr--)
538 	free(strings[nstr]);
539     free(strings);
540     return NULL;
541 
542 }
543 
544 char**
545 krb5_config_get_strings(krb5_context context,
546 			const krb5_config_section *c,
547 			...)
548 {
549     va_list ap;
550     char **ret;
551     va_start(ap, c);
552     ret = krb5_config_vget_strings(context, c, ap);
553     va_end(ap);
554     return ret;
555 }
556 
557 void
558 krb5_config_free_strings(char **strings)
559 {
560     char **s = strings;
561     while(s && *s){
562 	free(*s);
563 	s++;
564     }
565     free(strings);
566 }
567 
568 krb5_boolean
569 krb5_config_vget_bool_default (krb5_context context,
570 			       const krb5_config_section *c,
571 			       krb5_boolean def_value,
572 			       va_list args)
573 {
574     const char *str;
575     str = krb5_config_vget_string (context, c, args);
576     if(str == NULL)
577 	return def_value;
578     if(strcasecmp(str, "yes") == 0 ||
579        strcasecmp(str, "true") == 0 ||
580        atoi(str)) return TRUE;
581     return FALSE;
582 }
583 
584 krb5_boolean
585 krb5_config_vget_bool  (krb5_context context,
586 			const krb5_config_section *c,
587 			va_list args)
588 {
589     return krb5_config_vget_bool_default (context, c, FALSE, args);
590 }
591 
592 krb5_boolean
593 krb5_config_get_bool_default (krb5_context context,
594 			      const krb5_config_section *c,
595 			      krb5_boolean def_value,
596 			      ...)
597 {
598     va_list ap;
599     krb5_boolean ret;
600     va_start(ap, def_value);
601     ret = krb5_config_vget_bool_default(context, c, def_value, ap);
602     va_end(ap);
603     return ret;
604 }
605 
606 krb5_boolean
607 krb5_config_get_bool (krb5_context context,
608 		      const krb5_config_section *c,
609 		      ...)
610 {
611     va_list ap;
612     krb5_boolean ret;
613     va_start(ap, c);
614     ret = krb5_config_vget_bool (context, c, ap);
615     va_end(ap);
616     return ret;
617 }
618 
619 int
620 krb5_config_vget_time_default (krb5_context context,
621 			       const krb5_config_section *c,
622 			       int def_value,
623 			       va_list args)
624 {
625     const char *str;
626     str = krb5_config_vget_string (context, c, args);
627     if(str == NULL)
628 	return def_value;
629     return parse_time (str, NULL);
630 }
631 
632 int
633 krb5_config_vget_time  (krb5_context context,
634 			const krb5_config_section *c,
635 			va_list args)
636 {
637     return krb5_config_vget_time_default (context, c, -1, args);
638 }
639 
640 int
641 krb5_config_get_time_default (krb5_context context,
642 			      const krb5_config_section *c,
643 			      int def_value,
644 			      ...)
645 {
646     va_list ap;
647     int ret;
648     va_start(ap, def_value);
649     ret = krb5_config_vget_time_default(context, c, def_value, ap);
650     va_end(ap);
651     return ret;
652 }
653 
654 int
655 krb5_config_get_time (krb5_context context,
656 		      const krb5_config_section *c,
657 		      ...)
658 {
659     va_list ap;
660     int ret;
661     va_start(ap, c);
662     ret = krb5_config_vget_time (context, c, ap);
663     va_end(ap);
664     return ret;
665 }
666 
667 
668 int
669 krb5_config_vget_int_default (krb5_context context,
670 			      const krb5_config_section *c,
671 			      int def_value,
672 			      va_list args)
673 {
674     const char *str;
675     str = krb5_config_vget_string (context, c, args);
676     if(str == NULL)
677 	return def_value;
678     else {
679 	char *endptr;
680 	long l;
681 	l = strtol(str, &endptr, 0);
682 	if (endptr == str)
683 	    return def_value;
684 	else
685 	    return l;
686     }
687 }
688 
689 int
690 krb5_config_vget_int  (krb5_context context,
691 		       const krb5_config_section *c,
692 		       va_list args)
693 {
694     return krb5_config_vget_int_default (context, c, -1, args);
695 }
696 
697 int
698 krb5_config_get_int_default (krb5_context context,
699 			     const krb5_config_section *c,
700 			     int def_value,
701 			     ...)
702 {
703     va_list ap;
704     int ret;
705     va_start(ap, def_value);
706     ret = krb5_config_vget_int_default(context, c, def_value, ap);
707     va_end(ap);
708     return ret;
709 }
710 
711 int
712 krb5_config_get_int (krb5_context context,
713 		     const krb5_config_section *c,
714 		     ...)
715 {
716     va_list ap;
717     int ret;
718     va_start(ap, c);
719     ret = krb5_config_vget_int (context, c, ap);
720     va_end(ap);
721     return ret;
722 }
723