Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/util/profile/prof_init.c
34889 views
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
467
profile_free_buffer(profile_t profile, char *buf)
468
{
469
free(buf);
470
}
471
472
void KRB5_CALLCONV
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
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
*/
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
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
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
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
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
675