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)(); 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 size_t size, i; 297 const_profile_filespec_t *files; 298 prf_file_t file; 299 errcode_t err; 300 301 if (old_profile->vt) 302 return copy_vtable_profile(old_profile, new_profile); 303 304 /* The fields we care about are read-only after creation, so 305 no locking is needed. */ 306 COUNT_LINKED_LIST (size, prf_file_t, old_profile->first_file, next); 307 files = malloc ((size+1) * sizeof(*files)); 308 if (files == NULL) 309 return ENOMEM; 310 for (i = 0, file = old_profile->first_file; i < size; i++, file = file->next) 311 files[i] = file->data->filespec; 312 files[size] = NULL; 313 err = profile_init (files, new_profile); 314 free (files); 315 return err; 316 } 317 318 errcode_t KRB5_CALLCONV 319 profile_init_path(const_profile_filespec_list_t filepath, 320 profile_t *ret_profile) 321 { 322 unsigned int n_entries; 323 int i; 324 unsigned int ent_len; 325 const char *s, *t; 326 profile_filespec_t *filenames; 327 errcode_t retval; 328 329 /* count the distinct filename components */ 330 for(s = filepath, n_entries = 1; *s; s++) { 331 if (*s == ':') 332 n_entries++; 333 } 334 335 /* the array is NULL terminated */ 336 filenames = (profile_filespec_t*) malloc((n_entries+1) * sizeof(char*)); 337 if (filenames == 0) 338 return ENOMEM; 339 340 /* measure, copy, and skip each one */ 341 for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) { 342 ent_len = (unsigned int) (t-s); 343 filenames[i] = (char*) malloc(ent_len + 1); 344 if (filenames[i] == 0) { 345 /* if malloc fails, free the ones that worked */ 346 while(--i >= 0) free(filenames[i]); 347 free(filenames); 348 return ENOMEM; 349 } 350 strncpy(filenames[i], s, ent_len); 351 filenames[i][ent_len] = 0; 352 if (*t == 0) { 353 i++; 354 break; 355 } 356 } 357 /* cap the array */ 358 filenames[i] = 0; 359 360 retval = profile_init_flags((const_profile_filespec_t *) filenames, 0, 361 ret_profile); 362 363 /* count back down and free the entries */ 364 while(--i >= 0) free(filenames[i]); 365 free(filenames); 366 367 return retval; 368 } 369 370 errcode_t KRB5_CALLCONV 371 profile_is_writable(profile_t profile, int *writable) 372 { 373 if (!profile || profile->magic != PROF_MAGIC_PROFILE) 374 return PROF_MAGIC_PROFILE; 375 376 if (!writable) 377 return EINVAL; 378 *writable = 0; 379 380 if (profile->vt) { 381 if (profile->vt->writable) 382 return profile->vt->writable(profile->cbdata, writable); 383 else 384 return 0; 385 } 386 387 if (profile->first_file) 388 *writable = profile_file_is_writable(profile->first_file); 389 390 return 0; 391 } 392 393 errcode_t KRB5_CALLCONV 394 profile_is_modified(profile_t profile, int *modified) 395 { 396 if (!profile || profile->magic != PROF_MAGIC_PROFILE) 397 return PROF_MAGIC_PROFILE; 398 399 if (!modified) 400 return EINVAL; 401 *modified = 0; 402 403 if (profile->vt) { 404 if (profile->vt->modified) 405 return profile->vt->modified(profile->cbdata, modified); 406 else 407 return 0; 408 } 409 410 if (profile->first_file) 411 *modified = (profile->first_file->data->flags & PROFILE_FILE_DIRTY); 412 413 return 0; 414 } 415 416 errcode_t KRB5_CALLCONV 417 profile_flush(profile_t profile) 418 { 419 if (!profile || profile->magic != PROF_MAGIC_PROFILE) 420 return PROF_MAGIC_PROFILE; 421 422 if (profile->vt) { 423 if (profile->vt->flush) 424 return profile->vt->flush(profile->cbdata); 425 return 0; 426 } 427 428 if (profile->first_file) 429 return profile_flush_file(profile->first_file); 430 431 return 0; 432 } 433 434 errcode_t KRB5_CALLCONV 435 profile_flush_to_file(profile_t profile, const_profile_filespec_t outfile) 436 { 437 if (!profile || profile->magic != PROF_MAGIC_PROFILE) 438 return PROF_MAGIC_PROFILE; 439 440 if (profile->vt) 441 return PROF_UNSUPPORTED; 442 443 if (profile->first_file) 444 return profile_flush_file_to_file(profile->first_file, 445 outfile); 446 447 return 0; 448 } 449 450 errcode_t KRB5_CALLCONV 451 profile_flush_to_buffer(profile_t profile, char **buf) 452 { 453 if (profile->vt) 454 return PROF_UNSUPPORTED; 455 return profile_flush_file_data_to_buffer(profile->first_file->data, buf); 456 } 457 458 void KRB5_CALLCONV 459 profile_free_buffer(profile_t profile, char *buf) 460 { 461 free(buf); 462 } 463 464 void KRB5_CALLCONV 465 profile_abandon(profile_t profile) 466 { 467 prf_file_t p, next; 468 469 if (!profile || profile->magic != PROF_MAGIC_PROFILE) 470 return; 471 472 if (profile->vt) { 473 if (profile->vt->cleanup) 474 profile->vt->cleanup(profile->cbdata); 475 if (profile->lib_handle) { 476 /* Decrement the refcount on the handle and maybe free it. */ 477 k5_mutex_lock(&profile->lib_handle->lock); 478 if (--profile->lib_handle->refcount == 0) { 479 krb5int_close_plugin(profile->lib_handle->plugin_handle); 480 k5_mutex_unlock(&profile->lib_handle->lock); 481 k5_mutex_destroy(&profile->lib_handle->lock); 482 free(profile->lib_handle); 483 } else 484 k5_mutex_unlock(&profile->lib_handle->lock); 485 } 486 free(profile->vt); 487 } else { 488 for (p = profile->first_file; p; p = next) { 489 next = p->next; 490 profile_free_file(p); 491 } 492 } 493 profile->magic = 0; 494 free(profile); 495 } 496 497 void KRB5_CALLCONV 498 profile_release(profile_t profile) 499 { 500 prf_file_t p, next; 501 502 if (!profile || profile->magic != PROF_MAGIC_PROFILE) 503 return; 504 505 if (profile->vt) { 506 /* Flush the profile and then delegate to profile_abandon. */ 507 if (profile->vt->flush) 508 profile->vt->flush(profile->cbdata); 509 profile_abandon(profile); 510 return; 511 } else { 512 for (p = profile->first_file; p; p = next) { 513 next = p->next; 514 profile_close_file(p); 515 } 516 } 517 profile->magic = 0; 518 free(profile); 519 } 520 521 /* 522 * Here begins the profile serialization functions. 523 */ 524 errcode_t profile_ser_size(const char *unused, profile_t profile, 525 size_t *sizep) 526 { 527 size_t required; 528 prf_file_t pfp; 529 530 required = 3*sizeof(int32_t); 531 for (pfp = profile->first_file; pfp; pfp = pfp->next) { 532 required += sizeof(int32_t); 533 required += strlen(pfp->data->filespec); 534 } 535 *sizep += required; 536 return 0; 537 } 538 539 static void pack_int32(int32_t oval, unsigned char **bufpp, size_t *remainp) 540 { 541 store_32_be(oval, *bufpp); 542 *bufpp += sizeof(int32_t); 543 *remainp -= sizeof(int32_t); 544 } 545 546 errcode_t profile_ser_externalize(const char *unused, profile_t profile, 547 unsigned char **bufpp, size_t *remainp) 548 { 549 errcode_t retval; 550 size_t required; 551 unsigned char *bp; 552 size_t remain; 553 prf_file_t pfp; 554 int32_t fcount, slen; 555 556 required = 0; 557 bp = *bufpp; 558 remain = *remainp; 559 retval = EINVAL; 560 if (profile) { 561 retval = ENOMEM; 562 (void) profile_ser_size(unused, profile, &required); 563 if (required <= remain) { 564 fcount = 0; 565 for (pfp = profile->first_file; pfp; pfp = pfp->next) 566 fcount++; 567 pack_int32(PROF_MAGIC_PROFILE, &bp, &remain); 568 pack_int32(fcount, &bp, &remain); 569 for (pfp = profile->first_file; pfp; pfp = pfp->next) { 570 slen = (int32_t) strlen(pfp->data->filespec); 571 pack_int32(slen, &bp, &remain); 572 if (slen) { 573 memcpy(bp, pfp->data->filespec, (size_t) slen); 574 bp += slen; 575 remain -= (size_t) slen; 576 } 577 } 578 pack_int32(PROF_MAGIC_PROFILE, &bp, &remain); 579 retval = 0; 580 *bufpp = bp; 581 *remainp = remain; 582 } 583 } 584 return(retval); 585 } 586 587 static int unpack_int32(int32_t *intp, unsigned char **bufpp, 588 size_t *remainp) 589 { 590 if (*remainp >= sizeof(int32_t)) { 591 *intp = load_32_be(*bufpp); 592 *bufpp += sizeof(int32_t); 593 *remainp -= sizeof(int32_t); 594 return 0; 595 } 596 else 597 return 1; 598 } 599 600 errcode_t profile_ser_internalize(const char *unused, profile_t *profilep, 601 unsigned char **bufpp, size_t *remainp) 602 { 603 errcode_t retval; 604 unsigned char *bp; 605 size_t remain; 606 int i; 607 int32_t fcount, tmp; 608 profile_filespec_t *flist = 0; 609 610 bp = *bufpp; 611 remain = *remainp; 612 fcount = 0; 613 614 if (remain >= 12) 615 (void) unpack_int32(&tmp, &bp, &remain); 616 else 617 tmp = 0; 618 619 if (tmp != PROF_MAGIC_PROFILE) { 620 retval = EINVAL; 621 goto cleanup; 622 } 623 624 (void) unpack_int32(&fcount, &bp, &remain); 625 retval = ENOMEM; 626 627 flist = (profile_filespec_t *) malloc(sizeof(profile_filespec_t) * (size_t) (fcount + 1)); 628 if (!flist) 629 goto cleanup; 630 631 memset(flist, 0, sizeof(char *) * (size_t) (fcount+1)); 632 for (i=0; i<fcount; i++) { 633 if (!unpack_int32(&tmp, &bp, &remain)) { 634 flist[i] = (char *) malloc((size_t) (tmp+1)); 635 if (!flist[i]) 636 goto cleanup; 637 memcpy(flist[i], bp, (size_t) tmp); 638 flist[i][tmp] = '\0'; 639 bp += tmp; 640 remain -= (size_t) tmp; 641 } 642 } 643 644 if (unpack_int32(&tmp, &bp, &remain) || 645 (tmp != PROF_MAGIC_PROFILE)) { 646 retval = EINVAL; 647 goto cleanup; 648 } 649 650 if ((retval = profile_init((const_profile_filespec_t *) flist, 651 profilep))) 652 goto cleanup; 653 654 *bufpp = bp; 655 *remainp = remain; 656 657 cleanup: 658 if (flist) { 659 for (i=0; i<fcount; i++) { 660 if (flist[i]) 661 free(flist[i]); 662 } 663 free(flist); 664 } 665 return(retval); 666 } 667