1 /* $NetBSD: mcache.c,v 1.2 2017/01/28 21:31:49 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "krb5_locl.h" 39 40 typedef struct krb5_mcache { 41 char *name; 42 unsigned int refcnt; 43 int dead; 44 krb5_principal primary_principal; 45 struct link { 46 krb5_creds cred; 47 struct link *next; 48 } *creds; 49 struct krb5_mcache *next; 50 time_t mtime; 51 krb5_deltat kdc_offset; 52 HEIMDAL_MUTEX mutex; 53 } krb5_mcache; 54 55 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER; 56 static struct krb5_mcache *mcc_head; 57 58 #define MCACHE(X) ((krb5_mcache *)(X)->data.data) 59 60 #define MISDEAD(X) ((X)->dead) 61 62 static const char* KRB5_CALLCONV 63 mcc_get_name(krb5_context context, 64 krb5_ccache id) 65 { 66 return MCACHE(id)->name; 67 } 68 69 static krb5_mcache * KRB5_CALLCONV 70 mcc_alloc(const char *name) 71 { 72 krb5_mcache *m, *m_c; 73 int ret = 0; 74 75 ALLOC(m, 1); 76 if(m == NULL) 77 return NULL; 78 if(name == NULL) 79 ret = asprintf(&m->name, "%p", m); 80 else 81 m->name = strdup(name); 82 if(ret < 0 || m->name == NULL) { 83 free(m); 84 return NULL; 85 } 86 /* check for dups first */ 87 HEIMDAL_MUTEX_lock(&mcc_mutex); 88 for (m_c = mcc_head; m_c != NULL; m_c = m_c->next) 89 if (strcmp(m->name, m_c->name) == 0) 90 break; 91 if (m_c) { 92 free(m->name); 93 free(m); 94 HEIMDAL_MUTEX_unlock(&mcc_mutex); 95 return NULL; 96 } 97 98 m->dead = 0; 99 m->refcnt = 1; 100 m->primary_principal = NULL; 101 m->creds = NULL; 102 m->mtime = time(NULL); 103 m->kdc_offset = 0; 104 m->next = mcc_head; 105 HEIMDAL_MUTEX_init(&(m->mutex)); 106 mcc_head = m; 107 HEIMDAL_MUTEX_unlock(&mcc_mutex); 108 return m; 109 } 110 111 static krb5_error_code KRB5_CALLCONV 112 mcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 113 { 114 krb5_mcache *m; 115 116 HEIMDAL_MUTEX_lock(&mcc_mutex); 117 for (m = mcc_head; m != NULL; m = m->next) 118 if (strcmp(m->name, res) == 0) 119 break; 120 HEIMDAL_MUTEX_unlock(&mcc_mutex); 121 122 if (m != NULL) { 123 HEIMDAL_MUTEX_lock(&(m->mutex)); 124 m->refcnt++; 125 HEIMDAL_MUTEX_unlock(&(m->mutex)); 126 (*id)->data.data = m; 127 (*id)->data.length = sizeof(*m); 128 return 0; 129 } 130 131 m = mcc_alloc(res); 132 if (m == NULL) { 133 krb5_set_error_message(context, KRB5_CC_NOMEM, 134 N_("malloc: out of memory", "")); 135 return KRB5_CC_NOMEM; 136 } 137 138 (*id)->data.data = m; 139 (*id)->data.length = sizeof(*m); 140 141 return 0; 142 } 143 144 145 static krb5_error_code KRB5_CALLCONV 146 mcc_gen_new(krb5_context context, krb5_ccache *id) 147 { 148 krb5_mcache *m; 149 150 m = mcc_alloc(NULL); 151 152 if (m == NULL) { 153 krb5_set_error_message(context, KRB5_CC_NOMEM, 154 N_("malloc: out of memory", "")); 155 return KRB5_CC_NOMEM; 156 } 157 158 (*id)->data.data = m; 159 (*id)->data.length = sizeof(*m); 160 161 return 0; 162 } 163 164 static void KRB5_CALLCONV 165 mcc_destroy_internal(krb5_context context, 166 krb5_mcache *m) 167 { 168 struct link *l; 169 170 if (m->primary_principal != NULL) { 171 krb5_free_principal (context, m->primary_principal); 172 m->primary_principal = NULL; 173 } 174 m->dead = 1; 175 176 l = m->creds; 177 while (l != NULL) { 178 struct link *old; 179 180 krb5_free_cred_contents (context, &l->cred); 181 old = l; 182 l = l->next; 183 free (old); 184 } 185 186 m->creds = NULL; 187 return; 188 } 189 190 static krb5_error_code KRB5_CALLCONV 191 mcc_initialize(krb5_context context, 192 krb5_ccache id, 193 krb5_principal primary_principal) 194 { 195 krb5_mcache *m = MCACHE(id); 196 krb5_error_code ret = 0; 197 HEIMDAL_MUTEX_lock(&(m->mutex)); 198 heim_assert(m->refcnt != 0, "resurection released mcache"); 199 /* 200 * It's important to destroy any existing 201 * creds here, that matches the baheviour 202 * of all other backends and also the 203 * MEMORY: backend in MIT. 204 */ 205 mcc_destroy_internal(context, m); 206 m->dead = 0; 207 m->kdc_offset = 0; 208 m->mtime = time(NULL); 209 ret = krb5_copy_principal (context, 210 primary_principal, 211 &m->primary_principal); 212 HEIMDAL_MUTEX_unlock(&(m->mutex)); 213 return ret; 214 } 215 216 static int 217 mcc_close_internal(krb5_mcache *m) 218 { 219 HEIMDAL_MUTEX_lock(&(m->mutex)); 220 heim_assert(m->refcnt != 0, "closed dead cache mcache"); 221 if (--m->refcnt != 0) { 222 HEIMDAL_MUTEX_unlock(&(m->mutex)); 223 return 0; 224 } 225 if (MISDEAD(m)) { 226 free (m->name); 227 HEIMDAL_MUTEX_unlock(&(m->mutex)); 228 return 1; 229 } 230 HEIMDAL_MUTEX_unlock(&(m->mutex)); 231 return 0; 232 } 233 234 static krb5_error_code KRB5_CALLCONV 235 mcc_close(krb5_context context, 236 krb5_ccache id) 237 { 238 krb5_mcache *m = MCACHE(id); 239 240 if (mcc_close_internal(MCACHE(id))) { 241 HEIMDAL_MUTEX_destroy(&(m->mutex)); 242 krb5_data_free(&id->data); 243 } 244 return 0; 245 } 246 247 static krb5_error_code KRB5_CALLCONV 248 mcc_destroy(krb5_context context, 249 krb5_ccache id) 250 { 251 krb5_mcache **n, *m = MCACHE(id); 252 253 HEIMDAL_MUTEX_lock(&(m->mutex)); 254 if (m->refcnt == 0) 255 { 256 HEIMDAL_MUTEX_unlock(&(m->mutex)); 257 krb5_abortx(context, "mcc_destroy: refcnt already 0"); 258 } 259 260 if (!MISDEAD(m)) { 261 /* if this is an active mcache, remove it from the linked 262 list, and free all data */ 263 HEIMDAL_MUTEX_lock(&mcc_mutex); 264 for(n = &mcc_head; n && *n; n = &(*n)->next) { 265 if(m == *n) { 266 *n = m->next; 267 break; 268 } 269 } 270 HEIMDAL_MUTEX_unlock(&mcc_mutex); 271 mcc_destroy_internal(context, m); 272 } 273 HEIMDAL_MUTEX_unlock(&(m->mutex)); 274 return 0; 275 } 276 277 static krb5_error_code KRB5_CALLCONV 278 mcc_store_cred(krb5_context context, 279 krb5_ccache id, 280 krb5_creds *creds) 281 { 282 krb5_mcache *m = MCACHE(id); 283 krb5_error_code ret; 284 struct link *l; 285 286 HEIMDAL_MUTEX_lock(&(m->mutex)); 287 if (MISDEAD(m)) 288 { 289 HEIMDAL_MUTEX_unlock(&(m->mutex)); 290 return ENOENT; 291 } 292 293 l = malloc (sizeof(*l)); 294 if (l == NULL) { 295 krb5_set_error_message(context, KRB5_CC_NOMEM, 296 N_("malloc: out of memory", "")); 297 HEIMDAL_MUTEX_unlock(&(m->mutex)); 298 return KRB5_CC_NOMEM; 299 } 300 l->next = m->creds; 301 m->creds = l; 302 memset (&l->cred, 0, sizeof(l->cred)); 303 ret = krb5_copy_creds_contents (context, creds, &l->cred); 304 if (ret) { 305 m->creds = l->next; 306 free (l); 307 HEIMDAL_MUTEX_unlock(&(m->mutex)); 308 return ret; 309 } 310 m->mtime = time(NULL); 311 HEIMDAL_MUTEX_unlock(&(m->mutex)); 312 return 0; 313 } 314 315 static krb5_error_code KRB5_CALLCONV 316 mcc_get_principal(krb5_context context, 317 krb5_ccache id, 318 krb5_principal *principal) 319 { 320 krb5_mcache *m = MCACHE(id); 321 krb5_error_code ret = 0; 322 323 HEIMDAL_MUTEX_lock(&(m->mutex)); 324 if (MISDEAD(m) || m->primary_principal == NULL) { 325 HEIMDAL_MUTEX_unlock(&(m->mutex)); 326 return ENOENT; 327 } 328 ret = krb5_copy_principal (context, 329 m->primary_principal, 330 principal); 331 HEIMDAL_MUTEX_unlock(&(m->mutex)); 332 return ret; 333 } 334 335 static krb5_error_code KRB5_CALLCONV 336 mcc_get_first (krb5_context context, 337 krb5_ccache id, 338 krb5_cc_cursor *cursor) 339 { 340 krb5_mcache *m = MCACHE(id); 341 342 HEIMDAL_MUTEX_lock(&(m->mutex)); 343 if (MISDEAD(m)) { 344 HEIMDAL_MUTEX_unlock(&(m->mutex)); 345 return ENOENT; 346 } 347 *cursor = m->creds; 348 349 HEIMDAL_MUTEX_unlock(&(m->mutex)); 350 return 0; 351 } 352 353 static krb5_error_code KRB5_CALLCONV 354 mcc_get_next (krb5_context context, 355 krb5_ccache id, 356 krb5_cc_cursor *cursor, 357 krb5_creds *creds) 358 { 359 krb5_mcache *m = MCACHE(id); 360 struct link *l; 361 362 HEIMDAL_MUTEX_lock(&(m->mutex)); 363 if (MISDEAD(m)) { 364 HEIMDAL_MUTEX_unlock(&(m->mutex)); 365 return ENOENT; 366 } 367 HEIMDAL_MUTEX_unlock(&(m->mutex)); 368 369 l = *cursor; 370 if (l != NULL) { 371 *cursor = l->next; 372 return krb5_copy_creds_contents (context, 373 &l->cred, 374 creds); 375 } else 376 return KRB5_CC_END; 377 } 378 379 static krb5_error_code KRB5_CALLCONV 380 mcc_end_get (krb5_context context, 381 krb5_ccache id, 382 krb5_cc_cursor *cursor) 383 { 384 return 0; 385 } 386 387 static krb5_error_code KRB5_CALLCONV 388 mcc_remove_cred(krb5_context context, 389 krb5_ccache id, 390 krb5_flags which, 391 krb5_creds *mcreds) 392 { 393 krb5_mcache *m = MCACHE(id); 394 struct link **q, *p; 395 396 HEIMDAL_MUTEX_lock(&(m->mutex)); 397 398 for(q = &m->creds, p = *q; p; p = *q) { 399 if(krb5_compare_creds(context, which, mcreds, &p->cred)) { 400 *q = p->next; 401 krb5_free_cred_contents(context, &p->cred); 402 free(p); 403 m->mtime = time(NULL); 404 } else 405 q = &p->next; 406 } 407 HEIMDAL_MUTEX_unlock(&(m->mutex)); 408 return 0; 409 } 410 411 static krb5_error_code KRB5_CALLCONV 412 mcc_set_flags(krb5_context context, 413 krb5_ccache id, 414 krb5_flags flags) 415 { 416 return 0; /* XXX */ 417 } 418 419 struct mcache_iter { 420 krb5_mcache *cache; 421 }; 422 423 static krb5_error_code KRB5_CALLCONV 424 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 425 { 426 struct mcache_iter *iter; 427 428 iter = calloc(1, sizeof(*iter)); 429 if (iter == NULL) 430 return krb5_enomem(context); 431 432 HEIMDAL_MUTEX_lock(&mcc_mutex); 433 iter->cache = mcc_head; 434 if (iter->cache) { 435 HEIMDAL_MUTEX_lock(&(iter->cache->mutex)); 436 iter->cache->refcnt++; 437 HEIMDAL_MUTEX_unlock(&(iter->cache->mutex)); 438 } 439 HEIMDAL_MUTEX_unlock(&mcc_mutex); 440 441 *cursor = iter; 442 return 0; 443 } 444 445 static krb5_error_code KRB5_CALLCONV 446 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 447 { 448 struct mcache_iter *iter = cursor; 449 krb5_error_code ret; 450 krb5_mcache *m; 451 452 if (iter->cache == NULL) 453 return KRB5_CC_END; 454 455 HEIMDAL_MUTEX_lock(&mcc_mutex); 456 m = iter->cache; 457 if (m->next) 458 { 459 HEIMDAL_MUTEX_lock(&(m->next->mutex)); 460 m->next->refcnt++; 461 HEIMDAL_MUTEX_unlock(&(m->next->mutex)); 462 } 463 464 iter->cache = m->next; 465 HEIMDAL_MUTEX_unlock(&mcc_mutex); 466 467 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id); 468 if (ret) 469 return ret; 470 471 (*id)->data.data = m; 472 (*id)->data.length = sizeof(*m); 473 474 return 0; 475 } 476 477 static krb5_error_code KRB5_CALLCONV 478 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 479 { 480 struct mcache_iter *iter = cursor; 481 482 if (iter->cache) 483 mcc_close_internal(iter->cache); 484 iter->cache = NULL; 485 free(iter); 486 return 0; 487 } 488 489 static krb5_error_code KRB5_CALLCONV 490 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 491 { 492 krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to); 493 struct link *creds; 494 krb5_principal principal; 495 krb5_mcache **n; 496 497 HEIMDAL_MUTEX_lock(&mcc_mutex); 498 499 /* drop the from cache from the linked list to avoid lookups */ 500 for(n = &mcc_head; n && *n; n = &(*n)->next) { 501 if(mfrom == *n) { 502 *n = mfrom->next; 503 break; 504 } 505 } 506 507 HEIMDAL_MUTEX_lock(&(mfrom->mutex)); 508 HEIMDAL_MUTEX_lock(&(mto->mutex)); 509 /* swap creds */ 510 creds = mto->creds; 511 mto->creds = mfrom->creds; 512 mfrom->creds = creds; 513 /* swap principal */ 514 principal = mto->primary_principal; 515 mto->primary_principal = mfrom->primary_principal; 516 mfrom->primary_principal = principal; 517 518 mto->mtime = mfrom->mtime = time(NULL); 519 520 HEIMDAL_MUTEX_unlock(&(mfrom->mutex)); 521 HEIMDAL_MUTEX_unlock(&(mto->mutex)); 522 HEIMDAL_MUTEX_unlock(&mcc_mutex); 523 mcc_destroy(context, from); 524 525 return 0; 526 } 527 528 static krb5_error_code KRB5_CALLCONV 529 mcc_default_name(krb5_context context, char **str) 530 { 531 *str = strdup("MEMORY:"); 532 if (*str == NULL) 533 return krb5_enomem(context); 534 return 0; 535 } 536 537 static krb5_error_code KRB5_CALLCONV 538 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 539 { 540 krb5_mcache *m = MCACHE(id); 541 HEIMDAL_MUTEX_lock(&(m->mutex)); 542 *mtime = m->mtime; 543 HEIMDAL_MUTEX_unlock(&(m->mutex)); 544 return 0; 545 } 546 547 static krb5_error_code KRB5_CALLCONV 548 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 549 { 550 krb5_mcache *m = MCACHE(id); 551 HEIMDAL_MUTEX_lock(&(m->mutex)); 552 m->kdc_offset = kdc_offset; 553 HEIMDAL_MUTEX_unlock(&(m->mutex)); 554 return 0; 555 } 556 557 static krb5_error_code KRB5_CALLCONV 558 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 559 { 560 krb5_mcache *m = MCACHE(id); 561 HEIMDAL_MUTEX_lock(&(m->mutex)); 562 *kdc_offset = m->kdc_offset; 563 HEIMDAL_MUTEX_unlock(&(m->mutex)); 564 return 0; 565 } 566 567 568 /** 569 * Variable containing the MEMORY based credential cache implemention. 570 * 571 * @ingroup krb5_ccache 572 */ 573 574 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = { 575 KRB5_CC_OPS_VERSION, 576 "MEMORY", 577 mcc_get_name, 578 mcc_resolve, 579 mcc_gen_new, 580 mcc_initialize, 581 mcc_destroy, 582 mcc_close, 583 mcc_store_cred, 584 NULL, /* mcc_retrieve */ 585 mcc_get_principal, 586 mcc_get_first, 587 mcc_get_next, 588 mcc_end_get, 589 mcc_remove_cred, 590 mcc_set_flags, 591 NULL, 592 mcc_get_cache_first, 593 mcc_get_cache_next, 594 mcc_end_cache_get, 595 mcc_move, 596 mcc_default_name, 597 NULL, 598 mcc_lastchange, 599 mcc_set_kdc_offset, 600 mcc_get_kdc_offset 601 }; 602