xref: /freebsd/crypto/krb5/src/lib/krb5/ccache/cc_memory.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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