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
krb5_mcc_initialize(krb5_context context,krb5_ccache id,krb5_principal princ)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
krb5_mcc_close(krb5_context context,krb5_ccache id)162 krb5_mcc_close(krb5_context context, krb5_ccache id)
163 {
164 krb5_xfree(id);
165 return KRB5_OK;
166 }
167
168 void
krb5_mcc_free(krb5_context context,krb5_ccache id)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
krb5_mcc_destroy(krb5_context context,krb5_ccache id)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
krb5_mcc_resolve(krb5_context context,krb5_ccache * id,const char * residual)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
krb5_mcc_start_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)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
krb5_mcc_next_cred(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)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
krb5_mcc_end_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)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
new_mcc_data(const char * name,krb5_mcc_data ** dataptr)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
krb5_mcc_generate_new(krb5_context context,krb5_ccache * id)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
random_string(krb5_context context,char * string,unsigned int length)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
krb5_mcc_get_name(krb5_context context,krb5_ccache id)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
krb5_mcc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * princ)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
krb5_mcc_retrieve(krb5_context context,krb5_ccache id,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds)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
krb5_mcc_remove_cred(krb5_context context,krb5_ccache cache,krb5_flags flags,krb5_creds * creds)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
krb5_mcc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)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
krb5_mcc_get_flags(krb5_context context,krb5_ccache id,krb5_flags * flags)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
krb5_mcc_store(krb5_context ctx,krb5_ccache id,krb5_creds * creds)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
krb5_mcc_ptcursor_new(krb5_context context,krb5_cc_ptcursor * cursor)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
krb5_mcc_ptcursor_next(krb5_context context,krb5_cc_ptcursor cursor,krb5_ccache * ccache)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
krb5_mcc_ptcursor_free(krb5_context context,krb5_cc_ptcursor * cursor)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