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 38 typedef 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_mcache; 51 52 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER; 53 static struct krb5_mcache *mcc_head; 54 55 #define MCACHE(X) ((krb5_mcache *)(X)->data.data) 56 57 #define MISDEAD(X) ((X)->dead) 58 59 static const char* KRB5_CALLCONV 60 mcc_get_name(krb5_context context, 61 krb5_ccache id) 62 { 63 return MCACHE(id)->name; 64 } 65 66 static krb5_mcache * KRB5_CALLCONV 67 mcc_alloc(const char *name) 68 { 69 krb5_mcache *m, *m_c; 70 int ret = 0; 71 72 ALLOC(m, 1); 73 if(m == NULL) 74 return NULL; 75 if(name == NULL) 76 ret = asprintf(&m->name, "%p", m); 77 else 78 m->name = strdup(name); 79 if(ret < 0 || m->name == NULL) { 80 free(m); 81 return NULL; 82 } 83 /* check for dups first */ 84 HEIMDAL_MUTEX_lock(&mcc_mutex); 85 for (m_c = mcc_head; m_c != NULL; m_c = m_c->next) 86 if (strcmp(m->name, m_c->name) == 0) 87 break; 88 if (m_c) { 89 free(m->name); 90 free(m); 91 HEIMDAL_MUTEX_unlock(&mcc_mutex); 92 return NULL; 93 } 94 95 m->dead = 0; 96 m->refcnt = 1; 97 m->primary_principal = NULL; 98 m->creds = NULL; 99 m->mtime = time(NULL); 100 m->kdc_offset = 0; 101 m->next = mcc_head; 102 mcc_head = m; 103 HEIMDAL_MUTEX_unlock(&mcc_mutex); 104 return m; 105 } 106 107 static krb5_error_code KRB5_CALLCONV 108 mcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 109 { 110 krb5_mcache *m; 111 112 HEIMDAL_MUTEX_lock(&mcc_mutex); 113 for (m = mcc_head; m != NULL; m = m->next) 114 if (strcmp(m->name, res) == 0) 115 break; 116 HEIMDAL_MUTEX_unlock(&mcc_mutex); 117 118 if (m != NULL) { 119 m->refcnt++; 120 (*id)->data.data = m; 121 (*id)->data.length = sizeof(*m); 122 return 0; 123 } 124 125 m = mcc_alloc(res); 126 if (m == NULL) { 127 krb5_set_error_message(context, KRB5_CC_NOMEM, 128 N_("malloc: out of memory", "")); 129 return KRB5_CC_NOMEM; 130 } 131 132 (*id)->data.data = m; 133 (*id)->data.length = sizeof(*m); 134 135 return 0; 136 } 137 138 139 static krb5_error_code KRB5_CALLCONV 140 mcc_gen_new(krb5_context context, krb5_ccache *id) 141 { 142 krb5_mcache *m; 143 144 m = mcc_alloc(NULL); 145 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 static krb5_error_code KRB5_CALLCONV 159 mcc_initialize(krb5_context context, 160 krb5_ccache id, 161 krb5_principal primary_principal) 162 { 163 krb5_mcache *m = MCACHE(id); 164 m->dead = 0; 165 m->mtime = time(NULL); 166 return krb5_copy_principal (context, 167 primary_principal, 168 &m->primary_principal); 169 } 170 171 static int 172 mcc_close_internal(krb5_mcache *m) 173 { 174 if (--m->refcnt != 0) 175 return 0; 176 177 if (MISDEAD(m)) { 178 free (m->name); 179 return 1; 180 } 181 return 0; 182 } 183 184 static krb5_error_code KRB5_CALLCONV 185 mcc_close(krb5_context context, 186 krb5_ccache id) 187 { 188 if (mcc_close_internal(MCACHE(id))) 189 krb5_data_free(&id->data); 190 return 0; 191 } 192 193 static krb5_error_code KRB5_CALLCONV 194 mcc_destroy(krb5_context context, 195 krb5_ccache id) 196 { 197 krb5_mcache **n, *m = MCACHE(id); 198 struct link *l; 199 200 if (m->refcnt == 0) 201 krb5_abortx(context, "mcc_destroy: refcnt already 0"); 202 203 if (!MISDEAD(m)) { 204 /* if this is an active mcache, remove it from the linked 205 list, and free all data */ 206 HEIMDAL_MUTEX_lock(&mcc_mutex); 207 for(n = &mcc_head; n && *n; n = &(*n)->next) { 208 if(m == *n) { 209 *n = m->next; 210 break; 211 } 212 } 213 HEIMDAL_MUTEX_unlock(&mcc_mutex); 214 if (m->primary_principal != NULL) { 215 krb5_free_principal (context, m->primary_principal); 216 m->primary_principal = NULL; 217 } 218 m->dead = 1; 219 220 l = m->creds; 221 while (l != NULL) { 222 struct link *old; 223 224 krb5_free_cred_contents (context, &l->cred); 225 old = l; 226 l = l->next; 227 free (old); 228 } 229 m->creds = NULL; 230 } 231 return 0; 232 } 233 234 static krb5_error_code KRB5_CALLCONV 235 mcc_store_cred(krb5_context context, 236 krb5_ccache id, 237 krb5_creds *creds) 238 { 239 krb5_mcache *m = MCACHE(id); 240 krb5_error_code ret; 241 struct link *l; 242 243 if (MISDEAD(m)) 244 return ENOENT; 245 246 l = malloc (sizeof(*l)); 247 if (l == NULL) { 248 krb5_set_error_message(context, KRB5_CC_NOMEM, 249 N_("malloc: out of memory", "")); 250 return KRB5_CC_NOMEM; 251 } 252 l->next = m->creds; 253 m->creds = l; 254 memset (&l->cred, 0, sizeof(l->cred)); 255 ret = krb5_copy_creds_contents (context, creds, &l->cred); 256 if (ret) { 257 m->creds = l->next; 258 free (l); 259 return ret; 260 } 261 m->mtime = time(NULL); 262 return 0; 263 } 264 265 static krb5_error_code KRB5_CALLCONV 266 mcc_get_principal(krb5_context context, 267 krb5_ccache id, 268 krb5_principal *principal) 269 { 270 krb5_mcache *m = MCACHE(id); 271 272 if (MISDEAD(m) || m->primary_principal == NULL) 273 return ENOENT; 274 return krb5_copy_principal (context, 275 m->primary_principal, 276 principal); 277 } 278 279 static krb5_error_code KRB5_CALLCONV 280 mcc_get_first (krb5_context context, 281 krb5_ccache id, 282 krb5_cc_cursor *cursor) 283 { 284 krb5_mcache *m = MCACHE(id); 285 286 if (MISDEAD(m)) 287 return ENOENT; 288 289 *cursor = m->creds; 290 return 0; 291 } 292 293 static krb5_error_code KRB5_CALLCONV 294 mcc_get_next (krb5_context context, 295 krb5_ccache id, 296 krb5_cc_cursor *cursor, 297 krb5_creds *creds) 298 { 299 krb5_mcache *m = MCACHE(id); 300 struct link *l; 301 302 if (MISDEAD(m)) 303 return ENOENT; 304 305 l = *cursor; 306 if (l != NULL) { 307 *cursor = l->next; 308 return krb5_copy_creds_contents (context, 309 &l->cred, 310 creds); 311 } else 312 return KRB5_CC_END; 313 } 314 315 static krb5_error_code KRB5_CALLCONV 316 mcc_end_get (krb5_context context, 317 krb5_ccache id, 318 krb5_cc_cursor *cursor) 319 { 320 return 0; 321 } 322 323 static krb5_error_code KRB5_CALLCONV 324 mcc_remove_cred(krb5_context context, 325 krb5_ccache id, 326 krb5_flags which, 327 krb5_creds *mcreds) 328 { 329 krb5_mcache *m = MCACHE(id); 330 struct link **q, *p; 331 for(q = &m->creds, p = *q; p; p = *q) { 332 if(krb5_compare_creds(context, which, mcreds, &p->cred)) { 333 *q = p->next; 334 krb5_free_cred_contents(context, &p->cred); 335 free(p); 336 m->mtime = time(NULL); 337 } else 338 q = &p->next; 339 } 340 return 0; 341 } 342 343 static krb5_error_code KRB5_CALLCONV 344 mcc_set_flags(krb5_context context, 345 krb5_ccache id, 346 krb5_flags flags) 347 { 348 return 0; /* XXX */ 349 } 350 351 struct mcache_iter { 352 krb5_mcache *cache; 353 }; 354 355 static krb5_error_code KRB5_CALLCONV 356 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 357 { 358 struct mcache_iter *iter; 359 360 iter = calloc(1, sizeof(*iter)); 361 if (iter == NULL) { 362 krb5_set_error_message(context, ENOMEM, 363 N_("malloc: out of memory", "")); 364 return ENOMEM; 365 } 366 367 HEIMDAL_MUTEX_lock(&mcc_mutex); 368 iter->cache = mcc_head; 369 if (iter->cache) 370 iter->cache->refcnt++; 371 HEIMDAL_MUTEX_unlock(&mcc_mutex); 372 373 *cursor = iter; 374 return 0; 375 } 376 377 static krb5_error_code KRB5_CALLCONV 378 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 379 { 380 struct mcache_iter *iter = cursor; 381 krb5_error_code ret; 382 krb5_mcache *m; 383 384 if (iter->cache == NULL) 385 return KRB5_CC_END; 386 387 HEIMDAL_MUTEX_lock(&mcc_mutex); 388 m = iter->cache; 389 if (m->next) 390 m->next->refcnt++; 391 iter->cache = m->next; 392 HEIMDAL_MUTEX_unlock(&mcc_mutex); 393 394 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id); 395 if (ret) 396 return ret; 397 398 (*id)->data.data = m; 399 (*id)->data.length = sizeof(*m); 400 401 return 0; 402 } 403 404 static krb5_error_code KRB5_CALLCONV 405 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 406 { 407 struct mcache_iter *iter = cursor; 408 409 if (iter->cache) 410 mcc_close_internal(iter->cache); 411 iter->cache = NULL; 412 free(iter); 413 return 0; 414 } 415 416 static krb5_error_code KRB5_CALLCONV 417 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 418 { 419 krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to); 420 struct link *creds; 421 krb5_principal principal; 422 krb5_mcache **n; 423 424 HEIMDAL_MUTEX_lock(&mcc_mutex); 425 426 /* drop the from cache from the linked list to avoid lookups */ 427 for(n = &mcc_head; n && *n; n = &(*n)->next) { 428 if(mfrom == *n) { 429 *n = mfrom->next; 430 break; 431 } 432 } 433 434 /* swap creds */ 435 creds = mto->creds; 436 mto->creds = mfrom->creds; 437 mfrom->creds = creds; 438 /* swap principal */ 439 principal = mto->primary_principal; 440 mto->primary_principal = mfrom->primary_principal; 441 mfrom->primary_principal = principal; 442 443 mto->mtime = mfrom->mtime = time(NULL); 444 445 HEIMDAL_MUTEX_unlock(&mcc_mutex); 446 mcc_destroy(context, from); 447 448 return 0; 449 } 450 451 static krb5_error_code KRB5_CALLCONV 452 mcc_default_name(krb5_context context, char **str) 453 { 454 *str = strdup("MEMORY:"); 455 if (*str == NULL) { 456 krb5_set_error_message(context, ENOMEM, 457 N_("malloc: out of memory", "")); 458 return ENOMEM; 459 } 460 return 0; 461 } 462 463 static krb5_error_code KRB5_CALLCONV 464 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 465 { 466 *mtime = MCACHE(id)->mtime; 467 return 0; 468 } 469 470 static krb5_error_code KRB5_CALLCONV 471 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 472 { 473 krb5_mcache *m = MCACHE(id); 474 m->kdc_offset = kdc_offset; 475 return 0; 476 } 477 478 static krb5_error_code KRB5_CALLCONV 479 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 480 { 481 krb5_mcache *m = MCACHE(id); 482 *kdc_offset = m->kdc_offset; 483 return 0; 484 } 485 486 487 /** 488 * Variable containing the MEMORY based credential cache implemention. 489 * 490 * @ingroup krb5_ccache 491 */ 492 493 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = { 494 KRB5_CC_OPS_VERSION, 495 "MEMORY", 496 mcc_get_name, 497 mcc_resolve, 498 mcc_gen_new, 499 mcc_initialize, 500 mcc_destroy, 501 mcc_close, 502 mcc_store_cred, 503 NULL, /* mcc_retrieve */ 504 mcc_get_principal, 505 mcc_get_first, 506 mcc_get_next, 507 mcc_end_get, 508 mcc_remove_cred, 509 mcc_set_flags, 510 NULL, 511 mcc_get_cache_first, 512 mcc_get_cache_next, 513 mcc_end_cache_get, 514 mcc_move, 515 mcc_default_name, 516 NULL, 517 mcc_lastchange, 518 mcc_set_kdc_offset, 519 mcc_get_kdc_offset 520 }; 521