cache.c revision 1.1.1.1
1/* $NetBSD: cache.c,v 1.1.1.1 2011/04/13 18:14:35 elric 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 39HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER; 40kcm_ccache_data *ccache_head = NULL; 41static unsigned int ccache_nextid = 0; 42 43char *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 57krb5_error_code 58kcm_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 90krb5_error_code 91kcm_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 123krb5_error_code 124kcm_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 150krb5_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 190static void 191kcm_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 228krb5_error_code 229kcm_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 258out: 259 HEIMDAL_MUTEX_unlock(&ccache_mutex); 260 261 return ret; 262} 263 264static krb5_error_code 265kcm_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 333out: 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 342krb5_error_code 343kcm_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 362krb5_error_code 363kcm_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 377krb5_error_code 378kcm_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 396krb5_error_code 397kcm_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 411krb5_error_code 412kcm_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 424krb5_error_code 425kcm_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 443krb5_error_code 444kcm_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 464krb5_error_code 465kcm_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 483krb5_error_code 484kcm_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 499krb5_error_code 500kcm_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 517struct kcm_creds * 518kcm_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 533krb5_error_code 534kcm_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 568krb5_error_code 569kcm_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 595krb5_error_code 596kcm_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 612krb5_error_code 613kcm_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 642krb5_error_code 643kcm_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 661char * 662kcm_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