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 **err_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 **err_message);
83 static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
84                                   krb5_config_binding **parent,
85                                   const char **err_message);
86
87 krb5_config_section *
88 _krb5_config_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 `err_message'.
123  */
124
125 static krb5_error_code
126 parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
127               const char **err_message)
128 {
129     char *p1;
130     krb5_config_section *tmp;
131
132     p1 = strchr (p + 1, ']');
133     if (p1 == NULL) {
134         *err_message = "missing ]";
135         return KRB5_CONFIG_BADFORMAT;
136     }
137     *p1 = '\0';
138     tmp = _krb5_config_get_entry(parent, p + 1, krb5_config_list);
139     if(tmp == NULL) {
140         *err_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 `err_message'.
151  */
152
153 static krb5_error_code
154 parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent,
155            const char **err_message)
156 {
157     char buf[KRB5_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, err_message);
179         if (ret)
180             return ret;
181     }
182     *lineno = beg_lineno;
183     *err_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 **err_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         *err_message = "missing =";
205         return KRB5_CONFIG_BADFORMAT;
206     }
207     p2 = p;
208     while (isspace((unsigned char)*p))
209         ++p;
210     if (*p != '=') {
211         *err_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 = _krb5_config_get_entry(parent, p1, krb5_config_list);
220         if (tmp == NULL) {
221             *err_message = "out of memory";
222             return KRB5_CONFIG_BADFORMAT;
223         }
224         ret = parse_list (f, lineno, &tmp->u.list, err_message);
225     } else {
226         tmp = _krb5_config_get_entry(parent, p1, krb5_config_string);
227         if (tmp == NULL) {
228             *err_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 #if defined(__APPLE__)
243
244 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
245 #define HAVE_CFPROPERTYLISTCREATEWITHSTREAM 1
246 #endif
247
248 static char *
249 cfstring2cstring(CFStringRef string)
250 {
251     CFIndex len;
252     char *str;
253     
254     str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
255     if (str)
256         return strdup(str);
257
258     len = CFStringGetLength(string);
259     len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
260     str = malloc(len);
261     if (str == NULL)
262         return NULL;
263         
264     if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) {
265         free (str);
266         return NULL;
267     }
268     return str;
269 }
270
271 static void
272 convert_content(const void *key, const void *value, void *context)
273 {
274     krb5_config_section *tmp, **parent = context;
275     char *k;
276
277     if (CFGetTypeID(key) != CFStringGetTypeID())
278         return;
279
280     k = cfstring2cstring(key);
281     if (k == NULL)
282         return;
283
284     if (CFGetTypeID(value) == CFStringGetTypeID()) {
285         tmp = _krb5_config_get_entry(parent, k, krb5_config_string);
286         tmp->u.string = cfstring2cstring(value);
287     } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
288         tmp = _krb5_config_get_entry(parent, k, krb5_config_list);
289         CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
290     } else {
291         /* log */
292     }
293     free(k);
294 }
295
296 static krb5_error_code
297 parse_plist_config(krb5_context context, const char *path, krb5_config_section **parent)
298 {
299     CFReadStreamRef s;
300     CFDictionaryRef d;
301     CFURLRef url;
302     
303     url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), FALSE);
304     if (url == NULL) {
305         krb5_clear_error_message(context);
306         return ENOMEM;
307     }
308
309     s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
310     CFRelease(url);
311     if (s == NULL) {
312         krb5_clear_error_message(context);
313         return ENOMEM;
314     }
315
316     if (!CFReadStreamOpen(s)) {
317         CFRelease(s);
318         krb5_clear_error_message(context);
319         return ENOENT;
320     }
321
322 #ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
323     d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
324 #else
325     d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
326 #endif
327     CFRelease(s);
328     if (d == NULL) {
329         krb5_clear_error_message(context);
330         return ENOENT;
331     }
332
333     CFDictionaryApplyFunction(d, convert_content, parent);
334     CFRelease(d);
335
336     return 0;
337 }
338
339 #endif
340
341
342 /*
343  * Parse the config file `fname', generating the structures into `res'
344  * returning error messages in `err_message'
345  */
346
347 static krb5_error_code
348 krb5_config_parse_debug (struct fileptr *f,
349                          krb5_config_section **res,
350                          unsigned *lineno,
351                          const char **err_message)
352 {
353     krb5_config_section *s = NULL;
354     krb5_config_binding *b = NULL;
355     char buf[KRB5_BUFSIZ];
356     krb5_error_code ret;
357
358     while (config_fgets(buf, sizeof(buf), f) != NULL) {
359         char *p;
360
361         ++*lineno;
362         buf[strcspn(buf, "\r\n")] = '\0';
363         p = buf;
364         while(isspace((unsigned char)*p))
365             ++p;
366         if (*p == '#' || *p == ';')
367             continue;
368         if (*p == '[') {
369             ret = parse_section(p, &s, res, err_message);
370             if (ret)
371                 return ret;
372             b = NULL;
373         } else if (*p == '}') {
374             *err_message = "unmatched }";
375             return EINVAL;      /* XXX */
376         } else if(*p != '\0') {
377             if (s == NULL) {
378                 *err_message = "binding before section";
379                 return EINVAL;
380             }
381             ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message);
382             if (ret)
383                 return ret;
384         }
385     }
386     return 0;
387 }
388
389 static int
390 is_plist_file(const char *fname)
391 {
392     size_t len = strlen(fname);
393     char suffix[] = ".plist";
394     if (len < sizeof(suffix))
395         return 0;
396     if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0)
397         return 0;
398     return 1;
399 }
400
401 /**
402  * Parse a configuration file and add the result into res. This
403  * interface can be used to parse several configuration files into one
404  * resulting krb5_config_section by calling it repeatably.
405  *
406  * @param context a Kerberos 5 context.
407  * @param fname a file name to a Kerberos configuration file
408  * @param res the returned result, must be free with krb5_free_config_files().
409  * @return Return an error code or 0, see krb5_get_error_message().
410  *
411  * @ingroup krb5_support
412  */
413
414 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
415 krb5_config_parse_file_multi (krb5_context context,
416                               const char *fname,
417                               krb5_config_section **res)
418 {
419     const char *str;
420     char *newfname = NULL;
421     unsigned lineno = 0;
422     krb5_error_code ret;
423     struct fileptr f;
424
425     /**
426      * If the fname starts with "~/" parse configuration file in the
427      * current users home directory. The behavior can be disabled and
428      * enabled by calling krb5_set_home_dir_access().
429      */
430     if (fname[0] == '~' && fname[1] == '/') {
431 #ifndef KRB5_USE_PATH_TOKENS
432         const char *home = NULL;
433
434         if (!_krb5_homedir_access(context)) {
435             krb5_set_error_message(context, EPERM,
436                                    "Access to home directory not allowed");
437             return EPERM;
438         }
439
440         if(!issuid())
441             home = getenv("HOME");
442
443         if (home == NULL) {
444             struct passwd *pw = getpwuid(getuid());     
445             if(pw != NULL)
446                 home = pw->pw_dir;
447         }
448         if (home) {
449             asprintf(&newfname, "%s%s", home, &fname[1]);
450             if (newfname == NULL) {
451                 krb5_set_error_message(context, ENOMEM,
452                                        N_("malloc: out of memory", ""));
453                 return ENOMEM;
454             }
455             fname = newfname;
456         }
457 #else  /* KRB5_USE_PATH_TOKENS */
458         if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
459             newfname == NULL)
460         {
461             krb5_set_error_message(context, ENOMEM,
462                                    N_("malloc: out of memory", ""));
463             return ENOMEM;
464         }
465         fname = newfname;
466 #endif
467     }
468
469     if (is_plist_file(fname)) {
470 #ifdef __APPLE__
471         ret = parse_plist_config(context, fname, res);
472         if (ret) {
473             krb5_set_error_message(context, ret,
474                                    "Failed to parse plist %s", fname);
475             if (newfname)
476                 free(newfname);
477             return ret;
478         }
479 #else
480         krb5_set_error_message(context, ENOENT, 
481                                "no support for plist configuration files");
482         return ENOENT;
483 #endif
484     } else {
485 #ifdef KRB5_USE_PATH_TOKENS
486         char * exp_fname = NULL;
487
488         ret = _krb5_expand_path_tokens(context, fname, &exp_fname);
489         if (ret) {
490             if (newfname)
491                 free(newfname);
492             return ret;
493         }
494         
495         if (newfname)
496             free(newfname);
497         fname = newfname = exp_fname;
498 #endif
499
500         f.f = fopen(fname, "r");
501         f.s = NULL;
502         if(f.f == NULL) {
503             ret = errno;
504             krb5_set_error_message (context, ret, "open %s: %s",
505                                     fname, strerror(ret));
506             if (newfname)
507                 free(newfname);
508             return ret;
509         }
510         
511         ret = krb5_config_parse_debug (&f, res, &lineno, &str);
512         fclose(f.f);
513         if (ret) {
514             krb5_set_error_message (context, ret, "%s:%u: %s",
515                                     fname, lineno, str);
516             if (newfname)
517                 free(newfname);
518             return ret;
519         }
520     }
521     return 0;
522 }
523
524 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
525 krb5_config_parse_file (krb5_context context,
526                         const char *fname,
527                         krb5_config_section **res)
528 {
529     *res = NULL;
530     return krb5_config_parse_file_multi(context, fname, res);
531 }
532
533 static void
534 free_binding (krb5_context context, krb5_config_binding *b)
535 {
536     krb5_config_binding *next_b;
537
538     while (b) {
539         free (b->name);
540         if (b->type == krb5_config_string)
541             free (b->u.string);
542         else if (b->type == krb5_config_list)
543             free_binding (context, b->u.list);
544         else
545             krb5_abortx(context, "unknown binding type (%d) in free_binding",
546                         b->type);
547         next_b = b->next;
548         free (b);
549         b = next_b;
550     }
551 }
552
553 /**
554  * Free configuration file section, the result of
555  * krb5_config_parse_file() and krb5_config_parse_file_multi().
556  *
557  * @param context A Kerberos 5 context
558  * @param s the configuration section to free
559  *
560  * @return returns 0 on successes, otherwise an error code, see
561  *          krb5_get_error_message()
562  *
563  * @ingroup krb5_support
564  */
565
566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
567 krb5_config_file_free (krb5_context context, krb5_config_section *s)
568 {
569     free_binding (context, s);
570     return 0;
571 }
572
573 #ifndef HEIMDAL_SMALLER
574
575 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
576 _krb5_config_copy(krb5_context context,
577                   krb5_config_section *c,
578                   krb5_config_section **head)
579 {
580     krb5_config_binding *d, *previous = NULL;
581
582     *head = NULL;
583
584     while (c) {
585         d = calloc(1, sizeof(*d));
586
587         if (*head == NULL)
588             *head = d;
589
590         d->name = strdup(c->name);
591         d->type = c->type;
592         if (d->type == krb5_config_string)
593             d->u.string = strdup(c->u.string);
594         else if (d->type == krb5_config_list)
595             _krb5_config_copy (context, c->u.list, &d->u.list);
596         else
597             krb5_abortx(context,
598                         "unknown binding type (%d) in krb5_config_copy",
599                         d->type);
600         if (previous)
601             previous->next = d;
602
603         previous = d;
604         c = c->next;
605     }
606     return 0;
607 }
608
609 #endif /* HEIMDAL_SMALLER */
610
611 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
612 _krb5_config_get_next (krb5_context context,
613                        const krb5_config_section *c,
614                        const krb5_config_binding **pointer,
615                        int type,
616                        ...)
617 {
618     const char *ret;
619     va_list args;
620
621     va_start(args, type);
622     ret = _krb5_config_vget_next (context, c, pointer, type, args);
623     va_end(args);
624     return ret;
625 }
626
627 static const void *
628 vget_next(krb5_context context,
629           const krb5_config_binding *b,
630           const krb5_config_binding **pointer,
631           int type,
632           const char *name,
633           va_list args)
634 {
635     const char *p = va_arg(args, const char *);
636     while(b != NULL) {
637         if(strcmp(b->name, name) == 0) {
638             if(b->type == type && p == NULL) {
639                 *pointer = b;
640                 return b->u.generic;
641             } else if(b->type == krb5_config_list && p != NULL) {
642                 return vget_next(context, b->u.list, pointer, type, p, args);
643             }
644         }
645         b = b->next;
646     }
647     return NULL;
648 }
649
650 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
651 _krb5_config_vget_next (krb5_context context,
652                         const krb5_config_section *c,
653                         const krb5_config_binding **pointer,
654                         int type,
655                         va_list args)
656 {
657     const krb5_config_binding *b;
658     const char *p;
659
660     if(c == NULL)
661         c = context->cf;
662
663     if (c == NULL)
664         return NULL;
665
666     if (*pointer == NULL) {
667         /* first time here, walk down the tree looking for the right
668            section */
669         p = va_arg(args, const char *);
670         if (p == NULL)
671             return NULL;
672         return vget_next(context, c, pointer, type, p, args);
673     }
674
675     /* we were called again, so just look for more entries with the
676        same name and type */
677     for (b = (*pointer)->next; b != NULL; b = b->next) {
678         if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) {
679             *pointer = b;
680             return b->u.generic;
681         }
682     }
683     return NULL;
684 }
685
686 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
687 _krb5_config_get (krb5_context context,
688                   const krb5_config_section *c,
689                   int type,
690                   ...)
691 {
692     const void *ret;
693     va_list args;
694
695     va_start(args, type);
696     ret = _krb5_config_vget (context, c, type, args);
697     va_end(args);
698     return ret;
699 }
700
701
702 const void *
703 _krb5_config_vget (krb5_context context,
704                    const krb5_config_section *c,
705                    int type,
706                    va_list args)
707 {
708     const krb5_config_binding *foo = NULL;
709
710     return _krb5_config_vget_next (context, c, &foo, type, args);
711 }
712
713 /**
714  * Get a list of configuration binding list for more processing
715  *
716  * @param context A Kerberos 5 context.
717  * @param c a configuration section, or NULL to use the section from context
718  * @param ... a list of names, terminated with NULL.
719  *
720  * @return NULL if configuration list is not found, a list otherwise
721  *
722  * @ingroup krb5_support
723  */
724
725 KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
726 krb5_config_get_list (krb5_context context,
727                       const krb5_config_section *c,
728                       ...)
729 {
730     const krb5_config_binding *ret;
731     va_list args;
732
733     va_start(args, c);
734     ret = krb5_config_vget_list (context, c, args);
735     va_end(args);
736     return ret;
737 }
738
739 /**
740  * Get a list of configuration binding list for more processing
741  *
742  * @param context A Kerberos 5 context.
743  * @param c a configuration section, or NULL to use the section from context
744  * @param args a va_list of arguments
745  *
746  * @return NULL if configuration list is not found, a list otherwise
747  *
748  * @ingroup krb5_support
749  */
750
751 KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
752 krb5_config_vget_list (krb5_context context,
753                        const krb5_config_section *c,
754                        va_list args)
755 {
756     return _krb5_config_vget (context, c, krb5_config_list, args);
757 }
758
759 /**
760  * Returns a "const char *" to a string in the configuration database.
761  * The string may not be valid after a reload of the configuration
762  * database so a caller should make a local copy if it needs to keep
763  * the string.
764  *
765  * @param context A Kerberos 5 context.
766  * @param c a configuration section, or NULL to use the section from context
767  * @param ... a list of names, terminated with NULL.
768  *
769  * @return NULL if configuration string not found, a string otherwise
770  *
771  * @ingroup krb5_support
772  */
773  
774 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
775 krb5_config_get_string (krb5_context context,
776                         const krb5_config_section *c,
777                         ...)
778 {
779     const char *ret;
780     va_list args;
781
782     va_start(args, c);
783     ret = krb5_config_vget_string (context, c, args);
784     va_end(args);
785     return ret;
786 }
787
788 /**
789  * Like krb5_config_get_string(), but uses a va_list instead of ...
790  *
791  * @param context A Kerberos 5 context.
792  * @param c a configuration section, or NULL to use the section from context
793  * @param args a va_list of arguments
794  *
795  * @return NULL if configuration string not found, a string otherwise
796  *
797  * @ingroup krb5_support
798  */
799
800 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
801 krb5_config_vget_string (krb5_context context,
802                          const krb5_config_section *c,
803                          va_list args)
804 {
805     return _krb5_config_vget (context, c, krb5_config_string, args);
806 }
807
808 /**
809  * Like krb5_config_vget_string(), but instead of returning NULL,
810  * instead return a default value.
811  *
812  * @param context A Kerberos 5 context.
813  * @param c a configuration section, or NULL to use the section from context
814  * @param def_value the default value to return if no configuration
815  *        found in the database.
816  * @param args a va_list of arguments
817  *
818  * @return a configuration string
819  *
820  * @ingroup krb5_support
821  */
822
823 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
824 krb5_config_vget_string_default (krb5_context context,
825                                  const krb5_config_section *c,
826                                  const char *def_value,
827                                  va_list args)
828 {
829     const char *ret;
830
831     ret = krb5_config_vget_string (context, c, args);
832     if (ret == NULL)
833         ret = def_value;
834     return ret;
835 }
836
837 /**
838  * Like krb5_config_get_string(), but instead of returning NULL,
839  * instead return a default value.
840  *
841  * @param context A Kerberos 5 context.
842  * @param c a configuration section, or NULL to use the section from context
843  * @param def_value the default value to return if no configuration
844  *        found in the database.
845  * @param ... a list of names, terminated with NULL.
846  *
847  * @return a configuration string
848  *
849  * @ingroup krb5_support
850  */
851
852 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
853 krb5_config_get_string_default (krb5_context context,
854                                 const krb5_config_section *c,
855                                 const char *def_value,
856                                 ...)
857 {
858     const char *ret;
859     va_list args;
860
861     va_start(args, def_value);
862     ret = krb5_config_vget_string_default (context, c, def_value, args);
863     va_end(args);
864     return ret;
865 }
866
867 static char *
868 next_component_string(char * begin, char * delims, char **state)
869 {
870     char * end;
871
872     if (begin == NULL)
873         begin = *state;
874
875     if (*begin == '\0')
876         return NULL;
877
878     end = begin;
879     while (*end == '"') {
880         char * t;
881         while ((t = strchr(end + 1, '"')) != NULL && *(t - 1) == '\\') {
882             --t;
883             memmove(t, t + 1, strlen(t));
884             end = t;
885         }
886
887         if (t)
888             end = ++t;
889         else
890             end += strlen(end);
891     }
892
893     if (*end != '\0') {
894         size_t pos;
895
896         pos = strcspn(end, delims);
897         end = end + pos;
898     }
899
900     if (*end != '\0') {
901         *end = '\0';
902         *state = end + 1;
903         if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
904             begin++; *(end - 1) = '\0';
905         }
906         return begin;
907     }
908
909     *state = end;
910     if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
911         begin++; *(end - 1) = '\0';
912     }
913     return begin;
914 }
915
916 /**
917  * Get a list of configuration strings, free the result with
918  * krb5_config_free_strings().
919  *
920  * @param context A Kerberos 5 context.
921  * @param c a configuration section, or NULL to use the section from context
922  * @param args a va_list of arguments
923  *
924  * @return TRUE or FALSE
925  *
926  * @ingroup krb5_support
927  */
928
929 KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL
930 krb5_config_vget_strings(krb5_context context,
931                          const krb5_config_section *c,
932                          va_list args)
933 {
934     char **strings = NULL;
935     int nstr = 0;
936     const krb5_config_binding *b = NULL;
937     const char *p;
938
939     while((p = _krb5_config_vget_next(context, c, &b,
940                                       krb5_config_string, args))) {
941         char *tmp = strdup(p);
942         char *pos = NULL;
943         char *s;
944         if(tmp == NULL)
945             goto cleanup;
946         s = next_component_string(tmp, " \t", &pos);
947         while(s){
948             char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
949             if(tmp2 == NULL)
950                 goto cleanup;
951             strings = tmp2;
952             strings[nstr] = strdup(s);
953             nstr++;
954             if(strings[nstr-1] == NULL)
955                 goto cleanup;
956             s = next_component_string(NULL, " \t", &pos);
957         }
958         free(tmp);
959     }
960     if(nstr){
961         char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
962         if(tmp == NULL)
963             goto cleanup;
964         strings = tmp;
965         strings[nstr] = NULL;
966     }
967     return strings;
968 cleanup:
969     while(nstr--)
970         free(strings[nstr]);
971     free(strings);
972     return NULL;
973
974 }
975
976 /**
977  * Get a list of configuration strings, free the result with
978  * krb5_config_free_strings().
979  *
980  * @param context A Kerberos 5 context.
981  * @param c a configuration section, or NULL to use the section from context
982  * @param ... a list of names, terminated with NULL.
983  *
984  * @return TRUE or FALSE
985  *
986  * @ingroup krb5_support
987  */
988
989 KRB5_LIB_FUNCTION char** KRB5_LIB_CALL
990 krb5_config_get_strings(krb5_context context,
991                         const krb5_config_section *c,
992                         ...)
993 {
994     va_list ap;
995     char **ret;
996     va_start(ap, c);
997     ret = krb5_config_vget_strings(context, c, ap);
998     va_end(ap);
999     return ret;
1000 }
1001
1002 /**
1003  * Free the resulting strings from krb5_config-get_strings() and
1004  * krb5_config_vget_strings().
1005  *
1006  * @param strings strings to free
1007  *
1008  * @ingroup krb5_support
1009  */
1010
1011 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1012 krb5_config_free_strings(char **strings)
1013 {
1014     char **s = strings;
1015     while(s && *s){
1016         free(*s);
1017         s++;
1018     }
1019     free(strings);
1020 }
1021
1022 /**
1023  * Like krb5_config_get_bool_default() but with a va_list list of
1024  * configuration selection.
1025  *
1026  * Configuration value to a boolean value, where yes/true and any
1027  * non-zero number means TRUE and other value is FALSE.
1028  *
1029  * @param context A Kerberos 5 context.
1030  * @param c a configuration section, or NULL to use the section from context
1031  * @param def_value the default value to return if no configuration
1032  *        found in the database.
1033  * @param args a va_list of arguments
1034  *
1035  * @return TRUE or FALSE
1036  *
1037  * @ingroup krb5_support
1038  */
1039
1040 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1041 krb5_config_vget_bool_default (krb5_context context,
1042                                const krb5_config_section *c,
1043                                krb5_boolean def_value,
1044                                va_list args)
1045 {
1046     const char *str;
1047     str = krb5_config_vget_string (context, c, args);
1048     if(str == NULL)
1049         return def_value;
1050     if(strcasecmp(str, "yes") == 0 ||
1051        strcasecmp(str, "true") == 0 ||
1052        atoi(str)) return TRUE;
1053     return FALSE;
1054 }
1055
1056 /**
1057  * krb5_config_get_bool() will convert the configuration
1058  * option value to a boolean value, where yes/true and any non-zero
1059  * number means TRUE and other value is FALSE.
1060  *
1061  * @param context A Kerberos 5 context.
1062  * @param c a configuration section, or NULL to use the section from context
1063  * @param args a va_list of arguments
1064  *
1065  * @return TRUE or FALSE
1066  *
1067  * @ingroup krb5_support
1068  */
1069
1070 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1071 krb5_config_vget_bool  (krb5_context context,
1072                         const krb5_config_section *c,
1073                         va_list args)
1074 {
1075     return krb5_config_vget_bool_default (context, c, FALSE, args);
1076 }
1077
1078 /**
1079  * krb5_config_get_bool_default() will convert the configuration
1080  * option value to a boolean value, where yes/true and any non-zero
1081  * number means TRUE and other value is FALSE.
1082  *
1083  * @param context A Kerberos 5 context.
1084  * @param c a configuration section, or NULL to use the section from context
1085  * @param def_value the default value to return if no configuration
1086  *        found in the database.
1087  * @param ... a list of names, terminated with NULL.
1088  *
1089  * @return TRUE or FALSE
1090  *
1091  * @ingroup krb5_support
1092  */
1093
1094 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1095 krb5_config_get_bool_default (krb5_context context,
1096                               const krb5_config_section *c,
1097                               krb5_boolean def_value,
1098                               ...)
1099 {
1100     va_list ap;
1101     krb5_boolean ret;
1102     va_start(ap, def_value);
1103     ret = krb5_config_vget_bool_default(context, c, def_value, ap);
1104     va_end(ap);
1105     return ret;
1106 }
1107
1108 /**
1109  * Like krb5_config_get_bool() but with a va_list list of
1110  * configuration selection.
1111  *
1112  * Configuration value to a boolean value, where yes/true and any
1113  * non-zero number means TRUE and other value is FALSE.
1114  *
1115  * @param context A Kerberos 5 context.
1116  * @param c a configuration section, or NULL to use the section from context
1117  * @param ... a list of names, terminated with NULL.
1118  *
1119  * @return TRUE or FALSE
1120  *
1121  * @ingroup krb5_support
1122  */
1123
1124 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1125 krb5_config_get_bool (krb5_context context,
1126                       const krb5_config_section *c,
1127                       ...)
1128 {
1129     va_list ap;
1130     krb5_boolean ret;
1131     va_start(ap, c);
1132     ret = krb5_config_vget_bool (context, c, ap);
1133     va_end(ap);
1134     return ret;
1135 }
1136
1137 /**
1138  * Get the time from the configuration file using a relative time.
1139  *
1140  * Like krb5_config_get_time_default() but with a va_list list of
1141  * configuration selection.
1142  *
1143  * @param context A Kerberos 5 context.
1144  * @param c a configuration section, or NULL to use the section from context
1145  * @param def_value the default value to return if no configuration
1146  *        found in the database.
1147  * @param args a va_list of arguments
1148  *
1149  * @return parsed the time (or def_value on parse error)
1150  *
1151  * @ingroup krb5_support
1152  */
1153
1154 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1155 krb5_config_vget_time_default (krb5_context context,
1156                                const krb5_config_section *c,
1157                                int def_value,
1158                                va_list args)
1159 {
1160     const char *str;
1161     krb5_deltat t;
1162
1163     str = krb5_config_vget_string (context, c, args);
1164     if(str == NULL)
1165         return def_value;
1166     if (krb5_string_to_deltat(str, &t))
1167         return def_value;
1168     return t;
1169 }
1170
1171 /**
1172  * Get the time from the configuration file using a relative time, for example: 1h30s
1173  *
1174  * @param context A Kerberos 5 context.
1175  * @param c a configuration section, or NULL to use the section from context
1176  * @param args a va_list of arguments
1177  *
1178  * @return parsed the time or -1 on error
1179  *
1180  * @ingroup krb5_support
1181  */
1182
1183 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1184 krb5_config_vget_time  (krb5_context context,
1185                         const krb5_config_section *c,
1186                         va_list args)
1187 {
1188     return krb5_config_vget_time_default (context, c, -1, args);
1189 }
1190
1191 /**
1192  * Get the time from the configuration file using a relative time, for example: 1h30s
1193  *
1194  * @param context A Kerberos 5 context.
1195  * @param c a configuration section, or NULL to use the section from context
1196  * @param def_value the default value to return if no configuration
1197  *        found in the database.
1198  * @param ... a list of names, terminated with NULL.
1199  *
1200  * @return parsed the time (or def_value on parse error)
1201  *
1202  * @ingroup krb5_support
1203  */
1204
1205 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1206 krb5_config_get_time_default (krb5_context context,
1207                               const krb5_config_section *c,
1208                               int def_value,
1209                               ...)
1210 {
1211     va_list ap;
1212     int ret;
1213     va_start(ap, def_value);
1214     ret = krb5_config_vget_time_default(context, c, def_value, ap);
1215     va_end(ap);
1216     return ret;
1217 }
1218
1219 /**
1220  * Get the time from the configuration file using a relative time, for example: 1h30s
1221  *
1222  * @param context A Kerberos 5 context.
1223  * @param c a configuration section, or NULL to use the section from context
1224  * @param ... a list of names, terminated with NULL.
1225  *
1226  * @return parsed the time or -1 on error
1227  *
1228  * @ingroup krb5_support
1229  */
1230
1231 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1232 krb5_config_get_time (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_time (context, c, ap);
1240     va_end(ap);
1241     return ret;
1242 }
1243
1244
1245 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1246 krb5_config_vget_int_default (krb5_context context,
1247                               const krb5_config_section *c,
1248                               int def_value,
1249                               va_list args)
1250 {
1251     const char *str;
1252     str = krb5_config_vget_string (context, c, args);
1253     if(str == NULL)
1254         return def_value;
1255     else {
1256         char *endptr;
1257         long l;
1258         l = strtol(str, &endptr, 0);
1259         if (endptr == str)
1260             return def_value;
1261         else
1262             return l;
1263     }
1264 }
1265
1266 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1267 krb5_config_vget_int  (krb5_context context,
1268                        const krb5_config_section *c,
1269                        va_list args)
1270 {
1271     return krb5_config_vget_int_default (context, c, -1, args);
1272 }
1273
1274 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1275 krb5_config_get_int_default (krb5_context context,
1276                              const krb5_config_section *c,
1277                              int def_value,
1278                              ...)
1279 {
1280     va_list ap;
1281     int ret;
1282     va_start(ap, def_value);
1283     ret = krb5_config_vget_int_default(context, c, def_value, ap);
1284     va_end(ap);
1285     return ret;
1286 }
1287
1288 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1289 krb5_config_get_int (krb5_context context,
1290                      const krb5_config_section *c,
1291                      ...)
1292 {
1293     va_list ap;
1294     int ret;
1295     va_start(ap, c);
1296     ret = krb5_config_vget_int (context, c, ap);
1297     va_end(ap);
1298     return ret;
1299 }
1300
1301
1302 #ifndef HEIMDAL_SMALLER
1303
1304 /**
1305  * Deprecated: configuration files are not strings
1306  *
1307  * @ingroup krb5_deprecated
1308  */
1309
1310 KRB5_DEPRECATED
1311 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1312 krb5_config_parse_string_multi(krb5_context context,
1313                                const char *string,
1314                                krb5_config_section **res)
1315 {
1316     const char *str;
1317     unsigned lineno = 0;
1318     krb5_error_code ret;
1319     struct fileptr f;
1320     f.f = NULL;
1321     f.s = string;
1322
1323     ret = krb5_config_parse_debug (&f, res, &lineno, &str);
1324     if (ret) {
1325         krb5_set_error_message (context, ret, "%s:%u: %s",
1326                                 "<constant>", lineno, str);
1327         return ret;
1328     }
1329     return 0;
1330 }
1331
1332 #endif