Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / heimdal / lib / krb5 / config_file.c
1 /*
2  * Copyright (c) 1997 - 2002 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 2002/09/10 19:04:55 joda 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 int
117 parse_list(FILE *f, unsigned *lineno, krb5_config_binding **parent,
118            const char **error_message)
119 {
120     char buf[BUFSIZ];
121     int 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 int
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     int 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             ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message);
254             if (ret)
255                 goto out;
256         }
257     }
258 out:
259     fclose (f);
260     return ret;
261 }
262
263 krb5_error_code
264 krb5_config_parse_file_multi (krb5_context context,
265                               const char *fname,
266                               krb5_config_section **res)
267 {
268     const char *str;
269     unsigned lineno;
270     krb5_error_code ret;
271
272     ret = krb5_config_parse_file_debug (fname, res, &lineno, &str);
273     if (ret) {
274         krb5_set_error_string (context, "%s:%u: %s", fname, lineno, str);
275         return ret;
276     }
277     return 0;
278 }
279
280 krb5_error_code
281 krb5_config_parse_file (krb5_context context,
282                         const char *fname,
283                         krb5_config_section **res)
284 {
285     *res = NULL;
286     return krb5_config_parse_file_multi(context, fname, res);
287 }
288
289 #endif /* !HAVE_NETINFO */
290
291 static void
292 free_binding (krb5_context context, krb5_config_binding *b)
293 {
294     krb5_config_binding *next_b;
295
296     while (b) {
297         free (b->name);
298         if (b->type == krb5_config_string)
299             free (b->u.string);
300         else if (b->type == krb5_config_list)
301             free_binding (context, b->u.list);
302         else
303             krb5_abortx(context, "unknown binding type (%d) in free_binding", 
304                         b->type);
305         next_b = b->next;
306         free (b);
307         b = next_b;
308     }
309 }
310
311 krb5_error_code
312 krb5_config_file_free (krb5_context context, krb5_config_section *s)
313 {
314     free_binding (context, s);
315     return 0;
316 }
317
318 const void *
319 krb5_config_get_next (krb5_context context,
320                       const krb5_config_section *c,
321                       const krb5_config_binding **pointer,
322                       int type,
323                       ...)
324 {
325     const char *ret;
326     va_list args;
327
328     va_start(args, type);
329     ret = krb5_config_vget_next (context, c, pointer, type, args);
330     va_end(args);
331     return ret;
332 }
333
334 static const void *
335 vget_next(krb5_context context,
336           const krb5_config_binding *b,
337           const krb5_config_binding **pointer,
338           int type,
339           const char *name,
340           va_list args)
341 {
342     const char *p = va_arg(args, const char *);
343     while(b != NULL) {
344         if(strcmp(b->name, name) == 0) {
345             if(b->type == type && p == NULL) {
346                 *pointer = b;
347                 return b->u.generic;
348             } else if(b->type == krb5_config_list && p != NULL) {
349                 return vget_next(context, b->u.list, pointer, type, p, args);
350             }
351         }
352         b = b->next;
353     }
354     return NULL;
355 }
356
357 const void *
358 krb5_config_vget_next (krb5_context context,
359                        const krb5_config_section *c,
360                        const krb5_config_binding **pointer,
361                        int type,
362                        va_list args)
363 {
364     const krb5_config_binding *b;
365     const char *p;
366
367     if(c == NULL)
368         c = context->cf;
369
370     if (c == NULL)
371         return NULL;
372
373     if (*pointer == NULL) {
374         /* first time here, walk down the tree looking for the right
375            section */
376         p = va_arg(args, const char *);
377         if (p == NULL)
378             return NULL;
379         return vget_next(context, c, pointer, type, p, args);
380     }
381
382     /* we were called again, so just look for more entries with the
383        same name and type */
384     for (b = (*pointer)->next; b != NULL; b = b->next) {
385         if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) {
386             *pointer = b;
387             return b->u.generic;
388         }
389     }
390     return NULL;
391 }
392
393 const void *
394 krb5_config_get (krb5_context context,
395                  const krb5_config_section *c,
396                  int type,
397                  ...)
398 {
399     const void *ret;
400     va_list args;
401
402     va_start(args, type);
403     ret = krb5_config_vget (context, c, type, args);
404     va_end(args);
405     return ret;
406 }
407
408 const void *
409 krb5_config_vget (krb5_context context,
410                   const krb5_config_section *c,
411                   int type,
412                   va_list args)
413 {
414     const krb5_config_binding *foo = NULL;
415
416     return krb5_config_vget_next (context, c, &foo, type, args);
417 }
418
419 const krb5_config_binding *
420 krb5_config_get_list (krb5_context context,
421                       const krb5_config_section *c,
422                       ...)
423 {
424     const krb5_config_binding *ret;
425     va_list args;
426
427     va_start(args, c);
428     ret = krb5_config_vget_list (context, c, args);
429     va_end(args);
430     return ret;
431 }
432
433 const krb5_config_binding *
434 krb5_config_vget_list (krb5_context context,
435                        const krb5_config_section *c,
436                        va_list args)
437 {
438     return krb5_config_vget (context, c, krb5_config_list, args);
439 }
440
441 const char *
442 krb5_config_get_string (krb5_context context,
443                         const krb5_config_section *c,
444                         ...)
445 {
446     const char *ret;
447     va_list args;
448
449     va_start(args, c);
450     ret = krb5_config_vget_string (context, c, args);
451     va_end(args);
452     return ret;
453 }
454
455 const char *
456 krb5_config_vget_string (krb5_context context,
457                          const krb5_config_section *c,
458                          va_list args)
459 {
460     return krb5_config_vget (context, c, krb5_config_string, args);
461 }
462
463 const char *
464 krb5_config_vget_string_default (krb5_context context,
465                                  const krb5_config_section *c,
466                                  const char *def_value,
467                                  va_list args)
468 {
469     const char *ret;
470
471     ret = krb5_config_vget_string (context, c, args);
472     if (ret == NULL)
473         ret = def_value;
474     return ret;
475 }
476
477 const char *
478 krb5_config_get_string_default (krb5_context context,
479                                 const krb5_config_section *c,
480                                 const char *def_value,
481                                 ...)
482 {
483     const char *ret;
484     va_list args;
485
486     va_start(args, def_value);
487     ret = krb5_config_vget_string_default (context, c, def_value, args);
488     va_end(args);
489     return ret;
490 }
491
492 char **
493 krb5_config_vget_strings(krb5_context context,
494                          const krb5_config_section *c,
495                          va_list args)
496 {
497     char **strings = NULL;
498     int nstr = 0;
499     const krb5_config_binding *b = NULL;
500     const char *p;
501
502     while((p = krb5_config_vget_next(context, c, &b, 
503                                      krb5_config_string, args))) {
504         char *tmp = strdup(p);
505         char *pos = NULL;
506         char *s;
507         if(tmp == NULL)
508             goto cleanup;
509         s = strtok_r(tmp, " \t", &pos);
510         while(s){
511             char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
512             if(tmp == NULL)
513                 goto cleanup;
514             strings = tmp;
515             strings[nstr] = strdup(s);
516             nstr++;
517             if(strings[nstr-1] == NULL)
518                 goto cleanup;
519             s = strtok_r(NULL, " \t", &pos);
520         }
521         free(tmp);
522     }
523     if(nstr){
524         char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
525         if(strings == NULL)
526             goto cleanup;
527         strings = tmp;
528         strings[nstr] = NULL;
529     }
530     return strings;
531 cleanup:
532     while(nstr--)
533         free(strings[nstr]);
534     free(strings);
535     return NULL;
536
537 }
538
539 char**
540 krb5_config_get_strings(krb5_context context,
541                         const krb5_config_section *c,
542                         ...)
543 {
544     va_list ap;
545     char **ret;
546     va_start(ap, c);
547     ret = krb5_config_vget_strings(context, c, ap);
548     va_end(ap);
549     return ret;
550 }
551
552 void
553 krb5_config_free_strings(char **strings)
554 {
555     char **s = strings;
556     while(s && *s){
557         free(*s);
558         s++;
559     }
560     free(strings);
561 }
562
563 krb5_boolean
564 krb5_config_vget_bool_default (krb5_context context,
565                                const krb5_config_section *c,
566                                krb5_boolean def_value,
567                                va_list args)
568 {
569     const char *str;
570     str = krb5_config_vget_string (context, c, args);
571     if(str == NULL)
572         return def_value;
573     if(strcasecmp(str, "yes") == 0 ||
574        strcasecmp(str, "true") == 0 ||
575        atoi(str)) return TRUE;
576     return FALSE;
577 }
578
579 krb5_boolean
580 krb5_config_vget_bool  (krb5_context context,
581                         const krb5_config_section *c,
582                         va_list args)
583 {
584     return krb5_config_vget_bool_default (context, c, FALSE, args);
585 }
586
587 krb5_boolean
588 krb5_config_get_bool_default (krb5_context context,
589                               const krb5_config_section *c,
590                               krb5_boolean def_value,
591                               ...)
592 {
593     va_list ap;
594     krb5_boolean ret;
595     va_start(ap, def_value);
596     ret = krb5_config_vget_bool_default(context, c, def_value, ap);
597     va_end(ap);
598     return ret;
599 }
600
601 krb5_boolean
602 krb5_config_get_bool (krb5_context context,
603                       const krb5_config_section *c,
604                       ...)
605 {
606     va_list ap;
607     krb5_boolean ret;
608     va_start(ap, c);
609     ret = krb5_config_vget_bool (context, c, ap);
610     va_end(ap);
611     return ret;
612 }
613
614 int
615 krb5_config_vget_time_default (krb5_context context,
616                                const krb5_config_section *c,
617                                int def_value,
618                                va_list args)
619 {
620     const char *str;
621     str = krb5_config_vget_string (context, c, args);
622     if(str == NULL)
623         return def_value;
624     return parse_time (str, NULL);
625 }
626
627 int
628 krb5_config_vget_time  (krb5_context context,
629                         const krb5_config_section *c,
630                         va_list args)
631 {
632     return krb5_config_vget_time_default (context, c, -1, args);
633 }
634
635 int
636 krb5_config_get_time_default (krb5_context context,
637                               const krb5_config_section *c,
638                               int def_value,
639                               ...)
640 {
641     va_list ap;
642     int ret;
643     va_start(ap, def_value);
644     ret = krb5_config_vget_time_default(context, c, def_value, ap);
645     va_end(ap);
646     return ret;
647 }
648
649 int
650 krb5_config_get_time (krb5_context context,
651                       const krb5_config_section *c,
652                       ...)
653 {
654     va_list ap;
655     int ret;
656     va_start(ap, c);
657     ret = krb5_config_vget_time (context, c, ap);
658     va_end(ap);
659     return ret;
660 }
661
662
663 int
664 krb5_config_vget_int_default (krb5_context context,
665                               const krb5_config_section *c,
666                               int def_value,
667                               va_list args)
668 {
669     const char *str;
670     str = krb5_config_vget_string (context, c, args);
671     if(str == NULL)
672         return def_value;
673     else { 
674         char *endptr; 
675         long l; 
676         l = strtol(str, &endptr, 0); 
677         if (endptr == str) 
678             return def_value; 
679         else 
680             return l;
681     }
682 }
683
684 int
685 krb5_config_vget_int  (krb5_context context,
686                        const krb5_config_section *c,
687                        va_list args)
688 {
689     return krb5_config_vget_int_default (context, c, -1, args);
690 }
691
692 int
693 krb5_config_get_int_default (krb5_context context,
694                              const krb5_config_section *c,
695                              int def_value,
696                              ...)
697 {
698     va_list ap;
699     int ret;
700     va_start(ap, def_value);
701     ret = krb5_config_vget_int_default(context, c, def_value, ap);
702     va_end(ap);
703     return ret;
704 }
705
706 int
707 krb5_config_get_int (krb5_context context,
708                      const krb5_config_section *c,
709                      ...)
710 {
711     va_list ap;
712     int ret;
713     va_start(ap, c);
714     ret = krb5_config_vget_int (context, c, ap);
715     va_end(ap);
716     return ret;
717 }