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