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