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