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