xref: /freebsd/crypto/krb5/src/util/profile/prof_init.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * prof_init.c --- routines that manipulate the user-visible profile_t
4  *      object.
5  */
6 
7 #include "prof_int.h"
8 
9 #include <stdio.h>
10 #include <string.h>
11 #ifdef HAVE_STDLIB_H
12 #include <stdlib.h>
13 #endif
14 #include <errno.h>
15 
16 /* Create a vtable profile, possibly with a library handle.  The new profile
17  * takes ownership of the handle refcount on success. */
18 static errcode_t
init_module(struct profile_vtable * vtable,void * cbdata,prf_lib_handle_t handle,profile_t * ret_profile)19 init_module(struct profile_vtable *vtable, void *cbdata,
20             prf_lib_handle_t handle, profile_t *ret_profile)
21 {
22     profile_t profile;
23     struct profile_vtable *vt_copy;
24 
25     /* Check that the vtable's minor version is sane and that mandatory methods
26      * are implemented. */
27     if (vtable->minor_ver < 1 || !vtable->get_values || !vtable->free_values)
28         return EINVAL;
29     if (vtable->cleanup && !vtable->copy)
30         return EINVAL;
31     if (vtable->iterator_create &&
32         (!vtable->iterator || !vtable->iterator_free || !vtable->free_string))
33         return EINVAL;
34 
35     profile = malloc(sizeof(*profile));
36     if (!profile)
37         return ENOMEM;
38     memset(profile, 0, sizeof(*profile));
39 
40     vt_copy = malloc(sizeof(*vt_copy));
41     if (!vt_copy) {
42         free(profile);
43         return ENOMEM;
44     }
45     /* It's safe to just copy the caller's vtable for now.  If the minor
46      * version is bumped, we'll need to copy individual fields. */
47     *vt_copy = *vtable;
48 
49     profile->vt = vt_copy;
50     profile->cbdata = cbdata;
51     profile->lib_handle = handle;
52     profile->magic = PROF_MAGIC_PROFILE;
53     *ret_profile = profile;
54     return 0;
55 }
56 
57 /* Parse modspec into the module path and residual string. */
58 static errcode_t
parse_modspec(const char * modspec,char ** ret_path,char ** ret_residual)59 parse_modspec(const char *modspec, char **ret_path, char **ret_residual)
60 {
61     const char *p;
62     char *path, *fullpath, *residual;
63     errcode_t ret;
64 
65     *ret_path = *ret_residual = NULL;
66 
67     /* Find the separator, skipping a Windows drive letter if present. */
68     p = (*modspec != '\0' && modspec[1] == ':') ? modspec + 2 : modspec;
69     p = strchr(p, ':');
70     if (p == NULL)
71         return PROF_MODULE_SYNTAX;
72 
73     /* Copy the path. */
74     path = malloc(p - modspec + 1);
75     if (path == NULL)
76         return ENOMEM;
77     memcpy(path, modspec, p - modspec);
78     path[p - modspec] = '\0';
79 
80     /* Compose the path with LIBDIR if it's not absolute. */
81     ret = k5_path_join(LIBDIR, path, &fullpath);
82     free(path);
83     if (ret)
84         return ret;
85 
86     residual = strdup(p + 1);
87     if (residual == NULL) {
88         free(fullpath);
89         return ENOMEM;
90     }
91 
92     *ret_path = fullpath;
93     *ret_residual = residual;
94     return 0;
95 }
96 
97 /* Load a dynamic profile module as specified by modspec and create a vtable
98  * profile for it in *ret_profile. */
99 static errcode_t
init_load_module(const char * modspec,profile_t * ret_profile)100 init_load_module(const char *modspec, profile_t *ret_profile)
101 {
102     char *modpath = NULL, *residual = NULL;
103     struct errinfo einfo = { 0 };
104     prf_lib_handle_t lib_handle = NULL;
105     struct plugin_file_handle *plhandle = NULL;
106     void *cbdata = NULL, (*fptr)(void);
107     int have_lock = 0, have_cbdata = 0;
108     struct profile_vtable vtable = { 1 };  /* Set minor_ver to 1, rest null. */
109     errcode_t err;
110     profile_module_init_fn initfn;
111 
112     err = parse_modspec(modspec, &modpath, &residual);
113     if (err)
114         goto cleanup;
115 
116     /* Allocate a reference-counted library handle container. */
117     lib_handle = malloc(sizeof(*lib_handle));
118     if (lib_handle == NULL)
119         goto cleanup;
120     err = k5_mutex_init(&lib_handle->lock);
121     if (err)
122         goto cleanup;
123     have_lock = 1;
124 
125     /* Open the module and get its initializer. */
126     err = krb5int_open_plugin(modpath, &plhandle, &einfo);
127     if (err)
128         goto cleanup;
129     err = krb5int_get_plugin_func(plhandle, "profile_module_init", &fptr,
130                                   &einfo);
131     if (err == ENOENT)
132         err = PROF_MODULE_INVALID;
133     if (err)
134         goto cleanup;
135 
136     /* Get the profile vtable and callback data pointer. */
137     initfn = (profile_module_init_fn)fptr;
138     err = (*initfn)(residual, &vtable, &cbdata);
139     if (err)
140         goto cleanup;
141     have_cbdata = 1;
142 
143     /* Create a vtable profile with the information obtained. */
144     lib_handle->plugin_handle = plhandle;
145     lib_handle->refcount = 1;
146     err = init_module(&vtable, cbdata, lib_handle, ret_profile);
147 
148 cleanup:
149     free(modpath);
150     free(residual);
151     k5_clear_error(&einfo);
152     if (err) {
153         if (have_cbdata && vtable.cleanup)
154             vtable.cleanup(cbdata);
155         if (have_lock)
156             k5_mutex_destroy(&lib_handle->lock);
157         free(lib_handle);
158         if (plhandle)
159             krb5int_close_plugin(plhandle);
160     }
161     return err;
162 }
163 
164 errcode_t KRB5_CALLCONV
profile_init_flags(const_profile_filespec_t * files,int flags,profile_t * ret_profile)165 profile_init_flags(const_profile_filespec_t *files, int flags,
166                    profile_t *ret_profile)
167 {
168     const_profile_filespec_t *fs;
169     profile_t profile;
170     prf_file_t  new_file, last = 0;
171     errcode_t retval = 0, access_retval = 0;
172     char *modspec = NULL, **modspec_arg;
173 
174     profile = malloc(sizeof(struct _profile_t));
175     if (!profile)
176         return ENOMEM;
177     memset(profile, 0, sizeof(struct _profile_t));
178     profile->magic = PROF_MAGIC_PROFILE;
179 
180     /*
181      * If the filenames list is not specified or empty, return an empty
182      * profile.
183      */
184     if ( files && !PROFILE_LAST_FILESPEC(*files) ) {
185         for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {
186             /* Allow a module declaration if it is permitted by flags and this
187              * is the first file parsed. */
188             modspec_arg = ((flags & PROFILE_INIT_ALLOW_MODULE) && !last) ?
189                 &modspec : NULL;
190             retval = profile_open_file(*fs, &new_file, modspec_arg);
191             if (retval == PROF_MODULE && modspec) {
192                 /* Stop parsing files and load a dynamic module instead. */
193                 free(profile);
194                 retval = init_load_module(modspec, ret_profile);
195                 free(modspec);
196                 return retval;
197             }
198             /* if this file is missing, skip to the next */
199             if (retval == ENOENT) {
200                 continue;
201             }
202             /* If we can't read this file, remember it but keep going. */
203             if (retval == EACCES || retval == EPERM) {
204                 access_retval = retval;
205                 continue;
206             }
207             if (retval) {
208                 profile_release(profile);
209                 return retval;
210             }
211             if (last)
212                 last->next = new_file;
213             else
214                 profile->first_file = new_file;
215             last = new_file;
216         }
217         /*
218          * If last is still null after the loop, then all the files were
219          * missing or unreadable, so return the appropriate error.
220          */
221         if (!last) {
222             profile_release(profile);
223             return access_retval ? access_retval : ENOENT;
224         }
225     }
226 
227     *ret_profile = profile;
228     return 0;
229 }
230 
231 errcode_t KRB5_CALLCONV
profile_init(const_profile_filespec_t * files,profile_t * ret_profile)232 profile_init(const_profile_filespec_t *files, profile_t *ret_profile)
233 {
234     return profile_init_flags(files, 0, ret_profile);
235 }
236 
237 errcode_t KRB5_CALLCONV
profile_init_vtable(struct profile_vtable * vtable,void * cbdata,profile_t * ret_profile)238 profile_init_vtable(struct profile_vtable *vtable, void *cbdata,
239                     profile_t *ret_profile)
240 {
241     return init_module(vtable, cbdata, NULL, ret_profile);
242 }
243 
244 /* Copy a vtable profile. */
245 static errcode_t
copy_vtable_profile(profile_t profile,profile_t * ret_new_profile)246 copy_vtable_profile(profile_t profile, profile_t *ret_new_profile)
247 {
248     errcode_t err;
249     void *cbdata;
250     profile_t new_profile;
251 
252     *ret_new_profile = NULL;
253 
254     if (profile->vt->copy) {
255         /* Make a copy of profile's cbdata for the new profile. */
256         err = profile->vt->copy(profile->cbdata, &cbdata);
257         if (err)
258             return err;
259         err = init_module(profile->vt, cbdata, profile->lib_handle,
260                           &new_profile);
261         if (err && profile->vt->cleanup)
262             profile->vt->cleanup(cbdata);
263     } else {
264         /* Use the same cbdata as the old profile. */
265         err = init_module(profile->vt, profile->cbdata, profile->lib_handle,
266                           &new_profile);
267     }
268     if (err)
269         return err;
270 
271     /* Increment the refcount on the library handle if there is one. */
272     if (profile->lib_handle) {
273         k5_mutex_lock(&profile->lib_handle->lock);
274         profile->lib_handle->refcount++;
275         k5_mutex_unlock(&profile->lib_handle->lock);
276     }
277 
278     *ret_new_profile = new_profile;
279     return 0;
280 }
281 
282 #define COUNT_LINKED_LIST(COUNT, PTYPE, START, FIELD)   \
283     {                                                   \
284         size_t cll_counter = 0;                         \
285         PTYPE cll_ptr = (START);                        \
286         while (cll_ptr != NULL) {                       \
287             cll_counter++;                              \
288             cll_ptr = cll_ptr->FIELD;                   \
289         }                                               \
290         (COUNT) = cll_counter;                          \
291     }
292 
293 errcode_t KRB5_CALLCONV
profile_copy(profile_t old_profile,profile_t * new_profile)294 profile_copy(profile_t old_profile, profile_t *new_profile)
295 {
296     profile_t profile;
297     prf_file_t p, q, *nextp;
298 
299     *new_profile = NULL;
300 
301     if (old_profile->vt)
302         return copy_vtable_profile(old_profile, new_profile);
303 
304     profile = calloc(1, sizeof(*profile));
305     if (profile == NULL)
306         return ENOMEM;
307     profile->magic = PROF_MAGIC_PROFILE;
308 
309     nextp = &profile->first_file;
310     for (p = old_profile->first_file; p != NULL; p = p->next) {
311         q = profile_copy_file(p);
312         if (q == NULL) {
313             profile_abandon(profile);
314             return ENOMEM;
315         }
316         *nextp = q;
317         nextp = &q->next;
318     }
319 
320     *new_profile = profile;
321     return 0;
322 }
323 
324 errcode_t KRB5_CALLCONV
profile_init_path(const_profile_filespec_list_t filepath,profile_t * ret_profile)325 profile_init_path(const_profile_filespec_list_t filepath,
326                   profile_t *ret_profile)
327 {
328     unsigned int n_entries;
329     size_t i;
330     unsigned int ent_len;
331     const char *s, *t;
332     profile_filespec_t *filenames;
333     errcode_t retval;
334 
335     /* count the distinct filename components */
336     for(s = filepath, n_entries = 1; *s; s++) {
337         if (*s == ':')
338             n_entries++;
339     }
340 
341     /* the array is NULL terminated */
342     filenames = (profile_filespec_t*) malloc((n_entries+1) * sizeof(char*));
343     if (filenames == 0)
344         return ENOMEM;
345 
346     /* measure, copy, and skip each one */
347     for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) {
348         ent_len = (unsigned int) (t-s);
349         filenames[i] = (char*) malloc(ent_len + 1);
350         if (filenames[i] == 0) {
351             /* if malloc fails, free the ones that worked */
352             while (i > 0)
353                 free(filenames[--i]);
354             free(filenames);
355             return ENOMEM;
356         }
357         strncpy(filenames[i], s, ent_len);
358         filenames[i][ent_len] = 0;
359         if (*t == 0) {
360             i++;
361             break;
362         }
363     }
364     /* cap the array */
365     filenames[i] = 0;
366 
367     retval = profile_init_flags((const_profile_filespec_t *) filenames, 0,
368                                 ret_profile);
369 
370     /* count back down and free the entries */
371     while (i > 0)
372         free(filenames[--i]);
373     free(filenames);
374 
375     return retval;
376 }
377 
378 errcode_t KRB5_CALLCONV
profile_is_writable(profile_t profile,int * writable)379 profile_is_writable(profile_t profile, int *writable)
380 {
381     if (!profile || profile->magic != PROF_MAGIC_PROFILE)
382         return PROF_MAGIC_PROFILE;
383 
384     if (!writable)
385         return EINVAL;
386     *writable = 0;
387 
388     if (profile->vt) {
389         if (profile->vt->writable)
390             return profile->vt->writable(profile->cbdata, writable);
391         else
392             return 0;
393     }
394 
395     if (profile->first_file)
396         *writable = profile_file_is_writable(profile->first_file);
397 
398     return 0;
399 }
400 
401 errcode_t KRB5_CALLCONV
profile_is_modified(profile_t profile,int * modified)402 profile_is_modified(profile_t profile, int *modified)
403 {
404     if (!profile || profile->magic != PROF_MAGIC_PROFILE)
405         return PROF_MAGIC_PROFILE;
406 
407     if (!modified)
408         return EINVAL;
409     *modified = 0;
410 
411     if (profile->vt) {
412         if (profile->vt->modified)
413             return profile->vt->modified(profile->cbdata, modified);
414         else
415             return 0;
416     }
417 
418     if (profile->first_file)
419         *modified = (profile->first_file->data->flags & PROFILE_FILE_DIRTY);
420 
421     return 0;
422 }
423 
424 errcode_t KRB5_CALLCONV
profile_flush(profile_t profile)425 profile_flush(profile_t profile)
426 {
427     if (!profile || profile->magic != PROF_MAGIC_PROFILE)
428         return PROF_MAGIC_PROFILE;
429 
430     if (profile->vt) {
431         if (profile->vt->flush)
432             return profile->vt->flush(profile->cbdata);
433         return 0;
434     }
435 
436     if (profile->first_file)
437         return profile_flush_file(profile->first_file);
438 
439     return 0;
440 }
441 
442 errcode_t KRB5_CALLCONV
profile_flush_to_file(profile_t profile,const_profile_filespec_t outfile)443 profile_flush_to_file(profile_t profile, const_profile_filespec_t outfile)
444 {
445     if (!profile || profile->magic != PROF_MAGIC_PROFILE)
446         return PROF_MAGIC_PROFILE;
447 
448     if (profile->vt)
449         return PROF_UNSUPPORTED;
450 
451     if (profile->first_file)
452         return profile_flush_file_to_file(profile->first_file,
453                                           outfile);
454 
455     return 0;
456 }
457 
458 errcode_t KRB5_CALLCONV
profile_flush_to_buffer(profile_t profile,char ** buf)459 profile_flush_to_buffer(profile_t profile, char **buf)
460 {
461     if (profile->vt)
462         return PROF_UNSUPPORTED;
463     return profile_flush_file_data_to_buffer(profile->first_file->data, buf);
464 }
465 
466 void KRB5_CALLCONV
profile_free_buffer(profile_t profile,char * buf)467 profile_free_buffer(profile_t profile, char *buf)
468 {
469     free(buf);
470 }
471 
472 void KRB5_CALLCONV
profile_abandon(profile_t profile)473 profile_abandon(profile_t profile)
474 {
475     prf_file_t      p, next;
476 
477     if (!profile || profile->magic != PROF_MAGIC_PROFILE)
478         return;
479 
480     if (profile->vt) {
481         if (profile->vt->cleanup)
482             profile->vt->cleanup(profile->cbdata);
483         if (profile->lib_handle) {
484             /* Decrement the refcount on the handle and maybe free it. */
485             k5_mutex_lock(&profile->lib_handle->lock);
486             if (--profile->lib_handle->refcount == 0) {
487                 krb5int_close_plugin(profile->lib_handle->plugin_handle);
488                 k5_mutex_unlock(&profile->lib_handle->lock);
489                 k5_mutex_destroy(&profile->lib_handle->lock);
490                 free(profile->lib_handle);
491             } else
492                 k5_mutex_unlock(&profile->lib_handle->lock);
493         }
494         free(profile->vt);
495     } else {
496         for (p = profile->first_file; p; p = next) {
497             next = p->next;
498             profile_free_file(p);
499         }
500     }
501     profile->magic = 0;
502     free(profile);
503 }
504 
505 void KRB5_CALLCONV
profile_release(profile_t profile)506 profile_release(profile_t profile)
507 {
508     prf_file_t      p, next;
509 
510     if (!profile || profile->magic != PROF_MAGIC_PROFILE)
511         return;
512 
513     if (profile->vt) {
514         /* Flush the profile and then delegate to profile_abandon. */
515         if (profile->vt->flush)
516             profile->vt->flush(profile->cbdata);
517         profile_abandon(profile);
518         return;
519     } else {
520         for (p = profile->first_file; p; p = next) {
521             next = p->next;
522             profile_close_file(p);
523         }
524     }
525     profile->magic = 0;
526     free(profile);
527 }
528 
529 /*
530  * Here begins the profile serialization functions.
531  */
profile_ser_size(profile_t profile,size_t * sizep)532 errcode_t profile_ser_size(profile_t profile, size_t *sizep)
533 {
534     size_t      required;
535     prf_file_t  pfp;
536 
537     required = 3*sizeof(int32_t);
538     for (pfp = profile->first_file; pfp; pfp = pfp->next) {
539         required += sizeof(int32_t);
540         required += strlen(pfp->data->filespec);
541     }
542     *sizep += required;
543     return 0;
544 }
545 
pack_int32(int32_t oval,unsigned char ** bufpp,size_t * remainp)546 static void pack_int32(int32_t oval, unsigned char **bufpp, size_t *remainp)
547 {
548     store_32_be(oval, *bufpp);
549     *bufpp += sizeof(int32_t);
550     *remainp -= sizeof(int32_t);
551 }
552 
profile_ser_externalize(profile_t profile,unsigned char ** bufpp,size_t * remainp)553 errcode_t profile_ser_externalize(profile_t profile,
554                                   unsigned char **bufpp, size_t *remainp)
555 {
556     errcode_t           retval;
557     size_t              required;
558     unsigned char       *bp;
559     size_t              remain;
560     prf_file_t          pfp;
561     int32_t             fcount, slen;
562 
563     required = 0;
564     bp = *bufpp;
565     remain = *remainp;
566     retval = EINVAL;
567     if (profile) {
568         retval = ENOMEM;
569         (void) profile_ser_size(profile, &required);
570         if (required <= remain) {
571             fcount = 0;
572             for (pfp = profile->first_file; pfp; pfp = pfp->next)
573                 fcount++;
574             pack_int32(PROF_MAGIC_PROFILE, &bp, &remain);
575             pack_int32(fcount, &bp, &remain);
576             for (pfp = profile->first_file; pfp; pfp = pfp->next) {
577                 slen = (int32_t) strlen(pfp->data->filespec);
578                 pack_int32(slen, &bp, &remain);
579                 if (slen) {
580                     memcpy(bp, pfp->data->filespec, (size_t) slen);
581                     bp += slen;
582                     remain -= (size_t) slen;
583                 }
584             }
585             pack_int32(PROF_MAGIC_PROFILE, &bp, &remain);
586             retval = 0;
587             *bufpp = bp;
588             *remainp = remain;
589         }
590     }
591     return(retval);
592 }
593 
unpack_int32(int32_t * intp,unsigned char ** bufpp,size_t * remainp)594 static int unpack_int32(int32_t *intp, unsigned char **bufpp,
595                         size_t *remainp)
596 {
597     if (*remainp >= sizeof(int32_t)) {
598         *intp = load_32_be(*bufpp);
599         *bufpp += sizeof(int32_t);
600         *remainp -= sizeof(int32_t);
601         return 0;
602     }
603     else
604         return 1;
605 }
606 
profile_ser_internalize(profile_t * profilep,unsigned char ** bufpp,size_t * remainp)607 errcode_t profile_ser_internalize(profile_t *profilep,
608                                   unsigned char **bufpp, size_t *remainp)
609 {
610     errcode_t               retval;
611     unsigned char   *bp;
612     size_t          remain;
613     int                     i;
614     int32_t                 fcount, tmp;
615     profile_filespec_t              *flist = 0;
616 
617     bp = *bufpp;
618     remain = *remainp;
619     fcount = 0;
620 
621     if (remain >= 12)
622         (void) unpack_int32(&tmp, &bp, &remain);
623     else
624         tmp = 0;
625 
626     if (tmp != PROF_MAGIC_PROFILE) {
627         retval = EINVAL;
628         goto cleanup;
629     }
630 
631     (void) unpack_int32(&fcount, &bp, &remain);
632     retval = ENOMEM;
633 
634     flist = (profile_filespec_t *) malloc(sizeof(profile_filespec_t) * (size_t) (fcount + 1));
635     if (!flist)
636         goto cleanup;
637 
638     memset(flist, 0, sizeof(char *) * (size_t) (fcount+1));
639     for (i=0; i<fcount; i++) {
640         if (!unpack_int32(&tmp, &bp, &remain)) {
641             flist[i] = (char *) malloc((size_t) (tmp+1));
642             if (!flist[i])
643                 goto cleanup;
644             memcpy(flist[i], bp, (size_t) tmp);
645             flist[i][tmp] = '\0';
646             bp += tmp;
647             remain -= (size_t) tmp;
648         }
649     }
650 
651     if (unpack_int32(&tmp, &bp, &remain) ||
652         (tmp != PROF_MAGIC_PROFILE)) {
653         retval = EINVAL;
654         goto cleanup;
655     }
656 
657     if ((retval = profile_init((const_profile_filespec_t *) flist,
658                                profilep)))
659         goto cleanup;
660 
661     *bufpp = bp;
662     *remainp = remain;
663 
664 cleanup:
665     if (flist) {
666         for (i=0; i<fcount; i++) {
667             if (flist[i])
668                 free(flist[i]);
669         }
670         free(flist);
671     }
672     return(retval);
673 }
674