1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * lib/krb5/ccache/cc_memory.c 10 * 11 * Copyright 1990,1991,2000,2004 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 * 34 * implementation of memory-based credentials cache 35 */ 36 #include "k5-int.h" 37 #include <errno.h> 38 39 static krb5_error_code KRB5_CALLCONV krb5_mcc_close 40 (krb5_context, krb5_ccache id ); 41 42 static krb5_error_code KRB5_CALLCONV krb5_mcc_destroy 43 (krb5_context, krb5_ccache id ); 44 45 static krb5_error_code KRB5_CALLCONV krb5_mcc_end_seq_get 46 (krb5_context, krb5_ccache id , krb5_cc_cursor *cursor ); 47 48 static krb5_error_code KRB5_CALLCONV krb5_mcc_generate_new 49 (krb5_context, krb5_ccache *id ); 50 51 static const char * KRB5_CALLCONV krb5_mcc_get_name 52 (krb5_context, krb5_ccache id ); 53 54 static krb5_error_code KRB5_CALLCONV krb5_mcc_get_principal 55 (krb5_context, krb5_ccache id , krb5_principal *princ ); 56 57 static krb5_error_code KRB5_CALLCONV krb5_mcc_initialize 58 (krb5_context, krb5_ccache id , krb5_principal princ ); 59 60 static krb5_error_code KRB5_CALLCONV krb5_mcc_next_cred 61 (krb5_context, 62 krb5_ccache id , 63 krb5_cc_cursor *cursor , 64 krb5_creds *creds ); 65 66 static krb5_error_code KRB5_CALLCONV krb5_mcc_resolve 67 (krb5_context, krb5_ccache *id , const char *residual ); 68 69 static krb5_error_code KRB5_CALLCONV krb5_mcc_retrieve 70 (krb5_context, 71 krb5_ccache id , 72 krb5_flags whichfields , 73 krb5_creds *mcreds , 74 krb5_creds *creds ); 75 76 static krb5_error_code KRB5_CALLCONV krb5_mcc_start_seq_get 77 (krb5_context, krb5_ccache id , krb5_cc_cursor *cursor ); 78 79 static krb5_error_code KRB5_CALLCONV krb5_mcc_store 80 (krb5_context, krb5_ccache id , krb5_creds *creds ); 81 82 static krb5_error_code KRB5_CALLCONV krb5_mcc_set_flags 83 (krb5_context, krb5_ccache id , krb5_flags flags ); 84 85 extern const krb5_cc_ops krb5_mcc_ops; 86 extern krb5_error_code krb5_change_cache (void); 87 88 #define KRB5_OK 0 89 90 typedef struct _krb5_mcc_link { 91 struct _krb5_mcc_link *next; 92 krb5_creds *creds; 93 } krb5_mcc_link, *krb5_mcc_cursor; 94 95 typedef struct _krb5_mcc_data { 96 char *name; 97 k5_mutex_t lock; 98 krb5_principal prin; 99 krb5_mcc_cursor link; 100 } krb5_mcc_data; 101 102 typedef struct krb5_mcc_list_node { 103 struct krb5_mcc_list_node *next; 104 krb5_mcc_data *cache; 105 } krb5_mcc_list_node; 106 107 k5_mutex_t krb5int_mcc_mutex = K5_MUTEX_PARTIAL_INITIALIZER; 108 static krb5_mcc_list_node *mcc_head = 0; 109 110 /* 111 * Modifies: 112 * id 113 * 114 * Effects: 115 * Creates/refreshes the file cred cache id. If the cache exists, its 116 * contents are destroyed. 117 * 118 * Errors: 119 * system errors 120 * permission errors 121 */ 122 static void krb5_mcc_free (krb5_context context, krb5_ccache id); 123 124 krb5_error_code KRB5_CALLCONV 125 krb5_mcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ) 126 { 127 krb5_error_code ret; 128 129 krb5_mcc_free(context, id); 130 ret = krb5_copy_principal(context, princ, 131 &((krb5_mcc_data *)id->data)->prin); 132 if (ret == KRB5_OK) 133 krb5_change_cache(); 134 return ret; 135 } 136 137 /* 138 * Modifies: 139 * id 140 * 141 * Effects: 142 * Closes the file cache, invalidates the id, and frees any resources 143 * associated with the cache. 144 */ 145 krb5_error_code KRB5_CALLCONV 146 krb5_mcc_close(krb5_context context, krb5_ccache id) 147 { 148 krb5_xfree(id); 149 return KRB5_OK; 150 } 151 152 void 153 krb5_mcc_free(krb5_context context, krb5_ccache id) 154 { 155 krb5_mcc_cursor curr,next; 156 krb5_mcc_data *d; 157 158 d = (krb5_mcc_data *) id->data; 159 for (curr = d->link; curr;) { 160 krb5_free_creds(context, curr->creds); 161 next = curr->next; 162 krb5_xfree(curr); 163 curr = next; 164 } 165 d->link = NULL; 166 krb5_free_principal(context, d->prin); 167 } 168 169 /* 170 * Effects: 171 * Destroys the contents of id. 172 * 173 * Errors: 174 * none 175 */ 176 krb5_error_code KRB5_CALLCONV 177 krb5_mcc_destroy(krb5_context context, krb5_ccache id) 178 { 179 krb5_mcc_list_node **curr, *node; 180 krb5_mcc_data *d; 181 krb5_error_code err; 182 183 err = k5_mutex_lock(&krb5int_mcc_mutex); 184 if (err) 185 return err; 186 187 d = (krb5_mcc_data *)id->data; 188 for (curr = &mcc_head; *curr; curr = &(*curr)->next) { 189 if ((*curr)->cache == d) { 190 node = *curr; 191 *curr = node->next; 192 free(node); 193 break; 194 } 195 } 196 k5_mutex_unlock(&krb5int_mcc_mutex); 197 198 krb5_mcc_free(context, id); 199 krb5_xfree(d->name); 200 k5_mutex_destroy(&d->lock); 201 krb5_xfree(d); 202 krb5_xfree(id); 203 204 krb5_change_cache (); 205 return KRB5_OK; 206 } 207 208 /* 209 * Requires: 210 * residual is a legal path name, and a null-terminated string 211 * 212 * Modifies: 213 * id 214 * 215 * Effects: 216 * creates a file-based cred cache that will reside in the file 217 * residual. The cache is not opened, but the filename is reserved. 218 * 219 * Returns: 220 * A filled in krb5_ccache structure "id". 221 * 222 * Errors: 223 * KRB5_CC_NOMEM - there was insufficient memory to allocate the 224 * krb5_ccache. id is undefined. 225 * permission errors 226 */ 227 static krb5_error_code new_mcc_data (const char *, krb5_mcc_data **); 228 229 krb5_error_code KRB5_CALLCONV 230 krb5_mcc_resolve (krb5_context context, krb5_ccache *id, const char *residual) 231 { 232 krb5_ccache lid; 233 krb5_mcc_list_node *ptr; 234 krb5_error_code err; 235 krb5_mcc_data *d; 236 237 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); 238 if (lid == NULL) 239 return KRB5_CC_NOMEM; 240 241 lid->ops = &krb5_mcc_ops; 242 243 err = k5_mutex_lock(&krb5int_mcc_mutex); 244 if (err) { 245 /* SUNW14resync - fix mem leak */ 246 krb5_xfree(lid); 247 return err; 248 } 249 for (ptr = mcc_head; ptr; ptr=ptr->next) 250 if (!strcmp(ptr->cache->name, residual)) 251 break; 252 if (ptr) 253 d = ptr->cache; 254 else { 255 err = new_mcc_data(residual, &d); 256 if (err) { 257 k5_mutex_unlock(&krb5int_mcc_mutex); 258 krb5_xfree(lid); 259 return err; 260 } 261 } 262 k5_mutex_unlock(&krb5int_mcc_mutex); 263 lid->data = d; 264 *id = lid; 265 return KRB5_OK; 266 } 267 268 /* 269 * Effects: 270 * Prepares for a sequential search of the credentials cache. 271 * Returns a krb5_cc_cursor to be used with krb5_mcc_next_cred and 272 * krb5_mcc_end_seq_get. 273 * 274 * If the cache is modified between the time of this call and the time 275 * of the final krb5_mcc_end_seq_get, the results are undefined. 276 * 277 * Errors: 278 * KRB5_CC_NOMEM 279 * system errors 280 */ 281 krb5_error_code KRB5_CALLCONV 282 krb5_mcc_start_seq_get(krb5_context context, krb5_ccache id, 283 krb5_cc_cursor *cursor) 284 { 285 krb5_mcc_cursor mcursor; 286 krb5_error_code err; 287 krb5_mcc_data *d; 288 289 d = id->data; 290 err = k5_mutex_lock(&d->lock); 291 if (err) 292 return err; 293 mcursor = d->link; 294 k5_mutex_unlock(&d->lock); 295 *cursor = (krb5_cc_cursor) mcursor; 296 return KRB5_OK; 297 } 298 299 /* 300 * Requires: 301 * cursor is a krb5_cc_cursor originally obtained from 302 * krb5_mcc_start_seq_get. 303 * 304 * Modifes: 305 * cursor, creds 306 * 307 * Effects: 308 * Fills in creds with the "next" credentals structure from the cache 309 * id. The actual order the creds are returned in is arbitrary. 310 * Space is allocated for the variable length fields in the 311 * credentials structure, so the object returned must be passed to 312 * krb5_destroy_credential. 313 * 314 * The cursor is updated for the next call to krb5_mcc_next_cred. 315 * 316 * Errors: 317 * system errors 318 */ 319 krb5_error_code KRB5_CALLCONV 320 krb5_mcc_next_cred(krb5_context context, krb5_ccache id, 321 krb5_cc_cursor *cursor, krb5_creds *creds) 322 { 323 krb5_mcc_cursor mcursor; 324 krb5_error_code retval; 325 krb5_data *scratch; 326 327 /* Once the node in the linked list is created, it's never 328 modified, so we don't need to worry about locking here. (Note 329 that we don't support _remove_cred.) */ 330 mcursor = (krb5_mcc_cursor) *cursor; 331 if (mcursor == NULL) 332 return KRB5_CC_END; 333 memset(creds, 0, sizeof(krb5_creds)); 334 if (mcursor->creds) { 335 *creds = *mcursor->creds; 336 retval = krb5_copy_principal(context, mcursor->creds->client, &creds->client); 337 if (retval) 338 return retval; 339 retval = krb5_copy_principal(context, mcursor->creds->server, 340 &creds->server); 341 if (retval) 342 goto cleanclient; 343 retval = krb5_copy_keyblock_contents(context, &mcursor->creds->keyblock, 344 &creds->keyblock); 345 if (retval) 346 goto cleanserver; 347 retval = krb5_copy_addresses(context, mcursor->creds->addresses, 348 &creds->addresses); 349 if (retval) 350 goto cleanblock; 351 retval = krb5_copy_data(context, &mcursor->creds->ticket, &scratch); 352 if (retval) 353 goto cleanaddrs; 354 creds->ticket = *scratch; 355 krb5_xfree(scratch); 356 retval = krb5_copy_data(context, &mcursor->creds->second_ticket, &scratch); 357 if (retval) 358 goto cleanticket; 359 creds->second_ticket = *scratch; 360 krb5_xfree(scratch); 361 retval = krb5_copy_authdata(context, mcursor->creds->authdata, 362 &creds->authdata); 363 if (retval) 364 goto clearticket; 365 } 366 *cursor = (krb5_cc_cursor)mcursor->next; 367 return KRB5_OK; 368 369 clearticket: 370 memset(creds->ticket.data,0, (unsigned) creds->ticket.length); 371 cleanticket: 372 krb5_xfree(creds->ticket.data); 373 cleanaddrs: 374 krb5_free_addresses(context, creds->addresses); 375 cleanblock: 376 krb5_xfree(creds->keyblock.contents); 377 cleanserver: 378 krb5_free_principal(context, creds->server); 379 cleanclient: 380 krb5_free_principal(context, creds->client); 381 return retval; 382 } 383 384 /* 385 * Requires: 386 * cursor is a krb5_cc_cursor originally obtained from 387 * krb5_mcc_start_seq_get. 388 * 389 * Modifies: 390 * id, cursor 391 * 392 * Effects: 393 * Finishes sequential processing of the file credentials ccache id, 394 * and invalidates the cursor (it must never be used after this call). 395 */ 396 /* ARGSUSED */ 397 krb5_error_code KRB5_CALLCONV 398 krb5_mcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor) 399 { 400 *cursor = 0L; 401 return KRB5_OK; 402 } 403 404 /* Utility routine: Creates the back-end data for a memory cache, and 405 threads it into the global linked list. 406 407 Call with the global list lock held. */ 408 static krb5_error_code 409 new_mcc_data (const char *name, krb5_mcc_data **dataptr) 410 { 411 krb5_error_code err; 412 krb5_mcc_data *d; 413 krb5_mcc_list_node *n; 414 415 d = malloc(sizeof(krb5_mcc_data)); 416 if (d == NULL) 417 return KRB5_CC_NOMEM; 418 419 err = k5_mutex_init(&d->lock); 420 if (err) { 421 krb5_xfree(d); 422 return err; 423 } 424 425 d->name = malloc(strlen(name) + 1); 426 if (d->name == NULL) { 427 k5_mutex_destroy(&d->lock); 428 krb5_xfree(d); 429 return KRB5_CC_NOMEM; 430 } 431 d->link = NULL; 432 d->prin = NULL; 433 434 /* Set up the filename */ 435 strcpy(d->name, name); 436 437 n = malloc(sizeof(krb5_mcc_list_node)); 438 if (n == NULL) { 439 free(d->name); 440 k5_mutex_destroy(&d->lock); 441 free(d); 442 return KRB5_CC_NOMEM; 443 } 444 445 n->cache = d; 446 n->next = mcc_head; 447 mcc_head = n; 448 449 *dataptr = d; 450 return 0; 451 } 452 453 /* 454 * Effects: 455 * Creates a new file cred cache whose name is guaranteed to be 456 * unique. The name begins with the string TKT_ROOT (from mcc.h). 457 * The cache is not opened, but the new filename is reserved. 458 * 459 * Returns: 460 * The filled in krb5_ccache id. 461 * 462 * Errors: 463 * KRB5_CC_NOMEM - there was insufficient memory to allocate the 464 * krb5_ccache. id is undefined. 465 * system errors (from open) 466 */ 467 krb5_error_code KRB5_CALLCONV 468 krb5_mcc_generate_new (krb5_context context, krb5_ccache *id) 469 { 470 krb5_ccache lid; 471 char scratch[6+1]; /* 6 for the scratch part, +1 for NUL */ 472 krb5_error_code err; 473 krb5_mcc_data *d; 474 475 /* Allocate memory */ 476 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); 477 if (lid == NULL) 478 return KRB5_CC_NOMEM; 479 480 lid->ops = &krb5_mcc_ops; 481 482 (void) strcpy(scratch, "XXXXXX"); 483 mktemp(scratch); 484 485 err = k5_mutex_lock(&krb5int_mcc_mutex); 486 if (err) { 487 free(lid); 488 return err; 489 } 490 err = new_mcc_data(scratch, &d); 491 k5_mutex_unlock(&krb5int_mcc_mutex); 492 if (err) { 493 krb5_xfree(lid); 494 return err; 495 } 496 lid->data = d; 497 *id = lid; /* SUNW14resync - fix to 1.4.2 */ 498 krb5_change_cache (); 499 return KRB5_OK; 500 } 501 502 /* 503 * Requires: 504 * id is a file credential cache 505 * 506 * Returns: 507 * The name of the file cred cache id. 508 */ 509 const char * KRB5_CALLCONV 510 krb5_mcc_get_name (krb5_context context, krb5_ccache id) 511 { 512 return (char *) ((krb5_mcc_data *) id->data)->name; 513 } 514 515 /* 516 * Modifies: 517 * id, princ 518 * 519 * Effects: 520 * Retrieves the primary principal from id, as set with 521 * krb5_mcc_initialize. The principal is returned is allocated 522 * storage that must be freed by the caller via krb5_free_principal. 523 * 524 * Errors: 525 * system errors 526 * KRB5_CC_NOMEM 527 */ 528 krb5_error_code KRB5_CALLCONV 529 krb5_mcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ) 530 { 531 krb5_mcc_data *ptr = (krb5_mcc_data *)id->data; 532 if (!ptr->prin) { 533 *princ = 0L; 534 return KRB5_FCC_NOFILE; 535 } 536 return krb5_copy_principal(context, ptr->prin, princ); 537 } 538 539 krb5_error_code KRB5_CALLCONV 540 krb5_mcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, 541 krb5_creds *mcreds, krb5_creds *creds) 542 { 543 return krb5_cc_retrieve_cred_default (context, id, whichfields, 544 mcreds, creds); 545 } 546 547 /* 548 * Non-functional stub implementation for krb5_mcc_remove 549 * 550 * Errors: 551 * KRB5_CC_NOSUPP - not implemented 552 */ 553 static krb5_error_code KRB5_CALLCONV 554 krb5_mcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags, 555 krb5_creds *creds) 556 { 557 return KRB5_CC_NOSUPP; 558 } 559 560 561 /* 562 * Requires: 563 * id is a cred cache returned by krb5_mcc_resolve or 564 * krb5_mcc_generate_new, but has not been opened by krb5_mcc_initialize. 565 * 566 * Modifies: 567 * id 568 * 569 * Effects: 570 * Sets the operational flags of id to flags. 571 */ 572 krb5_error_code KRB5_CALLCONV 573 krb5_mcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags) 574 { 575 return KRB5_OK; 576 } 577 578 /* store: Save away creds in the ccache. */ 579 krb5_error_code KRB5_CALLCONV 580 krb5_mcc_store(krb5_context ctx, krb5_ccache id, krb5_creds *creds) 581 { 582 krb5_error_code err; 583 krb5_mcc_link *new_node; 584 krb5_mcc_data *mptr = (krb5_mcc_data *)id->data; 585 586 new_node = malloc(sizeof(krb5_mcc_link)); 587 if (new_node == NULL) 588 return errno; 589 err = krb5_copy_creds(ctx, creds, &new_node->creds); 590 if (err) { 591 free(new_node); 592 return err; 593 } 594 err = k5_mutex_lock(&mptr->lock); 595 if (err) { 596 /* SUNW14resync - fix mem leak */ 597 free(new_node); 598 return err; 599 } 600 new_node->next = mptr->link; 601 mptr->link = new_node; 602 k5_mutex_unlock(&mptr->lock); 603 return 0; 604 } 605 606 const krb5_cc_ops krb5_mcc_ops = { 607 0, 608 "MEMORY", 609 krb5_mcc_get_name, 610 krb5_mcc_resolve, 611 krb5_mcc_generate_new, 612 krb5_mcc_initialize, 613 krb5_mcc_destroy, 614 krb5_mcc_close, 615 krb5_mcc_store, 616 krb5_mcc_retrieve, 617 krb5_mcc_get_principal, 618 krb5_mcc_start_seq_get, 619 krb5_mcc_next_cred, 620 krb5_mcc_end_seq_get, 621 krb5_mcc_remove_cred, 622 krb5_mcc_set_flags, 623 }; 624