1 /* $NetBSD: cache.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 2005, PADL Software Pty Ltd. 5 * All rights reserved. 6 * 7 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * 3. Neither the name of PADL Software nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include "kcm_locl.h" 38 39 HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER; 40 kcm_ccache_data *ccache_head = NULL; 41 static unsigned int ccache_nextid = 0; 42 43 char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid) 44 { 45 unsigned n; 46 char *name; 47 48 HEIMDAL_MUTEX_lock(&ccache_mutex); 49 n = ++ccache_nextid; 50 HEIMDAL_MUTEX_unlock(&ccache_mutex); 51 52 asprintf(&name, "%ld:%u", (long)uid, n); 53 54 return name; 55 } 56 57 krb5_error_code 58 kcm_ccache_resolve(krb5_context context, 59 const char *name, 60 kcm_ccache *ccache) 61 { 62 kcm_ccache p; 63 krb5_error_code ret; 64 65 *ccache = NULL; 66 67 ret = KRB5_FCC_NOFILE; 68 69 HEIMDAL_MUTEX_lock(&ccache_mutex); 70 71 for (p = ccache_head; p != NULL; p = p->next) { 72 if ((p->flags & KCM_FLAGS_VALID) == 0) 73 continue; 74 if (strcmp(p->name, name) == 0) { 75 ret = 0; 76 break; 77 } 78 } 79 80 if (ret == 0) { 81 kcm_retain_ccache(context, p); 82 *ccache = p; 83 } 84 85 HEIMDAL_MUTEX_unlock(&ccache_mutex); 86 87 return ret; 88 } 89 90 krb5_error_code 91 kcm_ccache_resolve_by_uuid(krb5_context context, 92 kcmuuid_t uuid, 93 kcm_ccache *ccache) 94 { 95 kcm_ccache p; 96 krb5_error_code ret; 97 98 *ccache = NULL; 99 100 ret = KRB5_FCC_NOFILE; 101 102 HEIMDAL_MUTEX_lock(&ccache_mutex); 103 104 for (p = ccache_head; p != NULL; p = p->next) { 105 if ((p->flags & KCM_FLAGS_VALID) == 0) 106 continue; 107 if (memcmp(p->uuid, uuid, sizeof(uuid)) == 0) { 108 ret = 0; 109 break; 110 } 111 } 112 113 if (ret == 0) { 114 kcm_retain_ccache(context, p); 115 *ccache = p; 116 } 117 118 HEIMDAL_MUTEX_unlock(&ccache_mutex); 119 120 return ret; 121 } 122 123 krb5_error_code 124 kcm_ccache_get_uuids(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *sp) 125 { 126 krb5_error_code ret; 127 kcm_ccache p; 128 129 ret = KRB5_FCC_NOFILE; 130 131 HEIMDAL_MUTEX_lock(&ccache_mutex); 132 133 for (p = ccache_head; p != NULL; p = p->next) { 134 if ((p->flags & KCM_FLAGS_VALID) == 0) 135 continue; 136 ret = kcm_access(context, client, opcode, p); 137 if (ret) { 138 ret = 0; 139 continue; 140 } 141 krb5_storage_write(sp, p->uuid, sizeof(p->uuid)); 142 } 143 144 HEIMDAL_MUTEX_unlock(&ccache_mutex); 145 146 return ret; 147 } 148 149 150 krb5_error_code kcm_debug_ccache(krb5_context context) 151 { 152 kcm_ccache p; 153 154 for (p = ccache_head; p != NULL; p = p->next) { 155 char *cpn = NULL, *spn = NULL; 156 int ncreds = 0; 157 struct kcm_creds *k; 158 159 if ((p->flags & KCM_FLAGS_VALID) == 0) { 160 kcm_log(7, "cache %08x: empty slot"); 161 continue; 162 } 163 164 KCM_ASSERT_VALID(p); 165 166 for (k = p->creds; k != NULL; k = k->next) 167 ncreds++; 168 169 if (p->client != NULL) 170 krb5_unparse_name(context, p->client, &cpn); 171 if (p->server != NULL) 172 krb5_unparse_name(context, p->server, &spn); 173 174 kcm_log(7, "cache %08x: name %s refcnt %d flags %04x mode %04o " 175 "uid %d gid %d client %s server %s ncreds %d", 176 p, p->name, p->refcnt, p->flags, p->mode, p->uid, p->gid, 177 (cpn == NULL) ? "<none>" : cpn, 178 (spn == NULL) ? "<none>" : spn, 179 ncreds); 180 181 if (cpn != NULL) 182 free(cpn); 183 if (spn != NULL) 184 free(spn); 185 } 186 187 return 0; 188 } 189 190 static void 191 kcm_free_ccache_data_internal(krb5_context context, 192 kcm_ccache_data *cache) 193 { 194 KCM_ASSERT_VALID(cache); 195 196 if (cache->name != NULL) { 197 free(cache->name); 198 cache->name = NULL; 199 } 200 201 if (cache->flags & KCM_FLAGS_USE_KEYTAB) { 202 krb5_kt_close(context, cache->key.keytab); 203 cache->key.keytab = NULL; 204 } else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) { 205 krb5_free_keyblock_contents(context, &cache->key.keyblock); 206 krb5_keyblock_zero(&cache->key.keyblock); 207 } 208 209 cache->flags = 0; 210 cache->mode = 0; 211 cache->uid = -1; 212 cache->gid = -1; 213 cache->session = -1; 214 215 kcm_zero_ccache_data_internal(context, cache); 216 217 cache->tkt_life = 0; 218 cache->renew_life = 0; 219 220 cache->next = NULL; 221 cache->refcnt = 0; 222 223 HEIMDAL_MUTEX_unlock(&cache->mutex); 224 HEIMDAL_MUTEX_destroy(&cache->mutex); 225 } 226 227 228 krb5_error_code 229 kcm_ccache_destroy(krb5_context context, const char *name) 230 { 231 kcm_ccache *p, ccache; 232 krb5_error_code ret; 233 234 ret = KRB5_FCC_NOFILE; 235 236 HEIMDAL_MUTEX_lock(&ccache_mutex); 237 for (p = &ccache_head; *p != NULL; p = &(*p)->next) { 238 if (((*p)->flags & KCM_FLAGS_VALID) == 0) 239 continue; 240 if (strcmp((*p)->name, name) == 0) { 241 ret = 0; 242 break; 243 } 244 } 245 if (ret) 246 goto out; 247 248 if ((*p)->refcnt != 1) { 249 ret = EAGAIN; 250 goto out; 251 } 252 253 ccache = *p; 254 *p = (*p)->next; 255 kcm_free_ccache_data_internal(context, ccache); 256 free(ccache); 257 258 out: 259 HEIMDAL_MUTEX_unlock(&ccache_mutex); 260 261 return ret; 262 } 263 264 static krb5_error_code 265 kcm_ccache_alloc(krb5_context context, 266 const char *name, 267 kcm_ccache *ccache) 268 { 269 kcm_ccache slot = NULL, p; 270 krb5_error_code ret; 271 int new_slot = 0; 272 273 *ccache = NULL; 274 275 /* First, check for duplicates */ 276 HEIMDAL_MUTEX_lock(&ccache_mutex); 277 ret = 0; 278 for (p = ccache_head; p != NULL; p = p->next) { 279 if (p->flags & KCM_FLAGS_VALID) { 280 if (strcmp(p->name, name) == 0) { 281 ret = KRB5_CC_WRITE; 282 break; 283 } 284 } else if (slot == NULL) 285 slot = p; 286 } 287 288 if (ret) 289 goto out; 290 291 /* 292 * Create an enpty slot for us. 293 */ 294 if (slot == NULL) { 295 slot = (kcm_ccache_data *)malloc(sizeof(*slot)); 296 if (slot == NULL) { 297 ret = KRB5_CC_NOMEM; 298 goto out; 299 } 300 slot->next = ccache_head; 301 HEIMDAL_MUTEX_init(&slot->mutex); 302 new_slot = 1; 303 } 304 305 RAND_bytes(slot->uuid, sizeof(slot->uuid)); 306 307 slot->name = strdup(name); 308 if (slot->name == NULL) { 309 ret = KRB5_CC_NOMEM; 310 goto out; 311 } 312 313 slot->refcnt = 1; 314 slot->flags = KCM_FLAGS_VALID; 315 slot->mode = S_IRUSR | S_IWUSR; 316 slot->uid = -1; 317 slot->gid = -1; 318 slot->client = NULL; 319 slot->server = NULL; 320 slot->creds = NULL; 321 slot->key.keytab = NULL; 322 slot->tkt_life = 0; 323 slot->renew_life = 0; 324 325 if (new_slot) 326 ccache_head = slot; 327 328 *ccache = slot; 329 330 HEIMDAL_MUTEX_unlock(&ccache_mutex); 331 return 0; 332 333 out: 334 HEIMDAL_MUTEX_unlock(&ccache_mutex); 335 if (new_slot && slot != NULL) { 336 HEIMDAL_MUTEX_destroy(&slot->mutex); 337 free(slot); 338 } 339 return ret; 340 } 341 342 krb5_error_code 343 kcm_ccache_remove_creds_internal(krb5_context context, 344 kcm_ccache ccache) 345 { 346 struct kcm_creds *k; 347 348 k = ccache->creds; 349 while (k != NULL) { 350 struct kcm_creds *old; 351 352 krb5_free_cred_contents(context, &k->cred); 353 old = k; 354 k = k->next; 355 free(old); 356 } 357 ccache->creds = NULL; 358 359 return 0; 360 } 361 362 krb5_error_code 363 kcm_ccache_remove_creds(krb5_context context, 364 kcm_ccache ccache) 365 { 366 krb5_error_code ret; 367 368 KCM_ASSERT_VALID(ccache); 369 370 HEIMDAL_MUTEX_lock(&ccache->mutex); 371 ret = kcm_ccache_remove_creds_internal(context, ccache); 372 HEIMDAL_MUTEX_unlock(&ccache->mutex); 373 374 return ret; 375 } 376 377 krb5_error_code 378 kcm_zero_ccache_data_internal(krb5_context context, 379 kcm_ccache_data *cache) 380 { 381 if (cache->client != NULL) { 382 krb5_free_principal(context, cache->client); 383 cache->client = NULL; 384 } 385 386 if (cache->server != NULL) { 387 krb5_free_principal(context, cache->server); 388 cache->server = NULL; 389 } 390 391 kcm_ccache_remove_creds_internal(context, cache); 392 393 return 0; 394 } 395 396 krb5_error_code 397 kcm_zero_ccache_data(krb5_context context, 398 kcm_ccache cache) 399 { 400 krb5_error_code ret; 401 402 KCM_ASSERT_VALID(cache); 403 404 HEIMDAL_MUTEX_lock(&cache->mutex); 405 ret = kcm_zero_ccache_data_internal(context, cache); 406 HEIMDAL_MUTEX_unlock(&cache->mutex); 407 408 return ret; 409 } 410 411 krb5_error_code 412 kcm_retain_ccache(krb5_context context, 413 kcm_ccache ccache) 414 { 415 KCM_ASSERT_VALID(ccache); 416 417 HEIMDAL_MUTEX_lock(&ccache->mutex); 418 ccache->refcnt++; 419 HEIMDAL_MUTEX_unlock(&ccache->mutex); 420 421 return 0; 422 } 423 424 krb5_error_code 425 kcm_release_ccache(krb5_context context, kcm_ccache c) 426 { 427 krb5_error_code ret = 0; 428 429 KCM_ASSERT_VALID(c); 430 431 HEIMDAL_MUTEX_lock(&c->mutex); 432 if (c->refcnt == 1) { 433 kcm_free_ccache_data_internal(context, c); 434 free(c); 435 } else { 436 c->refcnt--; 437 HEIMDAL_MUTEX_unlock(&c->mutex); 438 } 439 440 return ret; 441 } 442 443 krb5_error_code 444 kcm_ccache_gen_new(krb5_context context, 445 pid_t pid, 446 uid_t uid, 447 gid_t gid, 448 kcm_ccache *ccache) 449 { 450 krb5_error_code ret; 451 char *name; 452 453 name = kcm_ccache_nextid(pid, uid, gid); 454 if (name == NULL) { 455 return KRB5_CC_NOMEM; 456 } 457 458 ret = kcm_ccache_new(context, name, ccache); 459 460 free(name); 461 return ret; 462 } 463 464 krb5_error_code 465 kcm_ccache_new(krb5_context context, 466 const char *name, 467 kcm_ccache *ccache) 468 { 469 krb5_error_code ret; 470 471 ret = kcm_ccache_alloc(context, name, ccache); 472 if (ret == 0) { 473 /* 474 * one reference is held by the linked list, 475 * one by the caller 476 */ 477 kcm_retain_ccache(context, *ccache); 478 } 479 480 return ret; 481 } 482 483 krb5_error_code 484 kcm_ccache_destroy_if_empty(krb5_context context, 485 kcm_ccache ccache) 486 { 487 krb5_error_code ret; 488 489 KCM_ASSERT_VALID(ccache); 490 491 if (ccache->creds == NULL) { 492 ret = kcm_ccache_destroy(context, ccache->name); 493 } else 494 ret = 0; 495 496 return ret; 497 } 498 499 krb5_error_code 500 kcm_ccache_store_cred(krb5_context context, 501 kcm_ccache ccache, 502 krb5_creds *creds, 503 int copy) 504 { 505 krb5_error_code ret; 506 krb5_creds *tmp; 507 508 KCM_ASSERT_VALID(ccache); 509 510 HEIMDAL_MUTEX_lock(&ccache->mutex); 511 ret = kcm_ccache_store_cred_internal(context, ccache, creds, copy, &tmp); 512 HEIMDAL_MUTEX_unlock(&ccache->mutex); 513 514 return ret; 515 } 516 517 struct kcm_creds * 518 kcm_ccache_find_cred_uuid(krb5_context context, 519 kcm_ccache ccache, 520 kcmuuid_t uuid) 521 { 522 struct kcm_creds *c; 523 524 for (c = ccache->creds; c != NULL; c = c->next) 525 if (memcmp(c->uuid, uuid, sizeof(c->uuid)) == 0) 526 return c; 527 528 return NULL; 529 } 530 531 532 533 krb5_error_code 534 kcm_ccache_store_cred_internal(krb5_context context, 535 kcm_ccache ccache, 536 krb5_creds *creds, 537 int copy, 538 krb5_creds **credp) 539 { 540 struct kcm_creds **c; 541 krb5_error_code ret; 542 543 for (c = &ccache->creds; *c != NULL; c = &(*c)->next) 544 ; 545 546 *c = (struct kcm_creds *)calloc(1, sizeof(**c)); 547 if (*c == NULL) 548 return KRB5_CC_NOMEM; 549 550 RAND_bytes((*c)->uuid, sizeof((*c)->uuid)); 551 552 *credp = &(*c)->cred; 553 554 if (copy) { 555 ret = krb5_copy_creds_contents(context, creds, *credp); 556 if (ret) { 557 free(*c); 558 *c = NULL; 559 } 560 } else { 561 **credp = *creds; 562 ret = 0; 563 } 564 565 return ret; 566 } 567 568 krb5_error_code 569 kcm_ccache_remove_cred_internal(krb5_context context, 570 kcm_ccache ccache, 571 krb5_flags whichfields, 572 const krb5_creds *mcreds) 573 { 574 krb5_error_code ret; 575 struct kcm_creds **c; 576 577 ret = KRB5_CC_NOTFOUND; 578 579 for (c = &ccache->creds; *c != NULL; c = &(*c)->next) { 580 if (krb5_compare_creds(context, whichfields, mcreds, &(*c)->cred)) { 581 struct kcm_creds *cred = *c; 582 583 *c = cred->next; 584 krb5_free_cred_contents(context, &cred->cred); 585 free(cred); 586 ret = 0; 587 if (*c == NULL) 588 break; 589 } 590 } 591 592 return ret; 593 } 594 595 krb5_error_code 596 kcm_ccache_remove_cred(krb5_context context, 597 kcm_ccache ccache, 598 krb5_flags whichfields, 599 const krb5_creds *mcreds) 600 { 601 krb5_error_code ret; 602 603 KCM_ASSERT_VALID(ccache); 604 605 HEIMDAL_MUTEX_lock(&ccache->mutex); 606 ret = kcm_ccache_remove_cred_internal(context, ccache, whichfields, mcreds); 607 HEIMDAL_MUTEX_unlock(&ccache->mutex); 608 609 return ret; 610 } 611 612 krb5_error_code 613 kcm_ccache_retrieve_cred_internal(krb5_context context, 614 kcm_ccache ccache, 615 krb5_flags whichfields, 616 const krb5_creds *mcreds, 617 krb5_creds **creds) 618 { 619 krb5_boolean match; 620 struct kcm_creds *c; 621 krb5_error_code ret; 622 623 memset(creds, 0, sizeof(*creds)); 624 625 ret = KRB5_CC_END; 626 627 match = FALSE; 628 for (c = ccache->creds; c != NULL; c = c->next) { 629 match = krb5_compare_creds(context, whichfields, mcreds, &c->cred); 630 if (match) 631 break; 632 } 633 634 if (match) { 635 ret = 0; 636 *creds = &c->cred; 637 } 638 639 return ret; 640 } 641 642 krb5_error_code 643 kcm_ccache_retrieve_cred(krb5_context context, 644 kcm_ccache ccache, 645 krb5_flags whichfields, 646 const krb5_creds *mcreds, 647 krb5_creds **credp) 648 { 649 krb5_error_code ret; 650 651 KCM_ASSERT_VALID(ccache); 652 653 HEIMDAL_MUTEX_lock(&ccache->mutex); 654 ret = kcm_ccache_retrieve_cred_internal(context, ccache, 655 whichfields, mcreds, credp); 656 HEIMDAL_MUTEX_unlock(&ccache->mutex); 657 658 return ret; 659 } 660 661 char * 662 kcm_ccache_first_name(kcm_client *client) 663 { 664 kcm_ccache p; 665 char *name = NULL; 666 667 HEIMDAL_MUTEX_lock(&ccache_mutex); 668 669 for (p = ccache_head; p != NULL; p = p->next) { 670 if (kcm_is_same_session(client, p->uid, p->session)) 671 break; 672 } 673 if (p) 674 name = strdup(p->name); 675 HEIMDAL_MUTEX_unlock(&ccache_mutex); 676 return name; 677 } 678