Import of code from heimdal
[openafs.git] / src / external / heimdal / 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  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #define KRB5_DEPRECATED
37
38 #include "krb5_locl.h"
39
40 #ifdef __APPLE__
41 #include <CoreFoundation/CoreFoundation.h>
42 #endif
43
44 /* Gaah! I want a portable funopen */
45 struct fileptr {
46     const char *s;
47     FILE *f;
48 };
49
50 static char *
51 config_fgets(char *str, size_t len, struct fileptr *ptr)
52 {
53     /* XXX this is not correct, in that they don't do the same if the
54        line is longer than len */
55     if(ptr->f != NULL)
56         return fgets(str, len, ptr->f);
57     else {
58         /* this is almost strsep_copy */
59         const char *p;
60         ssize_t l;
61         if(*ptr->s == '\0')
62             return NULL;
63         p = ptr->s + strcspn(ptr->s, "\n");
64         if(*p == '\n')
65             p++;
66         l = min(len, p - ptr->s);
67         if(len > 0) {
68             memcpy(str, ptr->s, l);
69             str[l] = '\0';
70         }
71         ptr->s = p;
72         return str;
73     }
74 }
75
76 static krb5_error_code parse_section(char *p, krb5_config_section **s,
77                                      krb5_config_section **res,
78                                      const char **error_message);
79 static krb5_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
80                                      krb5_config_binding **b,
81                                      krb5_config_binding **parent,
82                                      const char **error_message);
83 static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
84                                   krb5_config_binding **parent,
85                                   const char **error_message);
86
87 static krb5_config_section *
88 get_entry(krb5_config_section **parent, const char *name, int type)
89 {
90     krb5_config_section **q;
91
92     for(q = parent; *q != NULL; q = &(*q)->next)
93         if(type == krb5_config_list &&
94            type == (*q)->type &&
95            strcmp(name, (*q)->name) == 0)
96             return *q;
97     *q = calloc(1, sizeof(**q));
98     if(*q == NULL)
99         return NULL;
100     (*q)->name = strdup(name);
101     (*q)->type = type;
102     if((*q)->name == NULL) {
103         free(*q);
104         *q = NULL;
105         return NULL;
106     }
107     return *q;
108 }
109
110 /*
111  * Parse a section:
112  *
113  * [section]
114  *      foo = bar
115  *      b = {
116  *              a
117  *          }
118  * ...
119  *
120  * starting at the line in `p', storing the resulting structure in
121  * `s' and hooking it into `parent'.
122  * Store the error message in `error_message'.
123  */
124
125 static krb5_error_code
126 parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
127               const char **error_message)
128 {
129     char *p1;
130     krb5_config_section *tmp;
131
132     p1 = strchr (p + 1, ']');
133     if (p1 == NULL) {
134         *error_message = "missing ]";
135         return KRB5_CONFIG_BADFORMAT;
136     }
137     *p1 = '\0';
138     tmp = get_entry(parent, p + 1, krb5_config_list);
139     if(tmp == NULL) {
140         *error_message = "out of memory";
141         return KRB5_CONFIG_BADFORMAT;
142     }
143     *s = tmp;
144     return 0;
145 }
146
147 /*
148  * Parse a brace-enclosed list from `f', hooking in the structure at
149  * `parent'.
150  * Store the error message in `error_message'.
151  */
152
153 static krb5_error_code
154 parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent,
155            const char **error_message)
156 {
157     char buf[BUFSIZ];
158     krb5_error_code ret;
159     krb5_config_binding *b = NULL;
160     unsigned beg_lineno = *lineno;
161
162     while(config_fgets(buf, sizeof(buf), f) != NULL) {
163         char *p;
164
165         ++*lineno;
166         buf[strcspn(buf, "\r\n")] = '\0';
167         p = buf;
168         while(isspace((unsigned char)*p))
169             ++p;
170         if (*p == '#' || *p == ';' || *p == '\0')
171             continue;
172         while(isspace((unsigned char)*p))
173             ++p;
174         if (*p == '}')
175             return 0;
176         if (*p == '\0')
177             continue;
178         ret = parse_binding (f, lineno, p, &b, parent, error_message);
179         if (ret)
180             return ret;
181     }
182     *lineno = beg_lineno;
183     *error_message = "unclosed {";
184     return KRB5_CONFIG_BADFORMAT;
185 }
186
187 /*
188  *
189  */
190
191 static krb5_error_code
192 parse_binding(struct fileptr *f, unsigned *lineno, char *p,
193               krb5_config_binding **b, krb5_config_binding **parent,
194               const char **error_message)
195 {
196     krb5_config_binding *tmp;
197     char *p1, *p2;
198     krb5_error_code ret = 0;
199
200     p1 = p;
201     while (*p && *p != '=' && !isspace((unsigned char)*p))
202         ++p;
203     if (*p == '\0') {
204         *error_message = "missing =";
205         return KRB5_CONFIG_BADFORMAT;
206     }
207     p2 = p;
208     while (isspace((unsigned char)*p))
209         ++p;
210     if (*p != '=') {
211         *error_message = "missing =";
212         return KRB5_CONFIG_BADFORMAT;
213     }
214     ++p;
215     while(isspace((unsigned char)*p))
216         ++p;
217     *p2 = '\0';
218     if (*p == '{') {
219         tmp = get_entry(parent, p1, krb5_config_list);
220         if (tmp == NULL) {
221             *error_message = "out of memory";
222             return KRB5_CONFIG_BADFORMAT;
223         }
224         ret = parse_list (f, lineno, &tmp->u.list, error_message);
225     } else {
226         tmp = get_entry(parent, p1, krb5_config_string);
227         if (tmp == NULL) {
228             *error_message = "out of memory";
229             return KRB5_CONFIG_BADFORMAT;
230         }
231         p1 = p;
232         p = p1 + strlen(p1);
233         while(p > p1 && isspace((unsigned char)*(p-1)))
234             --p;
235         *p = '\0';
236         tmp->u.string = strdup(p1);
237     }
238     *b = tmp;
239     return ret;
240 }
241
242 #ifdef __APPLE__
243 static char *
244 cfstring2cstring(CFStringRef string)
245 {
246     CFIndex len;
247     char *str;
248     
249     str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
250     if (str)
251         return strdup(str);
252
253     len = CFStringGetLength(string);
254     len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
255     str = malloc(len);
256     if (str == NULL)
257         return NULL;
258         
259     if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) {
260         free (str);
261         return NULL;
262     }
263     return str;
264 }
265
266 static void
267 convert_content(const void *key, const void *value, void *context)
268 {
269     krb5_config_section *tmp, **parent = context;
270     char *k;
271
272     if (CFGetTypeID(key) != CFStringGetTypeID())
273         return;
274
275     k = cfstring2cstring(key);
276     if (k == NULL)
277         return;
278
279     if (CFGetTypeID(value) == CFStringGetTypeID()) {
280         tmp = get_entry(parent, k, krb5_config_string);
281         tmp->u.string = cfstring2cstring(value);
282     } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
283         tmp = get_entry(parent, k, krb5_config_list);
284         CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
285     } else {
286         /* log */
287     }
288     free(k);
289 }
290
291 static krb5_error_code
292 parse_plist_config(krb5_context context, const char *path, krb5_config_section **parent)
293 {
294     CFReadStreamRef s;
295     CFDictionaryRef d;
296     CFErrorRef e;
297     CFURLRef url;
298     
299     url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), FALSE);
300     if (url == NULL) {
301         krb5_clear_error_message(context);
302         return ENOMEM;
303     }
304
305     s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
306     CFRelease(url);
307     if (s == NULL) {
308         krb5_clear_error_message(context);
309         return ENOMEM;
310     }
311
312     if (!CFReadStreamOpen(s)) {
313         CFRelease(s);
314         krb5_clear_error_message(context);
315         return ENOENT;
316     }
317
318     d = (CFDictionaryRef)CFPropertyListCreateWithStream (kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, &e);
319     CFRelease(s);
320     if (d == NULL) {
321         krb5_clear_error_message(context);
322         return ENOENT;
323     }
324
325     CFDictionaryApplyFunction(d, convert_content, parent);
326     CFRelease(d);
327
328     return 0;
329 }
330
331 #endif
332
333
334 /*
335  * Parse the config file `fname', generating the structures into `res'
336  * returning error messages in `error_message'
337  */
338
339 static krb5_error_code
340 krb5_config_parse_debug (struct fileptr *f,
341                          krb5_config_section **res,
342                          unsigned *lineno,
343                          const char **error_message)
344 {
345     krb5_config_section *s = NULL;
346     krb5_config_binding *b = NULL;
347     char buf[BUFSIZ];
348     krb5_error_code ret;
349
350     while (config_fgets(buf, sizeof(buf), f) != NULL) {
351         char *p;
352
353         ++*lineno;
354         buf[strcspn(buf, "\r\n")] = '\0';
355         p = buf;
356         while(isspace((unsigned char)*p))
357             ++p;
358         if (*p == '#' || *p == ';')
359             continue;
360         if (*p == '[') {
361             ret = parse_section(p, &s, res, error_message);
362             if (ret)
363                 return ret;
364             b = NULL;
365         } else if (*p == '}') {
366             *error_message = "unmatched }";
367             return EINVAL;      /* XXX */
368         } else if(*p != '\0') {
369             if (s == NULL) {
370                 *error_message = "binding before section";
371                 return EINVAL;
372             }
373             ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message);
374             if (ret)
375                 return ret;
376         }
377     }
378     return 0;
379 }
380
381 static int
382 is_plist_file(const char *fname)
383 {
384     size_t len = strlen(fname);
385     char suffix[] = ".plist";
386     if (len < sizeof(suffix))
387         return 0;
388     if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0)
389         return 0;
390     return 1;
391 }
392
393 /**
394  * Parse a configuration file and add the result into res. This
395  * interface can be used to parse several configuration files into one
396  * resulting krb5_config_section by calling it repeatably.
397  *
398  * @param context a Kerberos 5 context.
399  * @param fname a file name to a Kerberos configuration file
400  * @param res the returned result, must be free with krb5_free_config_files().
401  * @return Return an error code or 0, see krb5_get_error_message().
402  *
403  * @ingroup krb5_support
404  */
405
406 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
407 krb5_config_parse_file_multi (krb5_context context,
408                               const char *fname,
409                               krb5_config_section **res)
410 {
411     const char *str;
412     char *newfname = NULL;
413     unsigned lineno = 0;
414     krb5_error_code ret;
415     struct fileptr f;
416
417     /**
418      * If the fname starts with "~/" parse configuration file in the
419      * current users home directory. The behavior can be disabled and
420      * enabled by calling krb5_set_home_dir_access().
421      */
422     if (fname[0] == '~' && fname[1] == '/') {
423 #ifndef KRB5_USE_PATH_TOKENS
424         const char *home = NULL;
425
426         if (!_krb5_homedir_access(context)) {
427             krb5_set_error_message(context, EPERM,
428                                    "Access to home directory not allowed");
429             return EPERM;
430         }
431
432         if(!issuid())
433             home = getenv("HOME");
434
435         if (home == NULL) {
436             struct passwd *pw = getpwuid(getuid());     
437             if(pw != NULL)
438                 home = pw->pw_dir;
439         }
440         if (home) {
441             asprintf(&newfname, "%s%s", home, &fname[1]);
442             if (newfname == NULL) {
443                 krb5_set_error_message(context, ENOMEM,
444                                        N_("malloc: out of memory", ""));
445                 return ENOMEM;
446             }
447             fname = newfname;
448         }
449 #else  /* KRB5_USE_PATH_TOKENS */
450         if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
451             newfname == NULL)
452         {
453             krb5_set_error_message(context, ENOMEM,
454                                    N_("malloc: out of memory", ""));
455             return ENOMEM;
456         }
457         fname = newfname;
458 #endif
459     }
460
461     if (is_plist_file(fname)) {
462 #ifdef __APPLE__
463         ret = parse_plist_config(context, fname, res);
464         if (ret) {
465             krb5_set_error_message(context, ret,
466                                    "Failed to parse plist %s", fname);
467             if (newfname)
468                 free(newfname);
469             return ret;
470         }
471 #else
472         krb5_set_error_message(context, ENOENT, 
473                                "no support for plist configuration files");
474         return ENOENT;
475 #endif
476     } else {
477 #ifdef KRB5_USE_PATH_TOKENS
478         char * exp_fname = NULL;
479
480         ret = _krb5_expand_path_tokens(context, fname, &exp_fname);
481         if (ret) {
482             if (newfname)
483                 free(newfname);
484             return ret;
485         }
486         
487         if (newfname)
488             free(newfname);
489         fname = newfname = exp_fname;
490 #endif
491
492         f.f = fopen(fname, "r");
493         f.s = NULL;
494         if(f.f == NULL) {
495             ret = errno;
496             krb5_set_error_message (context, ret, "open %s: %s",
497                                     fname, strerror(ret));
498             if (newfname)
499                 free(newfname);
500             return ret;
501         }
502         
503         ret = krb5_config_parse_debug (&f, res, &lineno, &str);
504         fclose(f.f);
505         if (ret) {
506             krb5_set_error_message (context, ret, "%s:%u: %s",
507                                     fname, lineno, str);
508             if (newfname)
509                 free(newfname);
510             return ret;
511         }
512     }
513     return 0;
514 }
515
516 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
517 krb5_config_parse_file (krb5_context context,
518                         const char *fname,
519                         krb5_config_section **res)
520 {
521     *res = NULL;
522     return krb5_config_parse_file_multi(context, fname, res);
523 }
524
525 static void
526 free_binding (krb5_context context, krb5_config_binding *b)
527 {
528     krb5_config_binding *next_b;
529
530     while (b) {
531         free (b->name);
532         if (b->type == krb5_config_string)
533             free (b->u.string);
534         else if (b->type == krb5_config_list)
535             free_binding (context, b->u.list);
536         else
537             krb5_abortx(context, "unknown binding type (%d) in free_binding",
538                         b->type);
539         next_b = b->next;
540         free (b);
541         b = next_b;
542     }
543 }
544
545 /**
546  * Free configuration file section, the result of
547  * krb5_config_parse_file() and krb5_config_parse_file_multi().
548  *
549  * @param context A Kerberos 5 context
550  * @param s the configuration section to free
551  *
552  * @return returns 0 on successes, otherwise an error code, see
553  *          krb5_get_error_message()
554  *
555  * @ingroup krb5_support
556  */
557
558 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
559 krb5_config_file_free (krb5_context context, krb5_config_section *s)
560 {
561     free_binding (context, s);
562     return 0;
563 }
564
565 #ifndef HEIMDAL_SMALLER
566
567 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
568 _krb5_config_copy(krb5_context context,
569                   krb5_config_section *c,
570                   krb5_config_section **head)
571 {
572     krb5_config_binding *d, *previous = NULL;
573
574     *head = NULL;
575
576     while (c) {
577         d = calloc(1, sizeof(*d));
578
579         if (*head == NULL)
580             *head = d;
581
582         d->name = strdup(c->name);
583         d->type = c->type;
584         if (d->type == krb5_config_string)
585             d->u.string = strdup(c->u.string);
586         else if (d->type == krb5_config_list)
587             _krb5_config_copy (context, c->u.list, &d->u.list);
588         else
589             krb5_abortx(context,
590                         "unknown binding type (%d) in krb5_config_copy",
591                         d->type);
592         if (previous)
593             previous->next = d;
594
595         previous = d;
596         c = c->next;
597     }
598     return 0;
599 }
600
601 #endif /* HEIMDAL_SMALLER */
602
603 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
604 _krb5_config_get_next (krb5_context context,
605                        const krb5_config_section *c,
606                        const krb5_config_binding **pointer,
607                        int type,
608                        ...)
609 {
610     const char *ret;
611     va_list args;
612
613     va_start(args, type);
614     ret = _krb5_config_vget_next (context, c, pointer, type, args);
615     va_end(args);
616     return ret;
617 }
618
619 static const void *
620 vget_next(krb5_context context,
621           const krb5_config_binding *b,
622           const krb5_config_binding **pointer,
623           int type,
624           const char *name,
625           va_list args)
626 {
627     const char *p = va_arg(args, const char *);
628     while(b != NULL) {
629         if(strcmp(b->name, name) == 0) {
630             if(b->type == type && p == NULL) {
631                 *pointer = b;
632                 return b->u.generic;
633             } else if(b->type == krb5_config_list && p != NULL) {
634                 return vget_next(context, b->u.list, pointer, type, p, args);
635             }
636         }
637         b = b->next;
638     }
639     return NULL;
640 }
641
642 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
643 _krb5_config_vget_next (krb5_context context,
644                         const krb5_config_section *c,
645                         const krb5_config_binding **pointer,
646                         int type,
647                         va_list args)
648 {
649     const krb5_config_binding *b;
650     const char *p;
651
652     if(c == NULL)
653         c = context->cf;
654
655     if (c == NULL)
656         return NULL;
657
658     if (*pointer == NULL) {
659         /* first time here, walk down the tree looking for the right
660            section */
661         p = va_arg(args, const char *);
662         if (p == NULL)
663             return NULL;
664         return vget_next(context, c, pointer, type, p, args);
665     }
666
667     /* we were called again, so just look for more entries with the
668        same name and type */
669     for (b = (*pointer)->next; b != NULL; b = b->next) {
670         if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) {
671             *pointer = b;
672             return b->u.generic;
673         }
674     }
675     return NULL;
676 }
677
678 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
679 _krb5_config_get (krb5_context context,
680                   const krb5_config_section *c,
681                   int type,
682                   ...)
683 {
684     const void *ret;
685     va_list args;
686
687     va_start(args, type);
688     ret = _krb5_config_vget (context, c, type, args);
689     va_end(args);
690     return ret;
691 }
692
693
694 const void *
695 _krb5_config_vget (krb5_context context,
696                    const krb5_config_section *c,
697                    int type,
698                    va_list args)
699 {
700     const krb5_config_binding *foo = NULL;
701
702     return _krb5_config_vget_next (context, c, &foo, type, args);
703 }
704
705 /**
706  * Get a list of configuration binding list for more processing
707  *
708  * @param context A Kerberos 5 context.
709  * @param c a configuration section, or NULL to use the section from context
710  * @param ... a list of names, terminated with NULL.
711  *
712  * @return NULL if configuration list is not found, a list otherwise
713  *
714  * @ingroup krb5_support
715  */
716
717 KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
718 krb5_config_get_list (krb5_context context,
719                       const krb5_config_section *c,
720                       ...)
721 {
722     const krb5_config_binding *ret;
723     va_list args;
724
725     va_start(args, c);
726     ret = krb5_config_vget_list (context, c, args);
727     va_end(args);
728     return ret;
729 }
730
731 /**
732  * Get a list of configuration binding list for more processing
733  *
734  * @param context A Kerberos 5 context.
735  * @param c a configuration section, or NULL to use the section from context
736  * @param args a va_list of arguments
737  *
738  * @return NULL if configuration list is not found, a list otherwise
739  *
740  * @ingroup krb5_support
741  */
742
743 KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
744 krb5_config_vget_list (krb5_context context,
745                        const krb5_config_section *c,
746                        va_list args)
747 {
748     return _krb5_config_vget (context, c, krb5_config_list, args);
749 }
750
751 /**
752  * Returns a "const char *" to a string in the configuration database.
753  * The string may not be valid after a reload of the configuration
754  * database so a caller should make a local copy if it needs to keep
755  * the string.
756  *
757  * @param context A Kerberos 5 context.
758  * @param c a configuration section, or NULL to use the section from context
759  * @param ... a list of names, terminated with NULL.
760  *
761  * @return NULL if configuration string not found, a string otherwise
762  *
763  * @ingroup krb5_support
764  */
765  
766 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
767 krb5_config_get_string (krb5_context context,
768                         const krb5_config_section *c,
769                         ...)
770 {
771     const char *ret;
772     va_list args;
773
774     va_start(args, c);
775     ret = krb5_config_vget_string (context, c, args);
776     va_end(args);
777     return ret;
778 }
779
780 /**
781  * Like krb5_config_get_string(), but uses a va_list instead of ...
782  *
783  * @param context A Kerberos 5 context.
784  * @param c a configuration section, or NULL to use the section from context
785  * @param args a va_list of arguments
786  *
787  * @return NULL if configuration string not found, a string otherwise
788  *
789  * @ingroup krb5_support
790  */
791
792 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
793 krb5_config_vget_string (krb5_context context,
794                          const krb5_config_section *c,
795                          va_list args)
796 {
797     return _krb5_config_vget (context, c, krb5_config_string, args);
798 }
799
800 /**
801  * Like krb5_config_vget_string(), but instead of returning NULL,
802  * instead return a default value.
803  *
804  * @param context A Kerberos 5 context.
805  * @param c a configuration section, or NULL to use the section from context
806  * @param def_value the default value to return if no configuration
807  *        found in the database.
808  * @param args a va_list of arguments
809  *
810  * @return a configuration string
811  *
812  * @ingroup krb5_support
813  */
814
815 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
816 krb5_config_vget_string_default (krb5_context context,
817                                  const krb5_config_section *c,
818                                  const char *def_value,
819                                  va_list args)
820 {
821     const char *ret;
822
823     ret = krb5_config_vget_string (context, c, args);
824     if (ret == NULL)
825         ret = def_value;
826     return ret;
827 }
828
829 /**
830  * Like krb5_config_get_string(), but instead of returning NULL,
831  * instead return a default value.
832  *
833  * @param context A Kerberos 5 context.
834  * @param c a configuration section, or NULL to use the section from context
835  * @param def_value the default value to return if no configuration
836  *        found in the database.
837  * @param ... a list of names, terminated with NULL.
838  *
839  * @return a configuration string
840  *
841  * @ingroup krb5_support
842  */
843
844 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
845 krb5_config_get_string_default (krb5_context context,
846                                 const krb5_config_section *c,
847                                 const char *def_value,
848                                 ...)
849 {
850     const char *ret;
851     va_list args;
852
853     va_start(args, def_value);
854     ret = krb5_config_vget_string_default (context, c, def_value, args);
855     va_end(args);
856     return ret;
857 }
858
859 /**
860  * Get a list of configuration strings, free the result with
861  * krb5_config_free_strings().
862  *
863  * @param context A Kerberos 5 context.
864  * @param c a configuration section, or NULL to use the section from context
865  * @param args a va_list of arguments
866  *
867  * @return TRUE or FALSE
868  *
869  * @ingroup krb5_support
870  */
871
872 KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL
873 krb5_config_vget_strings(krb5_context context,
874                          const krb5_config_section *c,
875                          va_list args)
876 {
877     char **strings = NULL;
878     int nstr = 0;
879     const krb5_config_binding *b = NULL;
880     const char *p;
881
882     while((p = _krb5_config_vget_next(context, c, &b,
883                                       krb5_config_string, args))) {
884         char *tmp = strdup(p);
885         char *pos = NULL;
886         char *s;
887         if(tmp == NULL)
888             goto cleanup;
889         s = strtok_r(tmp, " \t", &pos);
890         while(s){
891             char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
892             if(tmp2 == NULL)
893                 goto cleanup;
894             strings = tmp2;
895             strings[nstr] = strdup(s);
896             nstr++;
897             if(strings[nstr-1] == NULL)
898                 goto cleanup;
899             s = strtok_r(NULL, " \t", &pos);
900         }
901         free(tmp);
902     }
903     if(nstr){
904         char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
905         if(tmp == NULL)
906             goto cleanup;
907         strings = tmp;
908         strings[nstr] = NULL;
909     }
910     return strings;
911 cleanup:
912     while(nstr--)
913         free(strings[nstr]);
914     free(strings);
915     return NULL;
916
917 }
918
919 /**
920  * Get a list of configuration strings, free the result with
921  * krb5_config_free_strings().
922  *
923  * @param context A Kerberos 5 context.
924  * @param c a configuration section, or NULL to use the section from context
925  * @param ... a list of names, terminated with NULL.
926  *
927  * @return TRUE or FALSE
928  *
929  * @ingroup krb5_support
930  */
931
932 KRB5_LIB_FUNCTION char** KRB5_LIB_CALL
933 krb5_config_get_strings(krb5_context context,
934                         const krb5_config_section *c,
935                         ...)
936 {
937     va_list ap;
938     char **ret;
939     va_start(ap, c);
940     ret = krb5_config_vget_strings(context, c, ap);
941     va_end(ap);
942     return ret;
943 }
944
945 /**
946  * Free the resulting strings from krb5_config-get_strings() and
947  * krb5_config_vget_strings().
948  *
949  * @param strings strings to free
950  *
951  * @ingroup krb5_support
952  */
953
954 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
955 krb5_config_free_strings(char **strings)
956 {
957     char **s = strings;
958     while(s && *s){
959         free(*s);
960         s++;
961     }
962     free(strings);
963 }
964
965 /**
966  * Like krb5_config_get_bool_default() but with a va_list list of
967  * configuration selection.
968  *
969  * Configuration value to a boolean value, where yes/true and any
970  * non-zero number means TRUE and other value is FALSE.
971  *
972  * @param context A Kerberos 5 context.
973  * @param c a configuration section, or NULL to use the section from context
974  * @param def_value the default value to return if no configuration
975  *        found in the database.
976  * @param args a va_list of arguments
977  *
978  * @return TRUE or FALSE
979  *
980  * @ingroup krb5_support
981  */
982
983 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
984 krb5_config_vget_bool_default (krb5_context context,
985                                const krb5_config_section *c,
986                                krb5_boolean def_value,
987                                va_list args)
988 {
989     const char *str;
990     str = krb5_config_vget_string (context, c, args);
991     if(str == NULL)
992         return def_value;
993     if(strcasecmp(str, "yes") == 0 ||
994        strcasecmp(str, "true") == 0 ||
995        atoi(str)) return TRUE;
996     return FALSE;
997 }
998
999 /**
1000  * krb5_config_get_bool() will convert the configuration
1001  * option value to a boolean value, where yes/true and any non-zero
1002  * number means TRUE and other value is FALSE.
1003  *
1004  * @param context A Kerberos 5 context.
1005  * @param c a configuration section, or NULL to use the section from context
1006  * @param args a va_list of arguments
1007  *
1008  * @return TRUE or FALSE
1009  *
1010  * @ingroup krb5_support
1011  */
1012
1013 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1014 krb5_config_vget_bool  (krb5_context context,
1015                         const krb5_config_section *c,
1016                         va_list args)
1017 {
1018     return krb5_config_vget_bool_default (context, c, FALSE, args);
1019 }
1020
1021 /**
1022  * krb5_config_get_bool_default() will convert the configuration
1023  * option value to a boolean value, where yes/true and any non-zero
1024  * number means TRUE and other value is FALSE.
1025  *
1026  * @param context A Kerberos 5 context.
1027  * @param c a configuration section, or NULL to use the section from context
1028  * @param def_value the default value to return if no configuration
1029  *        found in the database.
1030  * @param ... a list of names, terminated with NULL.
1031  *
1032  * @return TRUE or FALSE
1033  *
1034  * @ingroup krb5_support
1035  */
1036
1037 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1038 krb5_config_get_bool_default (krb5_context context,
1039                               const krb5_config_section *c,
1040                               krb5_boolean def_value,
1041                               ...)
1042 {
1043     va_list ap;
1044     krb5_boolean ret;
1045     va_start(ap, def_value);
1046     ret = krb5_config_vget_bool_default(context, c, def_value, ap);
1047     va_end(ap);
1048     return ret;
1049 }
1050
1051 /**
1052  * Like krb5_config_get_bool() but with a va_list list of
1053  * configuration selection.
1054  *
1055  * Configuration value to a boolean value, where yes/true and any
1056  * non-zero number means TRUE and other value is FALSE.
1057  *
1058  * @param context A Kerberos 5 context.
1059  * @param c a configuration section, or NULL to use the section from context
1060  * @param ... a list of names, terminated with NULL.
1061  *
1062  * @return TRUE or FALSE
1063  *
1064  * @ingroup krb5_support
1065  */
1066
1067 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1068 krb5_config_get_bool (krb5_context context,
1069                       const krb5_config_section *c,
1070                       ...)
1071 {
1072     va_list ap;
1073     krb5_boolean ret;
1074     va_start(ap, c);
1075     ret = krb5_config_vget_bool (context, c, ap);
1076     va_end(ap);
1077     return ret;
1078 }
1079
1080 /**
1081  * Get the time from the configuration file using a relative time.
1082  *
1083  * Like krb5_config_get_time_default() but with a va_list list of
1084  * configuration selection.
1085  *
1086  * @param context A Kerberos 5 context.
1087  * @param c a configuration section, or NULL to use the section from context
1088  * @param def_value the default value to return if no configuration
1089  *        found in the database.
1090  * @param args a va_list of arguments
1091  *
1092  * @return parsed the time (or def_value on parse error)
1093  *
1094  * @ingroup krb5_support
1095  */
1096
1097 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1098 krb5_config_vget_time_default (krb5_context context,
1099                                const krb5_config_section *c,
1100                                int def_value,
1101                                va_list args)
1102 {
1103     const char *str;
1104     krb5_deltat t;
1105
1106     str = krb5_config_vget_string (context, c, args);
1107     if(str == NULL)
1108         return def_value;
1109     if (krb5_string_to_deltat(str, &t))
1110         return def_value;
1111     return t;
1112 }
1113
1114 /**
1115  * Get the time from the configuration file using a relative time, for example: 1h30s
1116  *
1117  * @param context A Kerberos 5 context.
1118  * @param c a configuration section, or NULL to use the section from context
1119  * @param args a va_list of arguments
1120  *
1121  * @return parsed the time or -1 on error
1122  *
1123  * @ingroup krb5_support
1124  */
1125
1126 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1127 krb5_config_vget_time  (krb5_context context,
1128                         const krb5_config_section *c,
1129                         va_list args)
1130 {
1131     return krb5_config_vget_time_default (context, c, -1, args);
1132 }
1133
1134 /**
1135  * Get the time from the configuration file using a relative time, for example: 1h30s
1136  *
1137  * @param context A Kerberos 5 context.
1138  * @param c a configuration section, or NULL to use the section from context
1139  * @param def_value the default value to return if no configuration
1140  *        found in the database.
1141  * @param ... a list of names, terminated with NULL.
1142  *
1143  * @return parsed the time (or def_value on parse error)
1144  *
1145  * @ingroup krb5_support
1146  */
1147
1148 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1149 krb5_config_get_time_default (krb5_context context,
1150                               const krb5_config_section *c,
1151                               int def_value,
1152                               ...)
1153 {
1154     va_list ap;
1155     int ret;
1156     va_start(ap, def_value);
1157     ret = krb5_config_vget_time_default(context, c, def_value, ap);
1158     va_end(ap);
1159     return ret;
1160 }
1161
1162 /**
1163  * Get the time from the configuration file using a relative time, for example: 1h30s
1164  *
1165  * @param context A Kerberos 5 context.
1166  * @param c a configuration section, or NULL to use the section from context
1167  * @param ... a list of names, terminated with NULL.
1168  *
1169  * @return parsed the time or -1 on error
1170  *
1171  * @ingroup krb5_support
1172  */
1173
1174 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1175 krb5_config_get_time (krb5_context context,
1176                       const krb5_config_section *c,
1177                       ...)
1178 {
1179     va_list ap;
1180     int ret;
1181     va_start(ap, c);
1182     ret = krb5_config_vget_time (context, c, ap);
1183     va_end(ap);
1184     return ret;
1185 }
1186
1187
1188 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1189 krb5_config_vget_int_default (krb5_context context,
1190                               const krb5_config_section *c,
1191                               int def_value,
1192                               va_list args)
1193 {
1194     const char *str;
1195     str = krb5_config_vget_string (context, c, args);
1196     if(str == NULL)
1197         return def_value;
1198     else {
1199         char *endptr;
1200         long l;
1201         l = strtol(str, &endptr, 0);
1202         if (endptr == str)
1203             return def_value;
1204         else
1205             return l;
1206     }
1207 }
1208
1209 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1210 krb5_config_vget_int  (krb5_context context,
1211                        const krb5_config_section *c,
1212                        va_list args)
1213 {
1214     return krb5_config_vget_int_default (context, c, -1, args);
1215 }
1216
1217 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1218 krb5_config_get_int_default (krb5_context context,
1219                              const krb5_config_section *c,
1220                              int def_value,
1221                              ...)
1222 {
1223     va_list ap;
1224     int ret;
1225     va_start(ap, def_value);
1226     ret = krb5_config_vget_int_default(context, c, def_value, ap);
1227     va_end(ap);
1228     return ret;
1229 }
1230
1231 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1232 krb5_config_get_int (krb5_context context,
1233                      const krb5_config_section *c,
1234                      ...)
1235 {
1236     va_list ap;
1237     int ret;
1238     va_start(ap, c);
1239     ret = krb5_config_vget_int (context, c, ap);
1240     va_end(ap);
1241     return ret;
1242 }
1243
1244
1245 #ifndef HEIMDAL_SMALLER
1246
1247 /**
1248  * Deprecated: configuration files are not strings
1249  *
1250  * @ingroup krb5_deprecated
1251  */
1252
1253 KRB5_DEPRECATED
1254 krb5_error_code KRB5_LIB_FUNCTION
1255 krb5_config_parse_string_multi(krb5_context context,
1256                                const char *string,
1257                                krb5_config_section **res)
1258 {
1259     const char *str;
1260     unsigned lineno = 0;
1261     krb5_error_code ret;
1262     struct fileptr f;
1263     f.f = NULL;
1264     f.s = string;
1265
1266     ret = krb5_config_parse_debug (&f, res, &lineno, &str);
1267     if (ret) {
1268         krb5_set_error_message (context, ret, "%s:%u: %s",
1269                                 "<constant>", lineno, str);
1270         return ret;
1271     }
1272     return 0;
1273 }
1274
1275 #endif