1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_memory.c - Memory-based credential cache */
3 /*
4 * Copyright 1990,1991,2000,2004,2008 by the Massachusetts Institute of
5 * Technology. All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 #include "cc-int.h"
28 #include "../krb/int-proto.h"
29 #include "k5-hashtab.h"
30 #include <errno.h>
31
32 static krb5_error_code KRB5_CALLCONV krb5_mcc_close
33 (krb5_context, krb5_ccache id );
34
35 static krb5_error_code KRB5_CALLCONV krb5_mcc_destroy
36 (krb5_context, krb5_ccache id );
37
38 static krb5_error_code KRB5_CALLCONV krb5_mcc_end_seq_get
39 (krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
40
41 static krb5_error_code KRB5_CALLCONV krb5_mcc_generate_new
42 (krb5_context, krb5_ccache *id );
43
44 static const char * KRB5_CALLCONV krb5_mcc_get_name
45 (krb5_context, krb5_ccache id );
46
47 static krb5_error_code KRB5_CALLCONV krb5_mcc_get_principal
48 (krb5_context, krb5_ccache id , krb5_principal *princ );
49
50 static krb5_error_code KRB5_CALLCONV krb5_mcc_initialize
51 (krb5_context, krb5_ccache id , krb5_principal princ );
52
53 static krb5_error_code KRB5_CALLCONV krb5_mcc_next_cred
54 (krb5_context,
55 krb5_ccache id ,
56 krb5_cc_cursor *cursor ,
57 krb5_creds *creds );
58
59 static krb5_error_code KRB5_CALLCONV krb5_mcc_resolve
60 (krb5_context, krb5_ccache *id , const char *residual );
61
62 static krb5_error_code KRB5_CALLCONV krb5_mcc_retrieve
63 (krb5_context,
64 krb5_ccache id ,
65 krb5_flags whichfields ,
66 krb5_creds *mcreds ,
67 krb5_creds *creds );
68
69 static krb5_error_code KRB5_CALLCONV krb5_mcc_start_seq_get
70 (krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
71
72 static krb5_error_code KRB5_CALLCONV krb5_mcc_store
73 (krb5_context, krb5_ccache id , krb5_creds *creds );
74
75 static krb5_error_code KRB5_CALLCONV krb5_mcc_set_flags
76 (krb5_context, krb5_ccache id , krb5_flags flags );
77
78 static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_new
79 (krb5_context, krb5_cc_ptcursor *);
80
81 static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_next
82 (krb5_context, krb5_cc_ptcursor, krb5_ccache *);
83
84 static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_free
85 (krb5_context, krb5_cc_ptcursor *);
86
87 static krb5_error_code KRB5_CALLCONV krb5_mcc_lock
88 (krb5_context context, krb5_ccache id);
89
90 static krb5_error_code KRB5_CALLCONV krb5_mcc_unlock
91 (krb5_context context, krb5_ccache id);
92
93
94 extern const krb5_cc_ops krb5_mcc_ops;
95 extern krb5_error_code krb5_change_cache (void);
96
97 #define KRB5_OK 0
98
99 /* Individual credentials within a cache, in a linked list. */
100 typedef struct _krb5_mcc_link {
101 struct _krb5_mcc_link *next;
102 krb5_creds *creds;
103 } krb5_mcc_link;
104
105 /* Per-cache data header. */
106 typedef struct _krb5_mcc_data {
107 char *name;
108 k5_cc_mutex lock;
109 krb5_principal prin;
110 krb5_mcc_link *link;
111 krb5_mcc_link **tail; /* Where to store next added cred */
112 /* Time offsets for clock-skewed clients. */
113 krb5_int32 time_offset;
114 krb5_int32 usec_offset;
115 int refcount; /* One for the table slot, one per handle */
116 int generation; /* Incremented at each initialize */
117 } krb5_mcc_data;
118
119 /* Iterator over credentials in a memory cache. */
120 struct mcc_cursor {
121 int generation;
122 krb5_mcc_link *next_link;
123 };
124
125 /* Iterator over memory caches. */
126 struct krb5_mcc_ptcursor_data {
127 krb5_boolean first;
128 };
129
130 k5_cc_mutex krb5int_mcc_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
131 static struct k5_hashtab *mcc_hashtab = NULL;
132
133 /* Ensure that mcc_hashtab is initialized. Call with krb5int_mcc_mutex
134 * locked. */
135 static krb5_error_code
init_table(krb5_context context)136 init_table(krb5_context context)
137 {
138 krb5_error_code ret;
139 uint8_t seed[K5_HASH_SEED_LEN];
140 krb5_data d = make_data(seed, sizeof(seed));
141
142 if (mcc_hashtab != NULL)
143 return 0;
144 ret = krb5_c_random_make_octets(context, &d);
145 if (ret)
146 return ret;
147 return k5_hashtab_create(seed, 64, &mcc_hashtab);
148 }
149
150 /* Remove creds from d, invalidate any existing cursors, and unset the client
151 * principal. The caller is responsible for locking. */
152 static void
empty_mcc_cache(krb5_context context,krb5_mcc_data * d)153 empty_mcc_cache(krb5_context context, krb5_mcc_data *d)
154 {
155 krb5_mcc_link *curr, *next;
156
157 for (curr = d->link; curr != NULL; curr = next) {
158 next = curr->next;
159 krb5_free_creds(context, curr->creds);
160 free(curr);
161 }
162 d->link = NULL;
163 d->tail = &d->link;
164 d->generation++;
165 krb5_free_principal(context, d->prin);
166 d->prin = NULL;
167 }
168
169 /* Remove all creds from d and initialize it with princ as the default client
170 * principal. The caller is responsible for locking. */
171 static krb5_error_code
init_mcc_cache(krb5_context context,krb5_mcc_data * d,krb5_principal princ)172 init_mcc_cache(krb5_context context, krb5_mcc_data *d, krb5_principal princ)
173 {
174 krb5_os_context os_ctx = &context->os_context;
175
176 empty_mcc_cache(context, d);
177 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
178 /* Store client time offsets in the cache. */
179 d->time_offset = os_ctx->time_offset;
180 d->usec_offset = os_ctx->usec_offset;
181 }
182 return krb5_copy_principal(context, princ, &d->prin);
183 }
184
185 /* Add cred to d. The caller is responsible for locking. */
186 static krb5_error_code
store_cred(krb5_context context,krb5_mcc_data * d,krb5_creds * cred)187 store_cred(krb5_context context, krb5_mcc_data *d, krb5_creds *cred)
188 {
189 krb5_error_code ret;
190 krb5_mcc_link *new_node;
191
192 new_node = malloc(sizeof(*new_node));
193 if (new_node == NULL)
194 return ENOMEM;
195 new_node->next = NULL;
196 ret = krb5_copy_creds(context, cred, &new_node->creds);
197 if (ret) {
198 free(new_node);
199 return ret;
200 }
201
202 /* Place the new node at the tail of the list. */
203 *d->tail = new_node;
204 d->tail = &new_node->next;
205 return 0;
206 }
207
208 /*
209 * Modifies:
210 * id
211 *
212 * Effects:
213 * Creates/refreshes the memory cred cache id. If the cache exists, its
214 * contents are destroyed.
215 *
216 * Errors:
217 * system errors
218 */
219 krb5_error_code KRB5_CALLCONV
krb5_mcc_initialize(krb5_context context,krb5_ccache id,krb5_principal princ)220 krb5_mcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
221 {
222 krb5_error_code ret;
223 krb5_mcc_data *d = id->data;
224
225 k5_cc_mutex_lock(context, &d->lock);
226 ret = init_mcc_cache(context, d, princ);
227 k5_cc_mutex_unlock(context, &d->lock);
228 if (ret == KRB5_OK)
229 krb5_change_cache();
230 return ret;
231 }
232
233 /*
234 * Modifies:
235 * id
236 *
237 * Effects:
238 * Invalidates the id, and frees any resources associated with accessing
239 * the cache.
240 */
241 krb5_error_code KRB5_CALLCONV
krb5_mcc_close(krb5_context context,krb5_ccache id)242 krb5_mcc_close(krb5_context context, krb5_ccache id)
243 {
244 krb5_mcc_data *d = id->data;
245 int count;
246
247 free(id);
248 k5_cc_mutex_lock(context, &d->lock);
249 count = --d->refcount;
250 k5_cc_mutex_unlock(context, &d->lock);
251 if (count == 0) {
252 /* This is the last active handle referencing d and d has been removed
253 * from the table, so we can release it. */
254 empty_mcc_cache(context, d);
255 free(d->name);
256 k5_cc_mutex_destroy(&d->lock);
257 free(d);
258 }
259 return KRB5_OK;
260 }
261
262 /*
263 * Effects:
264 * Destroys the contents of id. id is invalid after call.
265 */
266 krb5_error_code KRB5_CALLCONV
krb5_mcc_destroy(krb5_context context,krb5_ccache id)267 krb5_mcc_destroy(krb5_context context, krb5_ccache id)
268 {
269 krb5_mcc_data *d = id->data;
270 krb5_boolean removed_from_table = FALSE;
271
272 /* Remove this node from the table if it is still present. */
273 k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
274 if (k5_hashtab_remove(mcc_hashtab, d->name, strlen(d->name)))
275 removed_from_table = TRUE;
276 k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
277
278 /* Empty the cache and remove the reference for the table slot. There will
279 * always be at least one reference left for the handle being destroyed. */
280 k5_cc_mutex_lock(context, &d->lock);
281 empty_mcc_cache(context, d);
282 if (removed_from_table)
283 d->refcount--;
284 k5_cc_mutex_unlock(context, &d->lock);
285
286 /* Invalidate the handle, possibly removing the last reference to d and
287 * freeing it. */
288 krb5_mcc_close(context, id);
289
290 krb5_change_cache ();
291 return KRB5_OK;
292 }
293
294 /*
295 * Requires:
296 * residual is a legal path name, and a null-terminated string
297 *
298 * Modifies:
299 * id
300 *
301 * Effects:
302 * creates or accesses a memory-based cred cache that is referenced by
303 * residual.
304 *
305 * Returns:
306 * A filled in krb5_ccache structure "id".
307 *
308 * Errors:
309 * KRB5_CC_NOMEM - there was insufficient memory to allocate the
310 * krb5_ccache. id is undefined.
311 * system errors (mutex locks related)
312 */
313 static krb5_error_code new_mcc_data (const char *, krb5_mcc_data **);
314
315 krb5_error_code KRB5_CALLCONV
krb5_mcc_resolve(krb5_context context,krb5_ccache * id,const char * residual)316 krb5_mcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
317 {
318 krb5_os_context os_ctx = &context->os_context;
319 krb5_ccache lid;
320 krb5_error_code err;
321 krb5_mcc_data *d;
322
323 k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
324 init_table(context);
325 d = k5_hashtab_get(mcc_hashtab, residual, strlen(residual));
326 if (d != NULL) {
327 k5_cc_mutex_lock(context, &d->lock);
328 d->refcount++;
329 k5_cc_mutex_unlock(context, &d->lock);
330 } else {
331 err = new_mcc_data(residual, &d);
332 if (err) {
333 k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
334 return err;
335 }
336 }
337 k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
338
339 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
340 if (lid == NULL)
341 return KRB5_CC_NOMEM;
342
343 if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) &&
344 !(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
345 /* Use the time offset from the cache entry */
346 os_ctx->time_offset = d->time_offset;
347 os_ctx->usec_offset = d->usec_offset;
348 os_ctx->os_flags = ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
349 KRB5_OS_TOFFSET_VALID);
350 }
351
352 lid->ops = &krb5_mcc_ops;
353 lid->data = d;
354 *id = lid;
355 return KRB5_OK;
356 }
357
358 /*
359 * Effects:
360 * Prepares for a sequential search of the credentials cache.
361 * Returns a krb5_cc_cursor to be used with krb5_mcc_next_cred and
362 * krb5_mcc_end_seq_get.
363 *
364 * If the cache is modified between the time of this call and the time
365 * of the final krb5_mcc_end_seq_get, the results are undefined.
366 *
367 * Errors:
368 * KRB5_CC_NOMEM
369 * system errors
370 */
371 krb5_error_code KRB5_CALLCONV
krb5_mcc_start_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)372 krb5_mcc_start_seq_get(krb5_context context, krb5_ccache id,
373 krb5_cc_cursor *cursor)
374 {
375 struct mcc_cursor *mcursor;
376 krb5_mcc_data *d;
377
378 mcursor = malloc(sizeof(*mcursor));
379 if (mcursor == NULL)
380 return KRB5_CC_NOMEM;
381 d = id->data;
382 k5_cc_mutex_lock(context, &d->lock);
383 mcursor->generation = d->generation;
384 mcursor->next_link = d->link;
385 k5_cc_mutex_unlock(context, &d->lock);
386 *cursor = mcursor;
387 return KRB5_OK;
388 }
389
390 /*
391 * Requires:
392 * cursor is a krb5_cc_cursor originally obtained from
393 * krb5_mcc_start_seq_get.
394 *
395 * Modifies:
396 * cursor, creds
397 *
398 * Effects:
399 * Fills in creds with the "next" credentals structure from the cache
400 * id. The actual order the creds are returned in is arbitrary.
401 * Space is allocated for the variable length fields in the
402 * credentials structure, so the object returned must be passed to
403 * krb5_destroy_credential.
404 *
405 * The cursor is updated for the next call to krb5_mcc_next_cred.
406 *
407 * Errors:
408 * system errors
409 */
410 krb5_error_code KRB5_CALLCONV
krb5_mcc_next_cred(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)411 krb5_mcc_next_cred(krb5_context context, krb5_ccache id,
412 krb5_cc_cursor *cursor, krb5_creds *creds)
413 {
414 struct mcc_cursor *mcursor;
415 krb5_error_code retval;
416 krb5_mcc_data *d = id->data;
417
418 memset(creds, 0, sizeof(krb5_creds));
419 mcursor = *cursor;
420 if (mcursor->next_link == NULL)
421 return KRB5_CC_END;
422
423 /*
424 * Check the cursor generation against the cache generation in case the
425 * cache has been reinitialized or destroyed, freeing the pointer in the
426 * cursor. Keep the cache locked while we copy the creds and advance the
427 * pointer, in case another thread reinitializes the cache after we check
428 * the generation.
429 */
430 k5_cc_mutex_lock(context, &d->lock);
431 if (mcursor->generation != d->generation) {
432 retval = KRB5_CC_END;
433 goto done;
434 }
435
436 /* Skip over removed creds. */
437 while (mcursor->next_link != NULL && mcursor->next_link->creds == NULL)
438 mcursor->next_link = mcursor->next_link->next;
439 if (mcursor->next_link == NULL) {
440 retval = KRB5_CC_END;
441 goto done;
442 }
443
444 retval = k5_copy_creds_contents(context, mcursor->next_link->creds, creds);
445 if (retval == 0)
446 mcursor->next_link = mcursor->next_link->next;
447
448 done:
449 k5_cc_mutex_unlock(context, &d->lock);
450 return retval;
451 }
452
453 /*
454 * Requires:
455 * cursor is a krb5_cc_cursor originally obtained from
456 * krb5_mcc_start_seq_get.
457 *
458 * Modifies:
459 * id, cursor
460 *
461 * Effects:
462 * Finishes sequential processing of the memory credentials ccache id,
463 * and invalidates the cursor (it must never be used after this call).
464 */
465 /* ARGSUSED */
466 krb5_error_code KRB5_CALLCONV
krb5_mcc_end_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)467 krb5_mcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
468 {
469 free(*cursor);
470 *cursor = NULL;
471 return KRB5_OK;
472 }
473
474 /*
475 * Utility routine: Creates the back-end data for a memory cache, and adds it
476 * to the global table. Give the new object two references, one for the table
477 * slot and one for the caller's handle.
478 *
479 * Call with the global table lock held.
480 */
481 static krb5_error_code
new_mcc_data(const char * name,krb5_mcc_data ** dataptr)482 new_mcc_data (const char *name, krb5_mcc_data **dataptr)
483 {
484 krb5_error_code err;
485 krb5_mcc_data *d;
486
487 d = malloc(sizeof(krb5_mcc_data));
488 if (d == NULL)
489 return KRB5_CC_NOMEM;
490
491 err = k5_cc_mutex_init(&d->lock);
492 if (err) {
493 free(d);
494 return err;
495 }
496
497 d->name = strdup(name);
498 if (d->name == NULL) {
499 k5_cc_mutex_destroy(&d->lock);
500 free(d);
501 return KRB5_CC_NOMEM;
502 }
503 d->link = NULL;
504 d->tail = &d->link;
505 d->prin = NULL;
506 d->time_offset = 0;
507 d->usec_offset = 0;
508 d->refcount = 2;
509 d->generation = 0;
510
511 if (k5_hashtab_add(mcc_hashtab, d->name, strlen(d->name), d) != 0) {
512 free(d->name);
513 k5_cc_mutex_destroy(&d->lock);
514 free(d);
515 return KRB5_CC_NOMEM;
516 }
517
518 *dataptr = d;
519 return 0;
520 }
521
522 /*
523 * Effects:
524 * Creates a new memory cred cache whose name is guaranteed to be
525 * unique. The name begins with the string TKT_ROOT (from mcc.h).
526 *
527 * Returns:
528 * The filled in krb5_ccache id.
529 *
530 * Errors:
531 * KRB5_CC_NOMEM - there was insufficient memory to allocate the
532 * krb5_ccache. id is undefined.
533 * system errors (from open, mutex locking)
534 */
535
536 krb5_error_code KRB5_CALLCONV
krb5_mcc_generate_new(krb5_context context,krb5_ccache * id)537 krb5_mcc_generate_new (krb5_context context, krb5_ccache *id)
538 {
539 krb5_ccache lid;
540 char uniquename[8];
541 krb5_error_code err;
542 krb5_mcc_data *d;
543
544 /* Allocate memory */
545 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
546 if (lid == NULL)
547 return KRB5_CC_NOMEM;
548
549 lid->ops = &krb5_mcc_ops;
550
551 k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
552 init_table(context);
553
554 /* Check for uniqueness with mutex locked to avoid race conditions */
555 while (1) {
556 err = krb5int_random_string (context, uniquename, sizeof (uniquename));
557 if (err) {
558 k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
559 free(lid);
560 return err;
561 }
562
563 if (k5_hashtab_get(mcc_hashtab, uniquename,
564 strlen(uniquename)) == NULL)
565 break;
566 }
567
568 err = new_mcc_data(uniquename, &d);
569
570 k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
571 if (err) {
572 free(lid);
573 return err;
574 }
575 lid->data = d;
576 *id = lid;
577 krb5_change_cache ();
578 return KRB5_OK;
579 }
580
581 /*
582 * Requires:
583 * id is a file credential cache
584 *
585 * Returns:
586 * A pointer to the name of the file cred cache id.
587 */
588 const char * KRB5_CALLCONV
krb5_mcc_get_name(krb5_context context,krb5_ccache id)589 krb5_mcc_get_name (krb5_context context, krb5_ccache id)
590 {
591 return (char *) ((krb5_mcc_data *) id->data)->name;
592 }
593
594 /*
595 * Modifies:
596 * id, princ
597 *
598 * Effects:
599 * Retrieves the primary principal from id, as set with
600 * krb5_mcc_initialize. The principal is returned is allocated
601 * storage that must be freed by the caller via krb5_free_principal.
602 *
603 * Errors:
604 * system errors
605 * ENOMEM
606 */
607 krb5_error_code KRB5_CALLCONV
krb5_mcc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * princ)608 krb5_mcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
609 {
610 krb5_error_code ret;
611 krb5_mcc_data *d = id->data;
612
613 *princ = NULL;
614 k5_cc_mutex_lock(context, &d->lock);
615 if (d->prin == NULL)
616 ret = KRB5_FCC_NOFILE;
617 else
618 ret = krb5_copy_principal(context, d->prin, princ);
619 k5_cc_mutex_unlock(context, &d->lock);
620 return ret;
621 }
622
623 krb5_error_code KRB5_CALLCONV
krb5_mcc_retrieve(krb5_context context,krb5_ccache id,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds)624 krb5_mcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields,
625 krb5_creds *mcreds, krb5_creds *creds)
626 {
627 return k5_cc_retrieve_cred_default(context, id, whichfields, mcreds,
628 creds);
629 }
630
631 /*
632 * Modifies:
633 * the memory cache
634 *
635 * Effects:
636 * Remove the given creds from the ccache.
637 */
638 static krb5_error_code KRB5_CALLCONV
krb5_mcc_remove_cred(krb5_context context,krb5_ccache cache,krb5_flags flags,krb5_creds * creds)639 krb5_mcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
640 krb5_creds *creds)
641 {
642 krb5_mcc_data *data = (krb5_mcc_data *)cache->data;
643 krb5_mcc_link *l;
644
645 k5_cc_mutex_lock(context, &data->lock);
646
647 for (l = data->link; l != NULL; l = l->next) {
648 if (l->creds != NULL &&
649 krb5int_cc_creds_match_request(context, flags, creds, l->creds)) {
650 krb5_free_creds(context, l->creds);
651 l->creds = NULL;
652 }
653 }
654
655 k5_cc_mutex_unlock(context, &data->lock);
656 return 0;
657 }
658
659
660 /*
661 * Requires:
662 * id is a cred cache returned by krb5_mcc_resolve or
663 * krb5_mcc_generate_new.
664 *
665 * Modifies:
666 * id
667 *
668 * Effects:
669 * Sets the operational flags of id to flags.
670 */
671 krb5_error_code KRB5_CALLCONV
krb5_mcc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)672 krb5_mcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
673 {
674 return KRB5_OK;
675 }
676
677 static krb5_error_code KRB5_CALLCONV
krb5_mcc_get_flags(krb5_context context,krb5_ccache id,krb5_flags * flags)678 krb5_mcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
679 {
680 *flags = 0;
681 return KRB5_OK;
682 }
683
684 /*
685 * Modifies:
686 * the memory cache
687 *
688 * Effects:
689 * Save away creds in the ccache.
690 *
691 * Errors:
692 * ENOMEM
693 */
694 krb5_error_code KRB5_CALLCONV
krb5_mcc_store(krb5_context context,krb5_ccache id,krb5_creds * creds)695 krb5_mcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
696 {
697 krb5_error_code ret;
698 krb5_mcc_data *d = id->data;
699
700 /* Place the new node at the tail of the list. */
701 k5_cc_mutex_lock(context, &d->lock);
702 ret = store_cred(context, d, creds);
703 k5_cc_mutex_unlock(context, &d->lock);
704 return ret;
705 }
706
707 static krb5_error_code KRB5_CALLCONV
krb5_mcc_ptcursor_new(krb5_context context,krb5_cc_ptcursor * cursor)708 krb5_mcc_ptcursor_new(
709 krb5_context context,
710 krb5_cc_ptcursor *cursor)
711 {
712 krb5_cc_ptcursor n = NULL;
713 struct krb5_mcc_ptcursor_data *cdata = NULL;
714
715 *cursor = NULL;
716
717 n = malloc(sizeof(*n));
718 if (n == NULL)
719 return ENOMEM;
720 n->ops = &krb5_mcc_ops;
721 cdata = malloc(sizeof(struct krb5_mcc_ptcursor_data));
722 if (cdata == NULL) {
723 free(n);
724 return ENOMEM;
725 }
726 n->data = cdata;
727 cdata->first = TRUE;
728 *cursor = n;
729 return 0;
730 }
731
732 static krb5_error_code KRB5_CALLCONV
krb5_mcc_ptcursor_next(krb5_context context,krb5_cc_ptcursor cursor,krb5_ccache * ccache)733 krb5_mcc_ptcursor_next(
734 krb5_context context,
735 krb5_cc_ptcursor cursor,
736 krb5_ccache *ccache)
737 {
738 struct krb5_mcc_ptcursor_data *cdata = NULL;
739 const char *defname;
740
741 *ccache = NULL;
742 cdata = cursor->data;
743 if (!cdata->first)
744 return 0;
745 cdata->first = FALSE;
746
747 defname = krb5_cc_default_name(context);
748 if (defname == NULL || strncmp(defname, "MEMORY:", 7) != 0)
749 return 0;
750
751 return krb5_cc_resolve(context, defname, ccache);
752 }
753
754 static krb5_error_code KRB5_CALLCONV
krb5_mcc_ptcursor_free(krb5_context context,krb5_cc_ptcursor * cursor)755 krb5_mcc_ptcursor_free(
756 krb5_context context,
757 krb5_cc_ptcursor *cursor)
758 {
759 if (*cursor == NULL)
760 return 0;
761 if ((*cursor)->data != NULL)
762 free((*cursor)->data);
763 free(*cursor);
764 *cursor = NULL;
765 return 0;
766 }
767
768 static krb5_error_code KRB5_CALLCONV
krb5_mcc_replace(krb5_context context,krb5_ccache id,krb5_principal princ,krb5_creds ** creds)769 krb5_mcc_replace(krb5_context context, krb5_ccache id, krb5_principal princ,
770 krb5_creds **creds)
771 {
772 krb5_error_code ret;
773 krb5_mcc_data *d = id->data;
774 int i;
775
776 k5_cc_mutex_lock(context, &d->lock);
777 ret = init_mcc_cache(context, d, princ);
778 for (i = 0; !ret && creds[i] != NULL; i++)
779 ret = store_cred(context, d, creds[i]);
780 k5_cc_mutex_unlock(context, &d->lock);
781 if (!ret)
782 krb5_change_cache();
783 return ret;
784 }
785
786 static krb5_error_code KRB5_CALLCONV
krb5_mcc_lock(krb5_context context,krb5_ccache id)787 krb5_mcc_lock(krb5_context context, krb5_ccache id)
788 {
789 krb5_mcc_data *data = (krb5_mcc_data *) id->data;
790
791 k5_cc_mutex_lock(context, &data->lock);
792 return 0;
793 }
794
795 static krb5_error_code KRB5_CALLCONV
krb5_mcc_unlock(krb5_context context,krb5_ccache id)796 krb5_mcc_unlock(krb5_context context, krb5_ccache id)
797 {
798 krb5_mcc_data *data = (krb5_mcc_data *) id->data;
799
800 k5_cc_mutex_unlock(context, &data->lock);
801 return 0;
802 }
803
804 const krb5_cc_ops krb5_mcc_ops = {
805 0,
806 "MEMORY",
807 krb5_mcc_get_name,
808 krb5_mcc_resolve,
809 krb5_mcc_generate_new,
810 krb5_mcc_initialize,
811 krb5_mcc_destroy,
812 krb5_mcc_close,
813 krb5_mcc_store,
814 krb5_mcc_retrieve,
815 krb5_mcc_get_principal,
816 krb5_mcc_start_seq_get,
817 krb5_mcc_next_cred,
818 krb5_mcc_end_seq_get,
819 krb5_mcc_remove_cred,
820 krb5_mcc_set_flags,
821 krb5_mcc_get_flags,
822 krb5_mcc_ptcursor_new,
823 krb5_mcc_ptcursor_next,
824 krb5_mcc_ptcursor_free,
825 krb5_mcc_replace,
826 NULL, /* wasdefault */
827 krb5_mcc_lock,
828 krb5_mcc_unlock,
829 NULL, /* switch_to */
830 };
831