xref: /freebsd/crypto/heimdal/lib/krb5/cache.c (revision f7c4bd95ba735bd6a5454b4953945a99cefbb80c)
1 /*
2  * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "krb5_locl.h"
35 
36 RCSID("$Id: cache.c 22127 2007-12-04 00:54:37Z lha $");
37 
38 /**
39  * Add a new ccache type with operations `ops', overwriting any
40  * existing one if `override'.
41  *
42  * @param context a Keberos context
43  * @param ops type of plugin symbol
44  * @param override flag to select if the registration is to overide
45  * an existing ops with the same name.
46  *
47  * @return Return an error code or 0.
48  *
49  * @ingroup krb5_ccache
50  */
51 
52 krb5_error_code KRB5_LIB_FUNCTION
53 krb5_cc_register(krb5_context context,
54 		 const krb5_cc_ops *ops,
55 		 krb5_boolean override)
56 {
57     int i;
58 
59     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
60 	if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) {
61 	    if(!override) {
62 		krb5_set_error_string(context,
63 				      "ccache type %s already exists",
64 				      ops->prefix);
65 		return KRB5_CC_TYPE_EXISTS;
66 	    }
67 	    break;
68 	}
69     }
70     if(i == context->num_cc_ops) {
71 	krb5_cc_ops *o = realloc(context->cc_ops,
72 				 (context->num_cc_ops + 1) *
73 				 sizeof(*context->cc_ops));
74 	if(o == NULL) {
75 	    krb5_set_error_string(context, "malloc: out of memory");
76 	    return KRB5_CC_NOMEM;
77 	}
78 	context->num_cc_ops++;
79 	context->cc_ops = o;
80 	memset(context->cc_ops + i, 0,
81 	       (context->num_cc_ops - i) * sizeof(*context->cc_ops));
82     }
83     memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
84     return 0;
85 }
86 
87 /*
88  * Allocate the memory for a `id' and the that function table to
89  * `ops'. Returns 0 or and error code.
90  */
91 
92 krb5_error_code
93 _krb5_cc_allocate(krb5_context context,
94 		  const krb5_cc_ops *ops,
95 		  krb5_ccache *id)
96 {
97     krb5_ccache p;
98 
99     p = malloc (sizeof(*p));
100     if(p == NULL) {
101 	krb5_set_error_string(context, "malloc: out of memory");
102 	return KRB5_CC_NOMEM;
103     }
104     p->ops = ops;
105     *id = p;
106 
107     return 0;
108 }
109 
110 /*
111  * Allocate memory for a new ccache in `id' with operations `ops'
112  * and name `residual'. Return 0 or an error code.
113  */
114 
115 static krb5_error_code
116 allocate_ccache (krb5_context context,
117 		 const krb5_cc_ops *ops,
118 		 const char *residual,
119 		 krb5_ccache *id)
120 {
121     krb5_error_code ret;
122 
123     ret = _krb5_cc_allocate(context, ops, id);
124     if (ret)
125 	return ret;
126     ret = (*id)->ops->resolve(context, id, residual);
127     if(ret)
128 	free(*id);
129     return ret;
130 }
131 
132 /**
133  * Find and allocate a ccache in `id' from the specification in `residual'.
134  * If the ccache name doesn't contain any colon, interpret it as a file name.
135  *
136  * @param context a Keberos context.
137  * @param name string name of a credential cache.
138  * @param id return pointer to a found credential cache.
139  *
140  * @return Return 0 or an error code. In case of an error, id is set
141  * to NULL.
142  *
143  * @ingroup krb5_ccache
144  */
145 
146 
147 krb5_error_code KRB5_LIB_FUNCTION
148 krb5_cc_resolve(krb5_context context,
149 		const char *name,
150 		krb5_ccache *id)
151 {
152     int i;
153 
154     *id = NULL;
155 
156     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
157 	size_t prefix_len = strlen(context->cc_ops[i].prefix);
158 
159 	if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
160 	   && name[prefix_len] == ':') {
161 	    return allocate_ccache (context, &context->cc_ops[i],
162 				    name + prefix_len + 1,
163 				    id);
164 	}
165     }
166     if (strchr (name, ':') == NULL)
167 	return allocate_ccache (context, &krb5_fcc_ops, name, id);
168     else {
169 	krb5_set_error_string(context, "unknown ccache type %s", name);
170 	return KRB5_CC_UNKNOWN_TYPE;
171     }
172 }
173 
174 /**
175  * Generate a new ccache of type `ops' in `id'.
176  *
177  * @return Return 0 or an error code.
178  *
179  * @ingroup krb5_ccache
180  */
181 
182 
183 krb5_error_code KRB5_LIB_FUNCTION
184 krb5_cc_gen_new(krb5_context context,
185 		const krb5_cc_ops *ops,
186 		krb5_ccache *id)
187 {
188     return krb5_cc_new_unique(context, ops->prefix, NULL, id);
189 }
190 
191 /**
192  * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
193  * the library chooses the default credential cache type. The supplied
194  * `hint' (that can be NULL) is a string that the credential cache
195  * type can use to base the name of the credential on, this is to make
196  * it easier for the user to differentiate the credentials.
197  *
198  * @return Returns 0 or an error code.
199  *
200  * @ingroup krb5_ccache
201  */
202 
203 krb5_error_code KRB5_LIB_FUNCTION
204 krb5_cc_new_unique(krb5_context context, const char *type,
205 		   const char *hint, krb5_ccache *id)
206 {
207     const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
208     krb5_error_code ret;
209 
210     if (type) {
211 	ops = krb5_cc_get_prefix_ops(context, type);
212 	if (ops == NULL) {
213 	    krb5_set_error_string(context,
214 				  "Credential cache type %s is unknown", type);
215 	    return KRB5_CC_UNKNOWN_TYPE;
216 	}
217     }
218 
219     ret = _krb5_cc_allocate(context, ops, id);
220     if (ret)
221 	return ret;
222     return (*id)->ops->gen_new(context, id);
223 }
224 
225 /**
226  * Return the name of the ccache `id'
227  *
228  * @ingroup krb5_ccache
229  */
230 
231 
232 const char* KRB5_LIB_FUNCTION
233 krb5_cc_get_name(krb5_context context,
234 		 krb5_ccache id)
235 {
236     return id->ops->get_name(context, id);
237 }
238 
239 /**
240  * Return the type of the ccache `id'.
241  *
242  * @ingroup krb5_ccache
243  */
244 
245 
246 const char* KRB5_LIB_FUNCTION
247 krb5_cc_get_type(krb5_context context,
248 		 krb5_ccache id)
249 {
250     return id->ops->prefix;
251 }
252 
253 /**
254  * Return the complete resolvable name the ccache `id' in `str�.
255  * `str` should be freed with free(3).
256  * Returns 0 or an error (and then *str is set to NULL).
257  *
258  * @ingroup krb5_ccache
259  */
260 
261 
262 krb5_error_code KRB5_LIB_FUNCTION
263 krb5_cc_get_full_name(krb5_context context,
264 		      krb5_ccache id,
265 		      char **str)
266 {
267     const char *type, *name;
268 
269     *str = NULL;
270 
271     type = krb5_cc_get_type(context, id);
272     if (type == NULL) {
273 	krb5_set_error_string(context, "cache have no name of type");
274 	return KRB5_CC_UNKNOWN_TYPE;
275     }
276 
277     name = krb5_cc_get_name(context, id);
278     if (name == NULL) {
279 	krb5_set_error_string(context, "cache of type %s have no name", type);
280 	return KRB5_CC_BADNAME;
281     }
282 
283     if (asprintf(str, "%s:%s", type, name) == -1) {
284 	krb5_set_error_string(context, "malloc - out of memory");
285 	*str = NULL;
286 	return ENOMEM;
287     }
288     return 0;
289 }
290 
291 /**
292  * Return krb5_cc_ops of a the ccache `id'.
293  *
294  * @ingroup krb5_ccache
295  */
296 
297 
298 const krb5_cc_ops *
299 krb5_cc_get_ops(krb5_context context, krb5_ccache id)
300 {
301     return id->ops;
302 }
303 
304 /*
305  * Expand variables in `str' into `res'
306  */
307 
308 krb5_error_code
309 _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
310 {
311     size_t tlen, len = 0;
312     char *tmp, *tmp2, *append;
313 
314     *res = NULL;
315 
316     while (str && *str) {
317 	tmp = strstr(str, "%{");
318 	if (tmp && tmp != str) {
319 	    append = malloc((tmp - str) + 1);
320 	    if (append) {
321 		memcpy(append, str, tmp - str);
322 		append[tmp - str] = '\0';
323 	    }
324 	    str = tmp;
325 	} else if (tmp) {
326 	    tmp2 = strchr(tmp, '}');
327 	    if (tmp2 == NULL) {
328 		free(*res);
329 		*res = NULL;
330 		krb5_set_error_string(context, "variable missing }");
331 		return KRB5_CONFIG_BADFORMAT;
332 	    }
333 	    if (strncasecmp(tmp, "%{uid}", 6) == 0)
334 		asprintf(&append, "%u", (unsigned)getuid());
335 	    else if (strncasecmp(tmp, "%{null}", 7) == 0)
336 		append = strdup("");
337 	    else {
338 		free(*res);
339 		*res = NULL;
340 		krb5_set_error_string(context,
341 				      "expand default cache unknown "
342 				      "variable \"%.*s\"",
343 				      (int)(tmp2 - tmp) - 2, tmp + 2);
344 		return KRB5_CONFIG_BADFORMAT;
345 	    }
346 	    str = tmp2 + 1;
347 	} else {
348 	    append = strdup(str);
349 	    str = NULL;
350 	}
351 	if (append == NULL) {
352 	    free(*res);
353 	    *res = NULL;
354 	    krb5_set_error_string(context, "malloc - out of memory");
355 	    return ENOMEM;
356 	}
357 
358 	tlen = strlen(append);
359 	tmp = realloc(*res, len + tlen + 1);
360 	if (tmp == NULL) {
361 	    free(append);
362 	    free(*res);
363 	    *res = NULL;
364 	    krb5_set_error_string(context, "malloc - out of memory");
365 	    return ENOMEM;
366 	}
367 	*res = tmp;
368 	memcpy(*res + len, append, tlen + 1);
369 	len = len + tlen;
370 	free(append);
371     }
372     return 0;
373 }
374 
375 /*
376  * Return non-zero if envirnoment that will determine default krb5cc
377  * name has changed.
378  */
379 
380 static int
381 environment_changed(krb5_context context)
382 {
383     const char *e;
384 
385     /* if the cc name was set, don't change it */
386     if (context->default_cc_name_set)
387 	return 0;
388 
389     if(issuid())
390 	return 0;
391 
392     e = getenv("KRB5CCNAME");
393     if (e == NULL) {
394 	if (context->default_cc_name_env) {
395 	    free(context->default_cc_name_env);
396 	    context->default_cc_name_env = NULL;
397 	    return 1;
398 	}
399     } else {
400 	if (context->default_cc_name_env == NULL)
401 	    return 1;
402 	if (strcmp(e, context->default_cc_name_env) != 0)
403 	    return 1;
404     }
405     return 0;
406 }
407 
408 /**
409  * Set the default cc name for `context' to `name'.
410  *
411  * @ingroup krb5_ccache
412  */
413 
414 
415 krb5_error_code KRB5_LIB_FUNCTION
416 krb5_cc_set_default_name(krb5_context context, const char *name)
417 {
418     krb5_error_code ret = 0;
419     char *p;
420 
421     if (name == NULL) {
422 	const char *e = NULL;
423 
424 	if(!issuid()) {
425 	    e = getenv("KRB5CCNAME");
426 	    if (e) {
427 		p = strdup(e);
428 		if (context->default_cc_name_env)
429 		    free(context->default_cc_name_env);
430 		context->default_cc_name_env = strdup(e);
431 	    }
432 	}
433 	if (e == NULL) {
434 	    e = krb5_config_get_string(context, NULL, "libdefaults",
435 				       "default_cc_name", NULL);
436 	    if (e) {
437 		ret = _krb5_expand_default_cc_name(context, e, &p);
438 		if (ret)
439 		    return ret;
440 	    }
441 	    if (e == NULL) {
442 		const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
443 		ret = (*ops->default_name)(context, &p);
444 		if (ret)
445 		    return ret;
446 	    }
447 	}
448 	context->default_cc_name_set = 0;
449     } else {
450 	p = strdup(name);
451 	context->default_cc_name_set = 1;
452     }
453 
454     if (p == NULL) {
455 	krb5_set_error_string(context, "malloc - out of memory");
456 	return ENOMEM;
457     }
458 
459     if (context->default_cc_name)
460 	free(context->default_cc_name);
461 
462     context->default_cc_name = p;
463 
464     return ret;
465 }
466 
467 /**
468  * Return a pointer to a context static string containing the default
469  * ccache name.
470  *
471  * @return String to the default credential cache name.
472  *
473  * @ingroup krb5_ccache
474  */
475 
476 
477 const char* KRB5_LIB_FUNCTION
478 krb5_cc_default_name(krb5_context context)
479 {
480     if (context->default_cc_name == NULL || environment_changed(context))
481 	krb5_cc_set_default_name(context, NULL);
482 
483     return context->default_cc_name;
484 }
485 
486 /**
487  * Open the default ccache in `id'.
488  *
489  * @return Return 0 or an error code.
490  *
491  * @ingroup krb5_ccache
492  */
493 
494 
495 krb5_error_code KRB5_LIB_FUNCTION
496 krb5_cc_default(krb5_context context,
497 		krb5_ccache *id)
498 {
499     const char *p = krb5_cc_default_name(context);
500 
501     if (p == NULL) {
502 	krb5_set_error_string(context, "malloc - out of memory");
503 	return ENOMEM;
504     }
505     return krb5_cc_resolve(context, p, id);
506 }
507 
508 /**
509  * Create a new ccache in `id' for `primary_principal'.
510  *
511  * @return Return 0 or an error code.
512  *
513  * @ingroup krb5_ccache
514  */
515 
516 
517 krb5_error_code KRB5_LIB_FUNCTION
518 krb5_cc_initialize(krb5_context context,
519 		   krb5_ccache id,
520 		   krb5_principal primary_principal)
521 {
522     return (*id->ops->init)(context, id, primary_principal);
523 }
524 
525 
526 /**
527  * Remove the ccache `id'.
528  *
529  * @return Return 0 or an error code.
530  *
531  * @ingroup krb5_ccache
532  */
533 
534 
535 krb5_error_code KRB5_LIB_FUNCTION
536 krb5_cc_destroy(krb5_context context,
537 		krb5_ccache id)
538 {
539     krb5_error_code ret;
540 
541     ret = (*id->ops->destroy)(context, id);
542     krb5_cc_close (context, id);
543     return ret;
544 }
545 
546 /**
547  * Stop using the ccache `id' and free the related resources.
548  *
549  * @return Return 0 or an error code.
550  *
551  * @ingroup krb5_ccache
552  */
553 
554 
555 krb5_error_code KRB5_LIB_FUNCTION
556 krb5_cc_close(krb5_context context,
557 	      krb5_ccache id)
558 {
559     krb5_error_code ret;
560     ret = (*id->ops->close)(context, id);
561     free(id);
562     return ret;
563 }
564 
565 /**
566  * Store `creds' in the ccache `id'.
567  *
568  * @return Return 0 or an error code.
569  *
570  * @ingroup krb5_ccache
571  */
572 
573 
574 krb5_error_code KRB5_LIB_FUNCTION
575 krb5_cc_store_cred(krb5_context context,
576 		   krb5_ccache id,
577 		   krb5_creds *creds)
578 {
579     return (*id->ops->store)(context, id, creds);
580 }
581 
582 /**
583  * Retrieve the credential identified by `mcreds' (and `whichfields')
584  * from `id' in `creds'. 'creds' must be free by the caller using
585  * krb5_free_cred_contents.
586  *
587  * @return Return 0 or an error code.
588  *
589  * @ingroup krb5_ccache
590  */
591 
592 
593 krb5_error_code KRB5_LIB_FUNCTION
594 krb5_cc_retrieve_cred(krb5_context context,
595 		      krb5_ccache id,
596 		      krb5_flags whichfields,
597 		      const krb5_creds *mcreds,
598 		      krb5_creds *creds)
599 {
600     krb5_error_code ret;
601     krb5_cc_cursor cursor;
602 
603     if (id->ops->retrieve != NULL) {
604 	return (*id->ops->retrieve)(context, id, whichfields,
605 				    mcreds, creds);
606     }
607 
608     ret = krb5_cc_start_seq_get(context, id, &cursor);
609     if (ret)
610 	return ret;
611     while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
612 	if(krb5_compare_creds(context, whichfields, mcreds, creds)){
613 	    ret = 0;
614 	    break;
615 	}
616 	krb5_free_cred_contents (context, creds);
617     }
618     krb5_cc_end_seq_get(context, id, &cursor);
619     return ret;
620 }
621 
622 /**
623  * Return the principal of `id' in `principal'.
624  *
625  * @return Return 0 or an error code.
626  *
627  * @ingroup krb5_ccache
628  */
629 
630 
631 krb5_error_code KRB5_LIB_FUNCTION
632 krb5_cc_get_principal(krb5_context context,
633 		      krb5_ccache id,
634 		      krb5_principal *principal)
635 {
636     return (*id->ops->get_princ)(context, id, principal);
637 }
638 
639 /**
640  * Start iterating over `id', `cursor' is initialized to the
641  * beginning.
642  *
643  * @return Return 0 or an error code.
644  *
645  * @ingroup krb5_ccache
646  */
647 
648 
649 krb5_error_code KRB5_LIB_FUNCTION
650 krb5_cc_start_seq_get (krb5_context context,
651 		       const krb5_ccache id,
652 		       krb5_cc_cursor *cursor)
653 {
654     return (*id->ops->get_first)(context, id, cursor);
655 }
656 
657 /**
658  * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
659  * and advance `cursor'.
660  *
661  * @return Return 0 or an error code.
662  *
663  * @ingroup krb5_ccache
664  */
665 
666 
667 krb5_error_code KRB5_LIB_FUNCTION
668 krb5_cc_next_cred (krb5_context context,
669 		   const krb5_ccache id,
670 		   krb5_cc_cursor *cursor,
671 		   krb5_creds *creds)
672 {
673     return (*id->ops->get_next)(context, id, cursor, creds);
674 }
675 
676 /**
677  * Like krb5_cc_next_cred, but allow for selective retrieval
678  *
679  * @ingroup krb5_ccache
680  */
681 
682 
683 krb5_error_code KRB5_LIB_FUNCTION
684 krb5_cc_next_cred_match(krb5_context context,
685 			const krb5_ccache id,
686 			krb5_cc_cursor * cursor,
687 			krb5_creds * creds,
688 			krb5_flags whichfields,
689 			const krb5_creds * mcreds)
690 {
691     krb5_error_code ret;
692     while (1) {
693 	ret = krb5_cc_next_cred(context, id, cursor, creds);
694 	if (ret)
695 	    return ret;
696 	if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
697 	    return 0;
698 	krb5_free_cred_contents(context, creds);
699     }
700 }
701 
702 /**
703  * Destroy the cursor `cursor'.
704  *
705  * @ingroup krb5_ccache
706  */
707 
708 
709 krb5_error_code KRB5_LIB_FUNCTION
710 krb5_cc_end_seq_get (krb5_context context,
711 		     const krb5_ccache id,
712 		     krb5_cc_cursor *cursor)
713 {
714     return (*id->ops->end_get)(context, id, cursor);
715 }
716 
717 /**
718  * Remove the credential identified by `cred', `which' from `id'.
719  *
720  * @ingroup krb5_ccache
721  */
722 
723 
724 krb5_error_code KRB5_LIB_FUNCTION
725 krb5_cc_remove_cred(krb5_context context,
726 		    krb5_ccache id,
727 		    krb5_flags which,
728 		    krb5_creds *cred)
729 {
730     if(id->ops->remove_cred == NULL) {
731 	krb5_set_error_string(context,
732 			      "ccache %s does not support remove_cred",
733 			      id->ops->prefix);
734 	return EACCES; /* XXX */
735     }
736     return (*id->ops->remove_cred)(context, id, which, cred);
737 }
738 
739 /**
740  * Set the flags of `id' to `flags'.
741  *
742  * @ingroup krb5_ccache
743  */
744 
745 
746 krb5_error_code KRB5_LIB_FUNCTION
747 krb5_cc_set_flags(krb5_context context,
748 		  krb5_ccache id,
749 		  krb5_flags flags)
750 {
751     return (*id->ops->set_flags)(context, id, flags);
752 }
753 
754 /**
755  * Copy the contents of `from' to `to'.
756  *
757  * @ingroup krb5_ccache
758  */
759 
760 
761 krb5_error_code KRB5_LIB_FUNCTION
762 krb5_cc_copy_cache_match(krb5_context context,
763 			 const krb5_ccache from,
764 			 krb5_ccache to,
765 			 krb5_flags whichfields,
766 			 const krb5_creds * mcreds,
767 			 unsigned int *matched)
768 {
769     krb5_error_code ret;
770     krb5_cc_cursor cursor;
771     krb5_creds cred;
772     krb5_principal princ;
773 
774     ret = krb5_cc_get_principal(context, from, &princ);
775     if (ret)
776 	return ret;
777     ret = krb5_cc_initialize(context, to, princ);
778     if (ret) {
779 	krb5_free_principal(context, princ);
780 	return ret;
781     }
782     ret = krb5_cc_start_seq_get(context, from, &cursor);
783     if (ret) {
784 	krb5_free_principal(context, princ);
785 	return ret;
786     }
787     if (matched)
788 	*matched = 0;
789     while (ret == 0 &&
790 	   krb5_cc_next_cred_match(context, from, &cursor, &cred,
791 				   whichfields, mcreds) == 0) {
792 	if (matched)
793 	    (*matched)++;
794 	ret = krb5_cc_store_cred(context, to, &cred);
795 	krb5_free_cred_contents(context, &cred);
796     }
797     krb5_cc_end_seq_get(context, from, &cursor);
798     krb5_free_principal(context, princ);
799     return ret;
800 }
801 
802 /**
803  * Just like krb5_cc_copy_cache_match, but copy everything.
804  *
805  * @ingroup krb5_ccache
806  */
807 
808 
809 krb5_error_code KRB5_LIB_FUNCTION
810 krb5_cc_copy_cache(krb5_context context,
811 		   const krb5_ccache from,
812 		   krb5_ccache to)
813 {
814     return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
815 }
816 
817 /**
818  * Return the version of `id'.
819  *
820  * @ingroup krb5_ccache
821  */
822 
823 
824 krb5_error_code KRB5_LIB_FUNCTION
825 krb5_cc_get_version(krb5_context context,
826 		    const krb5_ccache id)
827 {
828     if(id->ops->get_version)
829 	return (*id->ops->get_version)(context, id);
830     else
831 	return 0;
832 }
833 
834 /**
835  * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
836  *
837  * @ingroup krb5_ccache
838  */
839 
840 
841 void KRB5_LIB_FUNCTION
842 krb5_cc_clear_mcred(krb5_creds *mcred)
843 {
844     memset(mcred, 0, sizeof(*mcred));
845 }
846 
847 /**
848  * Get the cc ops that is registered in `context' to handle the
849  * `prefix'. `prefix' can be a complete credential cache name or a
850  * prefix, the function will only use part up to the first colon (:)
851  * if there is one.
852  * Returns NULL if ops not found.
853  *
854  * @ingroup krb5_ccache
855  */
856 
857 
858 const krb5_cc_ops *
859 krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
860 {
861     char *p, *p1;
862     int i;
863 
864     if (prefix[0] == '/')
865 	return &krb5_fcc_ops;
866 
867     p = strdup(prefix);
868     if (p == NULL) {
869 	krb5_set_error_string(context, "malloc - out of memory");
870 	return NULL;
871     }
872     p1 = strchr(p, ':');
873     if (p1)
874 	*p1 = '\0';
875 
876     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
877 	if(strcmp(context->cc_ops[i].prefix, p) == 0) {
878 	    free(p);
879 	    return &context->cc_ops[i];
880 	}
881     }
882     free(p);
883     return NULL;
884 }
885 
886 struct krb5_cc_cache_cursor_data {
887     const krb5_cc_ops *ops;
888     krb5_cc_cursor cursor;
889 };
890 
891 /**
892  * Start iterating over all caches of `type'. If `type' is NULL, the
893  * default type is * used. `cursor' is initialized to the beginning.
894  *
895  * @return Return 0 or an error code.
896  *
897  * @ingroup krb5_ccache
898  */
899 
900 
901 krb5_error_code KRB5_LIB_FUNCTION
902 krb5_cc_cache_get_first (krb5_context context,
903 			 const char *type,
904 			 krb5_cc_cache_cursor *cursor)
905 {
906     const krb5_cc_ops *ops;
907     krb5_error_code ret;
908 
909     if (type == NULL)
910 	type = krb5_cc_default_name(context);
911 
912     ops = krb5_cc_get_prefix_ops(context, type);
913     if (ops == NULL) {
914 	krb5_set_error_string(context, "Unknown type \"%s\" when iterating "
915 			      "trying to iterate the credential caches", type);
916 	return KRB5_CC_UNKNOWN_TYPE;
917     }
918 
919     if (ops->get_cache_first == NULL) {
920 	krb5_set_error_string(context, "Credential cache type %s doesn't support "
921 			      "iterations over caches", ops->prefix);
922 	return KRB5_CC_NOSUPP;
923     }
924 
925     *cursor = calloc(1, sizeof(**cursor));
926     if (*cursor == NULL) {
927 	krb5_set_error_string(context, "malloc - out of memory");
928 	return ENOMEM;
929     }
930 
931     (*cursor)->ops = ops;
932 
933     ret = ops->get_cache_first(context, &(*cursor)->cursor);
934     if (ret) {
935 	free(*cursor);
936 	*cursor = NULL;
937     }
938     return ret;
939 }
940 
941 /**
942  * Retrieve the next cache pointed to by (`cursor') in `id'
943  * and advance `cursor'.
944  *
945  * @return Return 0 or an error code.
946  *
947  * @ingroup krb5_ccache
948  */
949 
950 
951 krb5_error_code KRB5_LIB_FUNCTION
952 krb5_cc_cache_next (krb5_context context,
953 		   krb5_cc_cache_cursor cursor,
954 		   krb5_ccache *id)
955 {
956     return cursor->ops->get_cache_next(context, cursor->cursor, id);
957 }
958 
959 /**
960  * Destroy the cursor `cursor'.
961  *
962  * @return Return 0 or an error code.
963  *
964  * @ingroup krb5_ccache
965  */
966 
967 
968 krb5_error_code KRB5_LIB_FUNCTION
969 krb5_cc_cache_end_seq_get (krb5_context context,
970 			   krb5_cc_cache_cursor cursor)
971 {
972     krb5_error_code ret;
973     ret = cursor->ops->end_cache_get(context, cursor->cursor);
974     cursor->ops = NULL;
975     free(cursor);
976     return ret;
977 }
978 
979 /**
980  * Search for a matching credential cache of type `type' that have the
981  * `principal' as the default principal. If NULL is used for `type',
982  * the default type is used. On success, `id' needs to be freed with
983  * krb5_cc_close or krb5_cc_destroy.
984  *
985  * @return On failure, error code is returned and `id' is set to NULL.
986  *
987  * @ingroup krb5_ccache
988  */
989 
990 
991 krb5_error_code KRB5_LIB_FUNCTION
992 krb5_cc_cache_match (krb5_context context,
993 		     krb5_principal client,
994 		     const char *type,
995 		     krb5_ccache *id)
996 {
997     krb5_cc_cache_cursor cursor;
998     krb5_error_code ret;
999     krb5_ccache cache = NULL;
1000 
1001     *id = NULL;
1002 
1003     ret = krb5_cc_cache_get_first (context, type, &cursor);
1004     if (ret)
1005 	return ret;
1006 
1007     while ((ret = krb5_cc_cache_next (context, cursor, &cache)) == 0) {
1008 	krb5_principal principal;
1009 
1010 	ret = krb5_cc_get_principal(context, cache, &principal);
1011 	if (ret == 0) {
1012 	    krb5_boolean match;
1013 
1014 	    match = krb5_principal_compare(context, principal, client);
1015 	    krb5_free_principal(context, principal);
1016 	    if (match)
1017 		break;
1018 	}
1019 
1020 	krb5_cc_close(context, cache);
1021 	cache = NULL;
1022     }
1023 
1024     krb5_cc_cache_end_seq_get(context, cursor);
1025 
1026     if (cache == NULL) {
1027 	char *str;
1028 
1029 	krb5_unparse_name(context, client, &str);
1030 
1031 	krb5_set_error_string(context, "Principal %s not found in a "
1032 			  "credential cache", str ? str : "<out of memory>");
1033 	if (str)
1034 	    free(str);
1035 	return KRB5_CC_NOTFOUND;
1036     }
1037     *id = cache;
1038 
1039     return 0;
1040 }
1041 
1042 /**
1043  * Move the content from one credential cache to another. The
1044  * operation is an atomic switch.
1045  *
1046  * @param context a Keberos context
1047  * @param from the credential cache to move the content from
1048  * @param to the credential cache to move the content to
1049 
1050  * @return On sucess, from is freed. On failure, error code is
1051  * returned and from and to are both still allocated.
1052  *
1053  * @ingroup krb5_ccache
1054  */
1055 
1056 krb5_error_code
1057 krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1058 {
1059     krb5_error_code ret;
1060 
1061     if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
1062 	krb5_set_error_string(context, "Moving credentials between diffrent "
1063 			      "types not yet supported");
1064 	return KRB5_CC_NOSUPP;
1065     }
1066 
1067     ret = (*to->ops->move)(context, from, to);
1068     if (ret == 0) {
1069 	memset(from, 0, sizeof(*from));
1070 	free(from);
1071     }
1072     return ret;
1073 }
1074