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