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