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