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