s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e89...
[samba.git] / source4 / heimdal / lib / krb5 / config_file.c
1 /*
2  * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "krb5_locl.h"
35
36 /* Gaah! I want a portable funopen */
37 struct fileptr {
38     const char *s;
39     FILE *f;
40 };
41
42 static char *
43 config_fgets(char *str, size_t len, struct fileptr *ptr)
44 {
45     /* XXX this is not correct, in that they don't do the same if the
46        line is longer than len */
47     if(ptr->f != NULL)
48         return fgets(str, len, ptr->f);
49     else {
50         /* this is almost strsep_copy */
51         const char *p;
52         ssize_t l;
53         if(*ptr->s == '\0')
54             return NULL;
55         p = ptr->s + strcspn(ptr->s, "\n");
56         if(*p == '\n')
57             p++;
58         l = min(len, p - ptr->s);
59         if(len > 0) {
60             memcpy(str, ptr->s, l);
61             str[l] = '\0';
62         }
63         ptr->s = p;
64         return str;
65     }
66 }
67
68 static krb5_error_code parse_section(char *p, krb5_config_section **s,
69                                      krb5_config_section **res,
70                                      const char **error_message);
71 static krb5_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
72                                      krb5_config_binding **b,
73                                      krb5_config_binding **parent,
74                                      const char **error_message);
75 static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
76                                   krb5_config_binding **parent,
77                                   const char **error_message);
78
79 static krb5_config_section *
80 get_entry(krb5_config_section **parent, const char *name, int type)
81 {
82     krb5_config_section **q;
83
84     for(q = parent; *q != NULL; q = &(*q)->next)
85         if(type == krb5_config_list &&
86            type == (*q)->type &&
87            strcmp(name, (*q)->name) == 0)
88             return *q;
89     *q = calloc(1, sizeof(**q));
90     if(*q == NULL)
91         return NULL;
92     (*q)->name = strdup(name);
93     (*q)->type = type;
94     if((*q)->name == NULL) {
95         free(*q);
96         *q = NULL;
97         return NULL;
98     }
99     return *q;
100 }
101
102 /*
103  * Parse a section:
104  *
105  * [section]
106  *      foo = bar
107  *      b = {
108  *              a
109  *          }
110  * ...
111  *
112  * starting at the line in `p', storing the resulting structure in
113  * `s' and hooking it into `parent'.
114  * Store the error message in `error_message'.
115  */
116
117 static krb5_error_code
118 parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
119               const char **error_message)
120 {
121     char *p1;
122     krb5_config_section *tmp;
123
124     p1 = strchr (p + 1, ']');
125     if (p1 == NULL) {
126         *error_message = "missing ]";
127         return KRB5_CONFIG_BADFORMAT;
128     }
129     *p1 = '\0';
130     tmp = get_entry(parent, p + 1, krb5_config_list);
131     if(tmp == NULL) {
132         *error_message = "out of memory";
133         return KRB5_CONFIG_BADFORMAT;
134     }
135     *s = tmp;
136     return 0;
137 }
138
139 /*
140  * Parse a brace-enclosed list from `f', hooking in the structure at
141  * `parent'.
142  * Store the error message in `error_message'.
143  */
144
145 static krb5_error_code
146 parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent,
147            const char **error_message)
148 {
149     char buf[BUFSIZ];
150     krb5_error_code ret;
151     krb5_config_binding *b = NULL;
152     unsigned beg_lineno = *lineno;
153
154     while(config_fgets(buf, sizeof(buf), f) != NULL) {
155         char *p;
156
157         ++*lineno;
158         buf[strcspn(buf, "\r\n")] = '\0';
159         p = buf;
160         while(isspace((unsigned char)*p))
161             ++p;
162         if (*p == '#' || *p == ';' || *p == '\0')
163             continue;
164         while(isspace((unsigned char)*p))
165             ++p;
166         if (*p == '}')
167             return 0;
168         if (*p == '\0')
169             continue;
170         ret = parse_binding (f, lineno, p, &b, parent, error_message);
171         if (ret)
172             return ret;
173     }
174     *lineno = beg_lineno;
175     *error_message = "unclosed {";
176     return KRB5_CONFIG_BADFORMAT;
177 }
178
179 /*
180  *
181  */
182
183 static krb5_error_code
184 parse_binding(struct fileptr *f, unsigned *lineno, char *p,
185               krb5_config_binding **b, krb5_config_binding **parent,
186               const char **error_message)
187 {
188     krb5_config_binding *tmp;
189     char *p1, *p2;
190     krb5_error_code ret = 0;
191
192     p1 = p;
193     while (*p && *p != '=' && !isspace((unsigned char)*p))
194         ++p;
195     if (*p == '\0') {
196         *error_message = "missing =";
197         return KRB5_CONFIG_BADFORMAT;
198     }
199     p2 = p;
200     while (isspace((unsigned char)*p))
201         ++p;
202     if (*p != '=') {
203         *error_message = "missing =";
204         return KRB5_CONFIG_BADFORMAT;
205     }
206     ++p;
207     while(isspace((unsigned char)*p))
208         ++p;
209     *p2 = '\0';
210     if (*p == '{') {
211         tmp = get_entry(parent, p1, krb5_config_list);
212         if (tmp == NULL) {
213             *error_message = "out of memory";
214             return KRB5_CONFIG_BADFORMAT;
215         }
216         ret = parse_list (f, lineno, &tmp->u.list, error_message);
217     } else {
218         tmp = get_entry(parent, p1, krb5_config_string);
219         if (tmp == NULL) {
220             *error_message = "out of memory";
221             return KRB5_CONFIG_BADFORMAT;
222         }
223         p1 = p;
224         p = p1 + strlen(p1);
225         while(p > p1 && isspace((unsigned char)*(p-1)))
226             --p;
227         *p = '\0';
228         tmp->u.string = strdup(p1);
229     }
230     *b = tmp;
231     return ret;
232 }
233
234 /*
235  * Parse the config file `fname', generating the structures into `res'
236  * returning error messages in `error_message'
237  */
238
239 static krb5_error_code
240 krb5_config_parse_debug (struct fileptr *f,
241                          krb5_config_section **res,
242                          unsigned *lineno,
243                          const char **error_message)
244 {
245     krb5_config_section *s = NULL;
246     krb5_config_binding *b = NULL;
247     char buf[BUFSIZ];
248     krb5_error_code ret;
249
250     while (config_fgets(buf, sizeof(buf), f) != NULL) {
251         char *p;
252
253         ++*lineno;
254         buf[strcspn(buf, "\r\n")] = '\0';
255         p = buf;
256         while(isspace((unsigned char)*p))
257             ++p;
258         if (*p == '#' || *p == ';')
259             continue;
260         if (*p == '[') {
261             ret = parse_section(p, &s, res, error_message);
262             if (ret)
263                 return ret;
264             b = NULL;
265         } else if (*p == '}') {
266             *error_message = "unmatched }";
267             return EINVAL;      /* XXX */
268         } else if(*p != '\0') {
269             if (s == NULL) {
270                 *error_message = "binding before section";
271                 return EINVAL;
272             }
273             ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message);
274             if (ret)
275                 return ret;
276         }
277     }
278     return 0;
279 }
280
281 krb5_error_code KRB5_LIB_FUNCTION
282 krb5_config_parse_string_multi(krb5_context context,
283                                const char *string,
284                                krb5_config_section **res)
285 {
286     const char *str;
287     unsigned lineno = 0;
288     krb5_error_code ret;
289     struct fileptr f;
290     f.f = NULL;
291     f.s = string;
292
293     ret = krb5_config_parse_debug (&f, res, &lineno, &str);
294     if (ret) {
295         krb5_set_error_message (context, ret, "%s:%u: %s",
296                                 "<constant>", lineno, str);
297         return ret;
298     }
299     return 0;
300 }
301
302 /**
303  * Parse a configuration file and add the result into res. This
304  * interface can be used to parse several configuration files into one
305  * resulting krb5_config_section by calling it repeatably.
306  *
307  * @param context a Kerberos 5 context.
308  * @param fname a file name to a Kerberos configuration file
309  * @param res the returned result, must be free with krb5_free_config_files().
310  * @return Return an error code or 0, see krb5_get_error_message().
311  *
312  * @ingroup krb5_support
313  */
314
315 krb5_error_code KRB5_LIB_FUNCTION
316 krb5_config_parse_file_multi (krb5_context context,
317                               const char *fname,
318                               krb5_config_section **res)
319 {
320     const char *str;
321     char *newfname = NULL;
322     unsigned lineno = 0;
323     krb5_error_code ret;
324     struct fileptr f;
325
326     /**
327      * If the fname starts with "~/" parse configuration file in the
328      * current users home directory. The behavior can be disabled and
329      * enabled by calling krb5_set_home_dir_access().
330      */
331     if (_krb5_homedir_access(context) && fname[0] == '~' && fname[1] == '/') {
332         const char *home = NULL;
333
334         if(!issuid())
335             home = getenv("HOME");
336
337         if (home == NULL) {
338             struct passwd *pw = getpwuid(getuid());     
339             if(pw != NULL)
340                 home = pw->pw_dir;
341         }
342         if (home) {
343             asprintf(&newfname, "%s%s", home, &fname[1]);
344             if (newfname == NULL) {
345                 krb5_set_error_message(context, ENOMEM,
346                                        N_("malloc: out of memory", ""));
347                 return ENOMEM;
348             }
349             fname = newfname;
350         }
351     }
352
353     f.f = fopen(fname, "r");
354     f.s = NULL;
355     if(f.f == NULL) {
356         ret = errno;
357         krb5_set_error_message (context, ret, "open %s: %s",
358                                 fname, strerror(ret));
359         if (newfname)
360             free(newfname);
361         return ret;
362     }
363
364     ret = krb5_config_parse_debug (&f, res, &lineno, &str);
365     fclose(f.f);
366     if (ret) {
367         krb5_set_error_message (context, ret, "%s:%u: %s", fname, lineno, str);
368         if (newfname)
369             free(newfname);
370         return ret;
371     }
372     if (newfname)
373         free(newfname);
374     return 0;
375 }
376
377 krb5_error_code KRB5_LIB_FUNCTION
378 krb5_config_parse_file (krb5_context context,
379                         const char *fname,
380                         krb5_config_section **res)
381 {
382     *res = NULL;
383     return krb5_config_parse_file_multi(context, fname, res);
384 }
385
386 static void
387 free_binding (krb5_context context, krb5_config_binding *b)
388 {
389     krb5_config_binding *next_b;
390
391     while (b) {
392         free (b->name);
393         if (b->type == krb5_config_string)
394             free (b->u.string);
395         else if (b->type == krb5_config_list)
396             free_binding (context, b->u.list);
397         else
398             krb5_abortx(context, "unknown binding type (%d) in free_binding",
399                         b->type);
400         next_b = b->next;
401         free (b);
402         b = next_b;
403     }
404 }
405
406 krb5_error_code KRB5_LIB_FUNCTION
407 krb5_config_file_free (krb5_context context, krb5_config_section *s)
408 {
409     free_binding (context, s);
410     return 0;
411 }
412
413 krb5_error_code
414 _krb5_config_copy(krb5_context context,
415                   krb5_config_section *c,
416                   krb5_config_section **head)
417 {
418     krb5_config_binding *d, *previous = NULL;
419
420     *head = NULL;
421
422     while (c) {
423         d = calloc(1, sizeof(*d));
424
425         if (*head == NULL)
426             *head = d;
427
428         d->name = strdup(c->name);
429         d->type = c->type;
430         if (d->type == krb5_config_string)
431             d->u.string = strdup(c->u.string);
432         else if (d->type == krb5_config_list)
433             _krb5_config_copy (context, c->u.list, &d->u.list);
434         else
435             krb5_abortx(context,
436                         "unknown binding type (%d) in krb5_config_copy",
437                         d->type);
438         if (previous)
439             previous->next = d;
440
441         previous = d;
442         c = c->next;
443     }
444     return 0;
445 }
446
447
448
449 const void *
450 krb5_config_get_next (krb5_context context,
451                       const krb5_config_section *c,
452                       const krb5_config_binding **pointer,
453                       int type,
454                       ...)
455 {
456     const char *ret;
457     va_list args;
458
459     va_start(args, type);
460     ret = krb5_config_vget_next (context, c, pointer, type, args);
461     va_end(args);
462     return ret;
463 }
464
465 static const void *
466 vget_next(krb5_context context,
467           const krb5_config_binding *b,
468           const krb5_config_binding **pointer,
469           int type,
470           const char *name,
471           va_list args)
472 {
473     const char *p = va_arg(args, const char *);
474     while(b != NULL) {
475         if(strcmp(b->name, name) == 0) {
476             if(b->type == type && p == NULL) {
477                 *pointer = b;
478                 return b->u.generic;
479             } else if(b->type == krb5_config_list && p != NULL) {
480                 return vget_next(context, b->u.list, pointer, type, p, args);
481             }
482         }
483         b = b->next;
484     }
485     return NULL;
486 }
487
488 const void *
489 krb5_config_vget_next (krb5_context context,
490                        const krb5_config_section *c,
491                        const krb5_config_binding **pointer,
492                        int type,
493                        va_list args)
494 {
495     const krb5_config_binding *b;
496     const char *p;
497
498     if(c == NULL)
499         c = context->cf;
500
501     if (c == NULL)
502         return NULL;
503
504     if (*pointer == NULL) {
505         /* first time here, walk down the tree looking for the right
506            section */
507         p = va_arg(args, const char *);
508         if (p == NULL)
509             return NULL;
510         return vget_next(context, c, pointer, type, p, args);
511     }
512
513     /* we were called again, so just look for more entries with the
514        same name and type */
515     for (b = (*pointer)->next; b != NULL; b = b->next) {
516         if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) {
517             *pointer = b;
518             return b->u.generic;
519         }
520     }
521     return NULL;
522 }
523
524 const void *
525 krb5_config_get (krb5_context context,
526                  const krb5_config_section *c,
527                  int type,
528                  ...)
529 {
530     const void *ret;
531     va_list args;
532
533     va_start(args, type);
534     ret = krb5_config_vget (context, c, type, args);
535     va_end(args);
536     return ret;
537 }
538
539 const void *
540 krb5_config_vget (krb5_context context,
541                   const krb5_config_section *c,
542                   int type,
543                   va_list args)
544 {
545     const krb5_config_binding *foo = NULL;
546
547     return krb5_config_vget_next (context, c, &foo, type, args);
548 }
549
550 const krb5_config_binding *
551 krb5_config_get_list (krb5_context context,
552                       const krb5_config_section *c,
553                       ...)
554 {
555     const krb5_config_binding *ret;
556     va_list args;
557
558     va_start(args, c);
559     ret = krb5_config_vget_list (context, c, args);
560     va_end(args);
561     return ret;
562 }
563
564 const krb5_config_binding *
565 krb5_config_vget_list (krb5_context context,
566                        const krb5_config_section *c,
567                        va_list args)
568 {
569     return krb5_config_vget (context, c, krb5_config_list, args);
570 }
571
572 const char* KRB5_LIB_FUNCTION
573 krb5_config_get_string (krb5_context context,
574                         const krb5_config_section *c,
575                         ...)
576 {
577     const char *ret;
578     va_list args;
579
580     va_start(args, c);
581     ret = krb5_config_vget_string (context, c, args);
582     va_end(args);
583     return ret;
584 }
585
586 const char* KRB5_LIB_FUNCTION
587 krb5_config_vget_string (krb5_context context,
588                          const krb5_config_section *c,
589                          va_list args)
590 {
591     return krb5_config_vget (context, c, krb5_config_string, args);
592 }
593
594 const char* KRB5_LIB_FUNCTION
595 krb5_config_vget_string_default (krb5_context context,
596                                  const krb5_config_section *c,
597                                  const char *def_value,
598                                  va_list args)
599 {
600     const char *ret;
601
602     ret = krb5_config_vget_string (context, c, args);
603     if (ret == NULL)
604         ret = def_value;
605     return ret;
606 }
607
608 const char* KRB5_LIB_FUNCTION
609 krb5_config_get_string_default (krb5_context context,
610                                 const krb5_config_section *c,
611                                 const char *def_value,
612                                 ...)
613 {
614     const char *ret;
615     va_list args;
616
617     va_start(args, def_value);
618     ret = krb5_config_vget_string_default (context, c, def_value, args);
619     va_end(args);
620     return ret;
621 }
622
623 char ** KRB5_LIB_FUNCTION
624 krb5_config_vget_strings(krb5_context context,
625                          const krb5_config_section *c,
626                          va_list args)
627 {
628     char **strings = NULL;
629     int nstr = 0;
630     const krb5_config_binding *b = NULL;
631     const char *p;
632
633     while((p = krb5_config_vget_next(context, c, &b,
634                                      krb5_config_string, args))) {
635         char *tmp = strdup(p);
636         char *pos = NULL;
637         char *s;
638         if(tmp == NULL)
639             goto cleanup;
640         s = strtok_r(tmp, " \t", &pos);
641         while(s){
642             char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
643             if(tmp2 == NULL)
644                 goto cleanup;
645             strings = tmp2;
646             strings[nstr] = strdup(s);
647             nstr++;
648             if(strings[nstr-1] == NULL)
649                 goto cleanup;
650             s = strtok_r(NULL, " \t", &pos);
651         }
652         free(tmp);
653     }
654     if(nstr){
655         char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
656         if(tmp == NULL)
657             goto cleanup;
658         strings = tmp;
659         strings[nstr] = NULL;
660     }
661     return strings;
662 cleanup:
663     while(nstr--)
664         free(strings[nstr]);
665     free(strings);
666     return NULL;
667
668 }
669
670 char**
671 krb5_config_get_strings(krb5_context context,
672                         const krb5_config_section *c,
673                         ...)
674 {
675     va_list ap;
676     char **ret;
677     va_start(ap, c);
678     ret = krb5_config_vget_strings(context, c, ap);
679     va_end(ap);
680     return ret;
681 }
682
683 void KRB5_LIB_FUNCTION
684 krb5_config_free_strings(char **strings)
685 {
686     char **s = strings;
687     while(s && *s){
688         free(*s);
689         s++;
690     }
691     free(strings);
692 }
693
694 krb5_boolean KRB5_LIB_FUNCTION
695 krb5_config_vget_bool_default (krb5_context context,
696                                const krb5_config_section *c,
697                                krb5_boolean def_value,
698                                va_list args)
699 {
700     const char *str;
701     str = krb5_config_vget_string (context, c, args);
702     if(str == NULL)
703         return def_value;
704     if(strcasecmp(str, "yes") == 0 ||
705        strcasecmp(str, "true") == 0 ||
706        atoi(str)) return TRUE;
707     return FALSE;
708 }
709
710 krb5_boolean KRB5_LIB_FUNCTION
711 krb5_config_vget_bool  (krb5_context context,
712                         const krb5_config_section *c,
713                         va_list args)
714 {
715     return krb5_config_vget_bool_default (context, c, FALSE, args);
716 }
717
718 krb5_boolean KRB5_LIB_FUNCTION
719 krb5_config_get_bool_default (krb5_context context,
720                               const krb5_config_section *c,
721                               krb5_boolean def_value,
722                               ...)
723 {
724     va_list ap;
725     krb5_boolean ret;
726     va_start(ap, def_value);
727     ret = krb5_config_vget_bool_default(context, c, def_value, ap);
728     va_end(ap);
729     return ret;
730 }
731
732 krb5_boolean KRB5_LIB_FUNCTION
733 krb5_config_get_bool (krb5_context context,
734                       const krb5_config_section *c,
735                       ...)
736 {
737     va_list ap;
738     krb5_boolean ret;
739     va_start(ap, c);
740     ret = krb5_config_vget_bool (context, c, ap);
741     va_end(ap);
742     return ret;
743 }
744
745 int KRB5_LIB_FUNCTION
746 krb5_config_vget_time_default (krb5_context context,
747                                const krb5_config_section *c,
748                                int def_value,
749                                va_list args)
750 {
751     const char *str;
752     krb5_deltat t;
753
754     str = krb5_config_vget_string (context, c, args);
755     if(str == NULL)
756         return def_value;
757     if (krb5_string_to_deltat(str, &t))
758         return def_value;
759     return t;
760 }
761
762 int KRB5_LIB_FUNCTION
763 krb5_config_vget_time  (krb5_context context,
764                         const krb5_config_section *c,
765                         va_list args)
766 {
767     return krb5_config_vget_time_default (context, c, -1, args);
768 }
769
770 int KRB5_LIB_FUNCTION
771 krb5_config_get_time_default (krb5_context context,
772                               const krb5_config_section *c,
773                               int def_value,
774                               ...)
775 {
776     va_list ap;
777     int ret;
778     va_start(ap, def_value);
779     ret = krb5_config_vget_time_default(context, c, def_value, ap);
780     va_end(ap);
781     return ret;
782 }
783
784 int KRB5_LIB_FUNCTION
785 krb5_config_get_time (krb5_context context,
786                       const krb5_config_section *c,
787                       ...)
788 {
789     va_list ap;
790     int ret;
791     va_start(ap, c);
792     ret = krb5_config_vget_time (context, c, ap);
793     va_end(ap);
794     return ret;
795 }
796
797
798 int KRB5_LIB_FUNCTION
799 krb5_config_vget_int_default (krb5_context context,
800                               const krb5_config_section *c,
801                               int def_value,
802                               va_list args)
803 {
804     const char *str;
805     str = krb5_config_vget_string (context, c, args);
806     if(str == NULL)
807         return def_value;
808     else {
809         char *endptr;
810         long l;
811         l = strtol(str, &endptr, 0);
812         if (endptr == str)
813             return def_value;
814         else
815             return l;
816     }
817 }
818
819 int KRB5_LIB_FUNCTION
820 krb5_config_vget_int  (krb5_context context,
821                        const krb5_config_section *c,
822                        va_list args)
823 {
824     return krb5_config_vget_int_default (context, c, -1, args);
825 }
826
827 int KRB5_LIB_FUNCTION
828 krb5_config_get_int_default (krb5_context context,
829                              const krb5_config_section *c,
830                              int def_value,
831                              ...)
832 {
833     va_list ap;
834     int ret;
835     va_start(ap, def_value);
836     ret = krb5_config_vget_int_default(context, c, def_value, ap);
837     va_end(ap);
838     return ret;
839 }
840
841 int KRB5_LIB_FUNCTION
842 krb5_config_get_int (krb5_context context,
843                      const krb5_config_section *c,
844                      ...)
845 {
846     va_list ap;
847     int ret;
848     va_start(ap, c);
849     ret = krb5_config_vget_int (context, c, ap);
850     va_end(ap);
851     return ret;
852 }