xref: /freebsd/crypto/krb5/src/util/profile/prof_get.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * prof_get.c --- routines that expose the public interfaces for
4  *      querying items from the profile.
5  *
6  */
7 
8 #include "prof_int.h"
9 #include <stdio.h>
10 #include <string.h>
11 #ifdef HAVE_STDLIB_H
12 #include <stdlib.h>
13 #endif
14 #include <errno.h>
15 #include <limits.h>
16 
17 /*
18  * These functions --- init_list(), end_list(), and add_to_list() are
19  * internal functions used to build up a null-terminated char ** list
20  * of strings to be returned by functions like profile_get_values.
21  *
22  * The profile_string_list structure is used for internal booking
23  * purposes to build up the list, which is returned in *ret_list by
24  * the end_list() function.
25  *
26  * The publicly exported interface for freeing char** list is
27  * profile_free_list().
28  */
29 
30 struct profile_string_list {
31     char    **list;
32     unsigned int    num;
33     unsigned int    max;
34 };
35 
36 /*
37  * Initialize the string list abstraction.
38  */
init_list(struct profile_string_list * list)39 static errcode_t init_list(struct profile_string_list *list)
40 {
41     list->num = 0;
42     list->max = 10;
43     list->list = malloc(list->max * sizeof(char *));
44     if (list->list == 0)
45         return ENOMEM;
46     list->list[0] = 0;
47     return 0;
48 }
49 
50 /*
51  * Free any memory left over in the string abstraction, returning the
52  * built up list in *ret_list if it is non-null.
53  */
end_list(struct profile_string_list * list,char *** ret_list)54 static void end_list(struct profile_string_list *list, char ***ret_list)
55 {
56     char    **cp;
57 
58     if (list == 0)
59         return;
60 
61     if (ret_list) {
62         *ret_list = list->list;
63         return;
64     } else {
65         for (cp = list->list; cp && *cp; cp++)
66             free(*cp);
67         free(list->list);
68     }
69     list->num = list->max = 0;
70     list->list = 0;
71 }
72 
73 /*
74  * Add a string to the list.
75  */
add_to_list(struct profile_string_list * list,const char * str)76 static errcode_t add_to_list(struct profile_string_list *list, const char *str)
77 {
78     char    *newstr, **newlist;
79     unsigned int    newmax;
80 
81     if (list->num+1 >= list->max) {
82         newmax = list->max + 10;
83         newlist = realloc(list->list, newmax * sizeof(char *));
84         if (newlist == 0)
85             return ENOMEM;
86         list->max = newmax;
87         list->list = newlist;
88     }
89     newstr = strdup(str);
90     if (newstr == 0)
91         return ENOMEM;
92 
93     list->list[list->num++] = newstr;
94     list->list[list->num] = 0;
95     return 0;
96 }
97 
98 /*
99  * Return TRUE if the string is already a member of the list.
100  */
is_list_member(struct profile_string_list * list,const char * str)101 static int is_list_member(struct profile_string_list *list, const char *str)
102 {
103     char **cpp;
104 
105     if (!list->list)
106         return 0;
107 
108     for (cpp = list->list; *cpp; cpp++) {
109         if (!strcmp(*cpp, str))
110             return 1;
111     }
112     return 0;
113 }
114 
115 /*
116  * This function frees a null-terminated list as returned by
117  * profile_get_values.
118  */
profile_free_list(char ** list)119 void KRB5_CALLCONV profile_free_list(char **list)
120 {
121     char        **cp;
122 
123     if (list == 0)
124         return;
125 
126     for (cp = list; *cp; cp++)
127         free(*cp);
128     free(list);
129 }
130 
131 /* Look up a relation in a vtable profile. */
132 static errcode_t
get_values_vt(profile_t profile,const char * const * names,char *** ret_values)133 get_values_vt(profile_t profile, const char *const *names, char ***ret_values)
134 {
135     errcode_t               retval;
136     char                    **vtvalues, **val;
137     struct profile_string_list values;
138 
139     retval = profile->vt->get_values(profile->cbdata, names, &vtvalues);
140     if (retval)
141         return retval;
142 
143     /* Copy the result into memory we can free. */
144     retval = init_list(&values);
145     if (retval == 0) {
146         for (val = vtvalues; *val; val++)
147             add_to_list(&values, *val);
148         end_list(&values, ret_values);
149     }
150 
151     profile->vt->free_values(profile->cbdata, vtvalues);
152     return retval;
153 }
154 
155 errcode_t KRB5_CALLCONV
profile_get_values(profile_t profile,const char * const * names,char *** ret_values)156 profile_get_values(profile_t profile, const char *const *names,
157                    char ***ret_values)
158 {
159     errcode_t               retval;
160     void                    *state = NULL;
161     char                    *value;
162     struct profile_string_list values;
163 
164     *ret_values = NULL;
165     if (!profile)
166         return PROF_NO_PROFILE;
167     if (profile->vt)
168         return get_values_vt(profile, names, ret_values);
169 
170     if ((retval = profile_node_iterator_create(profile, names,
171                                                PROFILE_ITER_RELATIONS_ONLY,
172                                                &state)))
173         return retval;
174 
175     retval = init_list(&values);
176     if (retval)
177         goto cleanup;
178 
179     do {
180         if ((retval = profile_node_iterator(&state, 0, 0, &value)))
181             goto cleanup;
182         if (value)
183             add_to_list(&values, value);
184     } while (state);
185 
186     if (values.num == 0) {
187         retval = PROF_NO_RELATION;
188         goto cleanup;
189     }
190 
191 cleanup:
192     end_list(&values, retval ? NULL : ret_values);
193     profile_node_iterator_free(&state);
194     return retval;
195 }
196 
197 /* Look up a relation in a vtable profile and return the first value in the
198  * result. */
199 static errcode_t
get_value_vt(profile_t profile,const char * const * names,char ** ret_value)200 get_value_vt(profile_t profile, const char *const *names, char **ret_value)
201 {
202     errcode_t               retval;
203     char                    **vtvalues;
204 
205     retval = profile->vt->get_values(profile->cbdata, names, &vtvalues);
206     if (retval)
207         return retval;
208     *ret_value = strdup(*vtvalues);
209     if (*ret_value == NULL)
210         retval = ENOMEM;
211     profile->vt->free_values(profile->cbdata, vtvalues);
212     return retval;
213 }
214 
215 /*
216  * This function only gets the first value from the file; it is a
217  * helper function for profile_get_string, profile_get_integer, etc.
218  */
profile_get_value(profile_t profile,const char ** names,char ** ret_value)219 errcode_t profile_get_value(profile_t profile, const char **names,
220                             char **ret_value)
221 {
222     errcode_t               retval;
223     void                    *state;
224     char                    *value;
225 
226     *ret_value = NULL;
227     if (!profile)
228         return PROF_NO_PROFILE;
229     if (profile->vt)
230         return get_value_vt(profile, names, ret_value);
231 
232     retval = profile_iterator_create(profile, names,
233                                      PROFILE_ITER_RELATIONS_ONLY, &state);
234     if (retval)
235         return retval;
236 
237     retval = profile_iterator(&state, NULL, &value);
238     if (retval)
239         goto cleanup;
240 
241     if (value)
242         *ret_value = value;
243     else
244         retval = PROF_NO_RELATION;
245 
246 cleanup:
247     profile_iterator_free(&state);
248     return retval;
249 }
250 
251 errcode_t KRB5_CALLCONV
profile_get_string(profile_t profile,const char * name,const char * subname,const char * subsubname,const char * def_val,char ** ret_string)252 profile_get_string(profile_t profile, const char *name, const char *subname,
253                    const char *subsubname, const char *def_val,
254                    char **ret_string)
255 {
256     char            *value;
257     errcode_t       retval;
258     const char      *names[4];
259 
260     if (profile) {
261         names[0] = name;
262         names[1] = subname;
263         names[2] = subsubname;
264         names[3] = 0;
265         retval = profile_get_value(profile, names, &value);
266         if (retval == 0) {
267             *ret_string = value;
268             return 0;
269         } else if (retval != PROF_NO_SECTION && retval != PROF_NO_RELATION)
270             return retval;
271     }
272 
273     if (def_val) {
274         *ret_string = strdup(def_val);
275         if (*ret_string == NULL)
276             return ENOMEM;
277     } else
278         *ret_string = NULL;
279     return 0;
280 }
281 
282 static errcode_t
parse_int(const char * value,int * ret_int)283 parse_int(const char *value, int *ret_int)
284 {
285     char            *end_value;
286     long            ret_long;
287 
288     if (value[0] == 0)
289         /* Empty string is no good.  */
290         return PROF_BAD_INTEGER;
291     errno = 0;
292     ret_long = strtol(value, &end_value, 10);
293 
294     /* Overflow or underflow.  */
295     if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0)
296         return PROF_BAD_INTEGER;
297     /* Value outside "int" range.  */
298     if ((long) (int) ret_long != ret_long)
299         return PROF_BAD_INTEGER;
300     /* Garbage in string.  */
301     if (end_value != value + strlen (value))
302         return PROF_BAD_INTEGER;
303 
304     *ret_int = ret_long;
305     return 0;
306 }
307 
308 errcode_t KRB5_CALLCONV
profile_get_integer(profile_t profile,const char * name,const char * subname,const char * subsubname,int def_val,int * ret_int)309 profile_get_integer(profile_t profile, const char *name, const char *subname,
310                     const char *subsubname, int def_val, int *ret_int)
311 {
312     char            *value;
313     errcode_t       retval;
314     const char      *names[4];
315 
316     *ret_int = def_val;
317     if (profile == 0)
318         return 0;
319 
320     names[0] = name;
321     names[1] = subname;
322     names[2] = subsubname;
323     names[3] = 0;
324     retval = profile_get_value(profile, names, &value);
325     if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
326         *ret_int = def_val;
327         return 0;
328     } else if (retval)
329         return retval;
330 
331     retval = parse_int(value, ret_int);
332     free(value);
333     return retval;
334 }
335 
336 static const char *const conf_yes[] = {
337     "y", "yes", "true", "t", "1", "on",
338     0,
339 };
340 
341 static const char *const conf_no[] = {
342     "n", "no", "false", "nil", "0", "off",
343     0,
344 };
345 
346 static errcode_t
profile_parse_boolean(const char * s,int * ret_boolean)347 profile_parse_boolean(const char *s, int *ret_boolean)
348 {
349     const char *const *p;
350 
351     if (ret_boolean == NULL)
352         return PROF_EINVAL;
353 
354     for(p=conf_yes; *p; p++) {
355         if (!strcasecmp(*p,s)) {
356             *ret_boolean = 1;
357             return 0;
358         }
359     }
360 
361     for(p=conf_no; *p; p++) {
362         if (!strcasecmp(*p,s)) {
363             *ret_boolean = 0;
364             return 0;
365         }
366     }
367 
368     return PROF_BAD_BOOLEAN;
369 }
370 
371 errcode_t KRB5_CALLCONV
profile_get_boolean(profile_t profile,const char * name,const char * subname,const char * subsubname,int def_val,int * ret_boolean)372 profile_get_boolean(profile_t profile, const char *name, const char *subname,
373                     const char *subsubname, int def_val, int *ret_boolean)
374 {
375     char            *value;
376     errcode_t       retval;
377     const char      *names[4];
378 
379     if (profile == 0) {
380         *ret_boolean = def_val;
381         return 0;
382     }
383 
384     names[0] = name;
385     names[1] = subname;
386     names[2] = subsubname;
387     names[3] = 0;
388     retval = profile_get_value(profile, names, &value);
389     if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
390         *ret_boolean = def_val;
391         return 0;
392     } else if (retval)
393         return retval;
394 
395     retval = profile_parse_boolean(value, ret_boolean);
396     free(value);
397     return retval;
398 }
399 
400 /*
401  * This function will return the list of the names of subections in the
402  * under the specified section name.
403  */
404 errcode_t KRB5_CALLCONV
profile_get_subsection_names(profile_t profile,const char ** names,char *** ret_names)405 profile_get_subsection_names(profile_t profile, const char **names,
406                              char ***ret_names)
407 {
408     errcode_t               retval;
409     void                    *state;
410     char                    *name;
411     struct profile_string_list values;
412 
413     if ((retval = profile_iterator_create(profile, names,
414                                           PROFILE_ITER_LIST_SECTION |
415                                           PROFILE_ITER_SECTIONS_ONLY,
416                                           &state)))
417         return retval;
418 
419     if ((retval = init_list(&values)))
420         return retval;
421 
422     do {
423         if ((retval = profile_iterator(&state, &name, NULL)))
424             goto cleanup;
425         if (name)
426             add_to_list(&values, name);
427         free(name);
428     } while (state);
429 
430     end_list(&values, ret_names);
431     return 0;
432 
433 cleanup:
434     end_list(&values, 0);
435     return retval;
436 }
437 
438 /*
439  * This function will return the list of the names of relations in the
440  * under the specified section name.
441  */
442 errcode_t KRB5_CALLCONV
profile_get_relation_names(profile_t profile,const char ** names,char *** ret_names)443 profile_get_relation_names(profile_t profile, const char **names,
444                            char ***ret_names)
445 {
446     errcode_t               retval;
447     void                    *state;
448     char                    *name;
449     struct profile_string_list values;
450 
451     if ((retval = profile_iterator_create(profile, names,
452                                           PROFILE_ITER_LIST_SECTION |
453                                           PROFILE_ITER_RELATIONS_ONLY,
454                                           &state)))
455         return retval;
456 
457     if ((retval = init_list(&values)))
458         return retval;
459 
460     do {
461         if ((retval = profile_iterator(&state, &name, NULL)))
462             goto cleanup;
463         if (name && !is_list_member(&values, name))
464             add_to_list(&values, name);
465         free(name);
466     } while (state);
467 
468     end_list(&values, ret_names);
469     return 0;
470 
471 cleanup:
472     end_list(&values, 0);
473     return retval;
474 }
475 
476 struct profile_iterator {
477     prf_magic_t magic;
478     profile_t profile;
479     void *idata;
480 };
481 
482 errcode_t KRB5_CALLCONV
profile_iterator_create(profile_t profile,const char * const * names,int flags,void ** ret_iter)483 profile_iterator_create(profile_t profile, const char *const *names, int flags,
484                         void **ret_iter)
485 {
486     struct profile_iterator *iter;
487     errcode_t retval;
488 
489     *ret_iter = NULL;
490     if (!profile)
491         return PROF_NO_PROFILE;
492 
493     iter = malloc(sizeof(*iter));
494     if (iter == NULL)
495         return ENOMEM;
496     iter->magic = PROF_MAGIC_ITERATOR;
497     iter->profile = profile;
498 
499     /* Create the underlying iterator representation using the vtable or the
500      * built-in node iterator. */
501     if (profile->vt) {
502         if (!profile->vt->iterator_create)
503             retval = PROF_UNSUPPORTED;
504         else
505             retval = profile->vt->iterator_create(profile->cbdata, names,
506                                                   flags, &iter->idata);
507     } else {
508         retval = profile_node_iterator_create(profile, names, flags,
509                                               &iter->idata);
510     }
511     if (retval) {
512         free(iter);
513         return retval;
514     }
515 
516     *ret_iter = iter;
517     return 0;
518 }
519 
520 void KRB5_CALLCONV
profile_iterator_free(void ** iter_p)521 profile_iterator_free(void **iter_p)
522 {
523     struct profile_iterator *iter;
524     profile_t profile;
525 
526     if (!iter_p)
527         return;
528     iter = *iter_p;
529     if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
530         return;
531     profile = iter->profile;
532     if (profile->vt)
533         profile->vt->iterator_free(profile->cbdata, iter->idata);
534     else
535         profile_node_iterator_free(&iter->idata);
536     free(iter);
537     *iter_p = NULL;
538 }
539 
540 /* Make copies of name and value into *ret_name and *ret_value.  Handle null
541  * values of any argument. */
542 static errcode_t
set_results(const char * name,const char * value,char ** ret_name,char ** ret_value)543 set_results(const char *name, const char *value, char **ret_name,
544             char **ret_value)
545 {
546     char *name_copy = NULL, *value_copy = NULL;
547 
548     if (ret_name && name) {
549         name_copy = strdup(name);
550         if (name_copy == NULL)
551             goto oom;
552     }
553     if (ret_value && value) {
554         value_copy = strdup(value);
555         if (value_copy == NULL)
556             goto oom;
557     }
558     if (ret_name)
559         *ret_name = name_copy;
560     if (ret_value)
561         *ret_value = value_copy;
562     return 0;
563 oom:
564     free(name_copy);
565     free(value_copy);
566     return ENOMEM;
567 }
568 
569 errcode_t KRB5_CALLCONV
profile_iterator(void ** iter_p,char ** ret_name,char ** ret_value)570 profile_iterator(void **iter_p, char **ret_name, char **ret_value)
571 {
572     char *name, *value;
573     errcode_t       retval;
574     struct profile_iterator *iter = *iter_p;
575     profile_t profile;
576 
577     if (ret_name)
578         *ret_name = NULL;
579     if (ret_value)
580         *ret_value = NULL;
581     if (iter == NULL || iter->magic != PROF_MAGIC_ITERATOR)
582         return PROF_MAGIC_ITERATOR;
583     profile = iter->profile;
584 
585     if (profile->vt) {
586         retval = profile->vt->iterator(profile->cbdata, iter->idata, &name,
587                                        &value);
588         if (retval)
589             return retval;
590         if (name == NULL) {
591             profile->vt->iterator_free(profile->cbdata, iter->idata);
592             free(iter);
593             *iter_p = NULL;
594         }
595         retval = set_results(name, value, ret_name, ret_value);
596         if (name)
597             profile->vt->free_string(profile->cbdata, name);
598         if (value)
599             profile->vt->free_string(profile->cbdata, value);
600         return retval;
601     }
602 
603     retval = profile_node_iterator(&iter->idata, 0, &name, &value);
604     if (iter->idata == NULL) {
605         free(iter);
606         *iter_p = NULL;
607     }
608     if (retval)
609         return retval;
610     return set_results(name, value, ret_name, ret_value);
611 }
612 
613 void KRB5_CALLCONV
profile_release_string(char * str)614 profile_release_string(char *str)
615 {
616     free(str);
617 }
618