/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- krb5_cc_register
- _krb5_cc_allocate
- allocate_ccache
- krb5_cc_resolve
- krb5_cc_gen_new
- krb5_cc_new_unique
- krb5_cc_get_name
- krb5_cc_get_type
- krb5_cc_get_full_name
- krb5_cc_get_ops
- _krb5_expand_default_cc_name
- environment_changed
- krb5_cc_switch
- krb5_cc_set_default_name
- krb5_cc_default_name
- krb5_cc_default
- krb5_cc_initialize
- krb5_cc_destroy
- krb5_cc_close
- krb5_cc_store_cred
- krb5_cc_retrieve_cred
- krb5_cc_get_principal
- krb5_cc_start_seq_get
- krb5_cc_next_cred
- krb5_cc_next_cred_match
- krb5_cc_end_seq_get
- krb5_cc_remove_cred
- krb5_cc_set_flags
- krb5_cc_get_flags
- krb5_cc_copy_cache_match
- krb5_cc_copy_cache
- krb5_cc_copy_creds
- krb5_cc_get_version
- krb5_cc_clear_mcred
- krb5_cc_get_prefix_ops
- krb5_cc_cache_get_first
- krb5_cc_cache_next
- krb5_cc_cache_end_seq_get
- krb5_cc_cache_match
- krb5_cc_move
- build_conf_principals
- krb5_is_config_principal
- krb5_cc_set_config
- krb5_cc_get_config
- krb5_cccol_cursor_new
- krb5_cccol_cursor_next
- krb5_cccol_cursor_free
- krb5_cc_last_change_time
- krb5_cccol_last_change_time
1 /*
2 * Copyright (c) 1997 - 2008 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$");
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, see krb5_get_error_message().
48 *
49 * @ingroup krb5_ccache
50 */
51
52 krb5_error_code KRB5_LIB_FUNCTION
53 krb5_cc_register(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
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_message(context,
63 KRB5_CC_TYPE_EXISTS,
64 N_("cache type %s already exists", "type"),
65 ops->prefix);
66 return KRB5_CC_TYPE_EXISTS;
67 }
68 break;
69 }
70 }
71 if(i == context->num_cc_ops) {
72 krb5_cc_ops *o = realloc(context->cc_ops,
73 (context->num_cc_ops + 1) *
74 sizeof(*context->cc_ops));
75 if(o == NULL) {
76 krb5_set_error_message(context, KRB5_CC_NOMEM,
77 N_("malloc: out of memory", ""));
78 return KRB5_CC_NOMEM;
79 }
80 context->num_cc_ops++;
81 context->cc_ops = o;
82 memset(context->cc_ops + i, 0,
83 (context->num_cc_ops - i) * sizeof(*context->cc_ops));
84 }
85 memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
86 return 0;
87 }
88
89 /*
90 * Allocate the memory for a `id' and the that function table to
91 * `ops'. Returns 0 or and error code.
92 */
93
94 krb5_error_code
95 _krb5_cc_allocate(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
96 const krb5_cc_ops *ops,
97 krb5_ccache *id)
98 {
99 krb5_ccache p;
100
101 p = malloc (sizeof(*p));
102 if(p == NULL) {
103 krb5_set_error_message(context, KRB5_CC_NOMEM,
104 N_("malloc: out of memory", ""));
105 return KRB5_CC_NOMEM;
106 }
107 p->ops = ops;
108 *id = p;
109
110 return 0;
111 }
112
113 /*
114 * Allocate memory for a new ccache in `id' with operations `ops'
115 * and name `residual'. Return 0 or an error code.
116 */
117
118 static krb5_error_code
119 allocate_ccache (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
120 const krb5_cc_ops *ops,
121 const char *residual,
122 krb5_ccache *id)
123 {
124 krb5_error_code ret;
125
126 ret = _krb5_cc_allocate(context, ops, id);
127 if (ret)
128 return ret;
129 ret = (*id)->ops->resolve(context, id, residual);
130 if(ret)
131 free(*id);
132 return ret;
133 }
134
135 /**
136 * Find and allocate a ccache in `id' from the specification in `residual'.
137 * If the ccache name doesn't contain any colon, interpret it as a file name.
138 *
139 * @param context a Keberos context.
140 * @param name string name of a credential cache.
141 * @param id return pointer to a found credential cache.
142 *
143 * @return Return 0 or an error code. In case of an error, id is set
144 * to NULL, see krb5_get_error_message().
145 *
146 * @ingroup krb5_ccache
147 */
148
149
150 krb5_error_code KRB5_LIB_FUNCTION
151 krb5_cc_resolve(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
152 const char *name,
153 krb5_ccache *id)
154 {
155 int i;
156
157 *id = NULL;
158
159 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
160 size_t prefix_len = strlen(context->cc_ops[i].prefix);
161
162 if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
163 && name[prefix_len] == ':') {
164 return allocate_ccache (context, &context->cc_ops[i],
165 name + prefix_len + 1,
166 id);
167 }
168 }
169 if (strchr (name, ':') == NULL)
170 return allocate_ccache (context, &krb5_fcc_ops, name, id);
171 else {
172 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
173 N_("unknown ccache type %s", "name"), name);
174 return KRB5_CC_UNKNOWN_TYPE;
175 }
176 }
177
178 /**
179 * Generate a new ccache of type `ops' in `id'.
180 *
181 * @return Return an error code or 0, see krb5_get_error_message().
182 *
183 * @ingroup krb5_ccache
184 */
185
186
187 krb5_error_code KRB5_LIB_FUNCTION
188 krb5_cc_gen_new(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
189 const krb5_cc_ops *ops,
190 krb5_ccache *id)
191 {
192 return krb5_cc_new_unique(context, ops->prefix, NULL, id);
193 }
194
195 /**
196 * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
197 * the library chooses the default credential cache type. The supplied
198 * `hint' (that can be NULL) is a string that the credential cache
199 * type can use to base the name of the credential on, this is to make
200 * it easier for the user to differentiate the credentials.
201 *
202 * @return Return an error code or 0, see krb5_get_error_message().
203 *
204 * @ingroup krb5_ccache
205 */
206
207 krb5_error_code KRB5_LIB_FUNCTION
208 krb5_cc_new_unique(krb5_context context, const char *type,
/* [<][>][^][v][top][bottom][index][help] */
209 const char *hint, krb5_ccache *id)
210 {
211 const krb5_cc_ops *ops;
212 krb5_error_code ret;
213
214 ops = krb5_cc_get_prefix_ops(context, type);
215 if (ops == NULL) {
216 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
217 "Credential cache type %s is unknown", type);
218 return KRB5_CC_UNKNOWN_TYPE;
219 }
220
221 ret = _krb5_cc_allocate(context, ops, id);
222 if (ret)
223 return ret;
224 return (*id)->ops->gen_new(context, id);
225 }
226
227 /**
228 * Return the name of the ccache `id'
229 *
230 * @ingroup krb5_ccache
231 */
232
233
234 const char* KRB5_LIB_FUNCTION
235 krb5_cc_get_name(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
236 krb5_ccache id)
237 {
238 return id->ops->get_name(context, id);
239 }
240
241 /**
242 * Return the type of the ccache `id'.
243 *
244 * @ingroup krb5_ccache
245 */
246
247
248 const char* KRB5_LIB_FUNCTION
249 krb5_cc_get_type(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
250 krb5_ccache id)
251 {
252 return id->ops->prefix;
253 }
254
255 /**
256 * Return the complete resolvable name the ccache `id' in `str´.
257 * `str` should be freed with free(3).
258 * Returns 0 or an error (and then *str is set to NULL).
259 *
260 * @ingroup krb5_ccache
261 */
262
263
264 krb5_error_code KRB5_LIB_FUNCTION
265 krb5_cc_get_full_name(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
266 krb5_ccache id,
267 char **str)
268 {
269 const char *type, *name;
270
271 *str = NULL;
272
273 type = krb5_cc_get_type(context, id);
274 if (type == NULL) {
275 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
276 "cache have no name of type");
277 return KRB5_CC_UNKNOWN_TYPE;
278 }
279
280 name = krb5_cc_get_name(context, id);
281 if (name == NULL) {
282 krb5_set_error_message(context, KRB5_CC_BADNAME,
283 "cache of type %s have no name", type);
284 return KRB5_CC_BADNAME;
285 }
286
287 if (asprintf(str, "%s:%s", type, name) == -1) {
288 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
289 *str = NULL;
290 return ENOMEM;
291 }
292 return 0;
293 }
294
295 /**
296 * Return krb5_cc_ops of a the ccache `id'.
297 *
298 * @ingroup krb5_ccache
299 */
300
301
302 const krb5_cc_ops *
303 krb5_cc_get_ops(krb5_context context, krb5_ccache id)
/* [<][>][^][v][top][bottom][index][help] */
304 {
305 return id->ops;
306 }
307
308 /*
309 * Expand variables in `str' into `res'
310 */
311
312 krb5_error_code
313 _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
/* [<][>][^][v][top][bottom][index][help] */
314 {
315 size_t tlen, len = 0;
316 char *tmp, *tmp2, *append;
317
318 *res = NULL;
319
320 while (str && *str) {
321 tmp = strstr(str, "%{");
322 if (tmp && tmp != str) {
323 append = malloc((tmp - str) + 1);
324 if (append) {
325 memcpy(append, str, tmp - str);
326 append[tmp - str] = '\0';
327 }
328 str = tmp;
329 } else if (tmp) {
330 tmp2 = strchr(tmp, '}');
331 if (tmp2 == NULL) {
332 free(*res);
333 *res = NULL;
334 krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT,
335 "variable missing }");
336 return KRB5_CONFIG_BADFORMAT;
337 }
338 if (strncasecmp(tmp, "%{uid}", 6) == 0)
339 asprintf(&append, "%u", (unsigned)getuid());
340 else if (strncasecmp(tmp, "%{null}", 7) == 0)
341 append = strdup("");
342 else {
343 free(*res);
344 *res = NULL;
345 krb5_set_error_message(context,
346 KRB5_CONFIG_BADFORMAT,
347 "expand default cache unknown "
348 "variable \"%.*s\"",
349 (int)(tmp2 - tmp) - 2, tmp + 2);
350 return KRB5_CONFIG_BADFORMAT;
351 }
352 str = tmp2 + 1;
353 } else {
354 append = strdup(str);
355 str = NULL;
356 }
357 if (append == NULL) {
358 free(*res);
359 *res = NULL;
360 krb5_set_error_message(context, ENOMEM,
361 N_("malloc: out of memory", ""));
362 return ENOMEM;
363 }
364
365 tlen = strlen(append);
366 tmp = realloc(*res, len + tlen + 1);
367 if (tmp == NULL) {
368 free(append);
369 free(*res);
370 *res = NULL;
371 krb5_set_error_message(context, ENOMEM,
372 N_("malloc: out of memory", ""));
373 return ENOMEM;
374 }
375 *res = tmp;
376 memcpy(*res + len, append, tlen + 1);
377 len = len + tlen;
378 free(append);
379 }
380 return 0;
381 }
382
383 /*
384 * Return non-zero if envirnoment that will determine default krb5cc
385 * name has changed.
386 */
387
388 static int
389 environment_changed(krb5_context context)
/* [<][>][^][v][top][bottom][index][help] */
390 {
391 const char *e;
392
393 /* if the cc name was set, don't change it */
394 if (context->default_cc_name_set)
395 return 0;
396
397 if(issuid())
398 return 0;
399
400 e = getenv("KRB5CCNAME");
401 if (e == NULL) {
402 if (context->default_cc_name_env) {
403 free(context->default_cc_name_env);
404 context->default_cc_name_env = NULL;
405 return 1;
406 }
407 } else {
408 if (context->default_cc_name_env == NULL)
409 return 1;
410 if (strcmp(e, context->default_cc_name_env) != 0)
411 return 1;
412 }
413 return 0;
414 }
415
416 /**
417 * Switch the default default credential cache for a specific
418 * credcache type (and name for some implementations).
419 *
420 * @return Return an error code or 0, see krb5_get_error_message().
421 *
422 * @ingroup krb5_ccache
423 */
424
425 krb5_error_code
426 krb5_cc_switch(krb5_context context, krb5_ccache id)
/* [<][>][^][v][top][bottom][index][help] */
427 {
428
429 if (id->ops->set_default == NULL)
430 return 0;
431
432 return (*id->ops->set_default)(context, id);
433 }
434
435 /**
436 * Set the default cc name for `context' to `name'.
437 *
438 * @ingroup krb5_ccache
439 */
440
441 krb5_error_code KRB5_LIB_FUNCTION
442 krb5_cc_set_default_name(krb5_context context, const char *name)
/* [<][>][^][v][top][bottom][index][help] */
443 {
444 krb5_error_code ret = 0;
445 char *p;
446
447 if (name == NULL) {
448 const char *e = NULL;
449
450 if(!issuid()) {
451 e = getenv("KRB5CCNAME");
452 if (e) {
453 p = strdup(e);
454 if (context->default_cc_name_env)
455 free(context->default_cc_name_env);
456 context->default_cc_name_env = strdup(e);
457 }
458 }
459 if (e == NULL) {
460 e = krb5_config_get_string(context, NULL, "libdefaults",
461 "default_cc_name", NULL);
462 if (e) {
463 ret = _krb5_expand_default_cc_name(context, e, &p);
464 if (ret)
465 return ret;
466 }
467 if (e == NULL) {
468 const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
469 e = krb5_config_get_string(context, NULL, "libdefaults",
470 "default_cc_type", NULL);
471 if (e) {
472 ops = krb5_cc_get_prefix_ops(context, e);
473 if (ops == NULL) {
474 krb5_set_error_message(context,
475 KRB5_CC_UNKNOWN_TYPE,
476 "Credential cache type %s "
477 "is unknown", e);
478 return KRB5_CC_UNKNOWN_TYPE;
479 }
480 }
481 ret = (*ops->get_default_name)(context, &p);
482 if (ret)
483 return ret;
484 }
485 }
486 context->default_cc_name_set = 0;
487 } else {
488 p = strdup(name);
489 context->default_cc_name_set = 1;
490 }
491
492 if (p == NULL) {
493 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
494 return ENOMEM;
495 }
496
497 if (context->default_cc_name)
498 free(context->default_cc_name);
499
500 context->default_cc_name = p;
501
502 return ret;
503 }
504
505 /**
506 * Return a pointer to a context static string containing the default
507 * ccache name.
508 *
509 * @return String to the default credential cache name.
510 *
511 * @ingroup krb5_ccache
512 */
513
514
515 const char* KRB5_LIB_FUNCTION
516 krb5_cc_default_name(krb5_context context)
/* [<][>][^][v][top][bottom][index][help] */
517 {
518 if (context->default_cc_name == NULL || environment_changed(context))
519 krb5_cc_set_default_name(context, NULL);
520
521 return context->default_cc_name;
522 }
523
524 /**
525 * Open the default ccache in `id'.
526 *
527 * @return Return an error code or 0, see krb5_get_error_message().
528 *
529 * @ingroup krb5_ccache
530 */
531
532
533 krb5_error_code KRB5_LIB_FUNCTION
534 krb5_cc_default(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
535 krb5_ccache *id)
536 {
537 const char *p = krb5_cc_default_name(context);
538
539 if (p == NULL) {
540 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
541 return ENOMEM;
542 }
543 return krb5_cc_resolve(context, p, id);
544 }
545
546 /**
547 * Create a new ccache in `id' for `primary_principal'.
548 *
549 * @return Return an error code or 0, see krb5_get_error_message().
550 *
551 * @ingroup krb5_ccache
552 */
553
554
555 krb5_error_code KRB5_LIB_FUNCTION
556 krb5_cc_initialize(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
557 krb5_ccache id,
558 krb5_principal primary_principal)
559 {
560 return (*id->ops->init)(context, id, primary_principal);
561 }
562
563
564 /**
565 * Remove the ccache `id'.
566 *
567 * @return Return an error code or 0, see krb5_get_error_message().
568 *
569 * @ingroup krb5_ccache
570 */
571
572
573 krb5_error_code KRB5_LIB_FUNCTION
574 krb5_cc_destroy(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
575 krb5_ccache id)
576 {
577 krb5_error_code ret;
578
579 ret = (*id->ops->destroy)(context, id);
580 krb5_cc_close (context, id);
581 return ret;
582 }
583
584 /**
585 * Stop using the ccache `id' and free the related resources.
586 *
587 * @return Return an error code or 0, see krb5_get_error_message().
588 *
589 * @ingroup krb5_ccache
590 */
591
592
593 krb5_error_code KRB5_LIB_FUNCTION
594 krb5_cc_close(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
595 krb5_ccache id)
596 {
597 krb5_error_code ret;
598 ret = (*id->ops->close)(context, id);
599 free(id);
600 return ret;
601 }
602
603 /**
604 * Store `creds' in the ccache `id'.
605 *
606 * @return Return an error code or 0, see krb5_get_error_message().
607 *
608 * @ingroup krb5_ccache
609 */
610
611
612 krb5_error_code KRB5_LIB_FUNCTION
613 krb5_cc_store_cred(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
614 krb5_ccache id,
615 krb5_creds *creds)
616 {
617 return (*id->ops->store)(context, id, creds);
618 }
619
620 /**
621 * Retrieve the credential identified by `mcreds' (and `whichfields')
622 * from `id' in `creds'. 'creds' must be free by the caller using
623 * krb5_free_cred_contents.
624 *
625 * @return Return an error code or 0, see krb5_get_error_message().
626 *
627 * @ingroup krb5_ccache
628 */
629
630
631 krb5_error_code KRB5_LIB_FUNCTION
632 krb5_cc_retrieve_cred(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
633 krb5_ccache id,
634 krb5_flags whichfields,
635 const krb5_creds *mcreds,
636 krb5_creds *creds)
637 {
638 krb5_error_code ret;
639 krb5_cc_cursor cursor;
640
641 if (id->ops->retrieve != NULL) {
642 return (*id->ops->retrieve)(context, id, whichfields,
643 mcreds, creds);
644 }
645
646 ret = krb5_cc_start_seq_get(context, id, &cursor);
647 if (ret)
648 return ret;
649 while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
650 if(krb5_compare_creds(context, whichfields, mcreds, creds)){
651 ret = 0;
652 break;
653 }
654 krb5_free_cred_contents (context, creds);
655 }
656 krb5_cc_end_seq_get(context, id, &cursor);
657 return ret;
658 }
659
660 /**
661 * Return the principal of `id' in `principal'.
662 *
663 * @return Return an error code or 0, see krb5_get_error_message().
664 *
665 * @ingroup krb5_ccache
666 */
667
668
669 krb5_error_code KRB5_LIB_FUNCTION
670 krb5_cc_get_principal(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
671 krb5_ccache id,
672 krb5_principal *principal)
673 {
674 return (*id->ops->get_princ)(context, id, principal);
675 }
676
677 /**
678 * Start iterating over `id', `cursor' is initialized to the
679 * beginning.
680 *
681 * @return Return an error code or 0, see krb5_get_error_message().
682 *
683 * @ingroup krb5_ccache
684 */
685
686
687 krb5_error_code KRB5_LIB_FUNCTION
688 krb5_cc_start_seq_get (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
689 const krb5_ccache id,
690 krb5_cc_cursor *cursor)
691 {
692 return (*id->ops->get_first)(context, id, cursor);
693 }
694
695 /**
696 * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
697 * and advance `cursor'.
698 *
699 * @return Return an error code or 0, see krb5_get_error_message().
700 *
701 * @ingroup krb5_ccache
702 */
703
704
705 krb5_error_code KRB5_LIB_FUNCTION
706 krb5_cc_next_cred (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
707 const krb5_ccache id,
708 krb5_cc_cursor *cursor,
709 krb5_creds *creds)
710 {
711 return (*id->ops->get_next)(context, id, cursor, creds);
712 }
713
714 /**
715 * Like krb5_cc_next_cred, but allow for selective retrieval
716 *
717 * @ingroup krb5_ccache
718 */
719
720
721 krb5_error_code KRB5_LIB_FUNCTION
722 krb5_cc_next_cred_match(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
723 const krb5_ccache id,
724 krb5_cc_cursor * cursor,
725 krb5_creds * creds,
726 krb5_flags whichfields,
727 const krb5_creds * mcreds)
728 {
729 krb5_error_code ret;
730 while (1) {
731 ret = krb5_cc_next_cred(context, id, cursor, creds);
732 if (ret)
733 return ret;
734 if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
735 return 0;
736 krb5_free_cred_contents(context, creds);
737 }
738 }
739
740 /**
741 * Destroy the cursor `cursor'.
742 *
743 * @ingroup krb5_ccache
744 */
745
746
747 krb5_error_code KRB5_LIB_FUNCTION
748 krb5_cc_end_seq_get (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
749 const krb5_ccache id,
750 krb5_cc_cursor *cursor)
751 {
752 return (*id->ops->end_get)(context, id, cursor);
753 }
754
755 /**
756 * Remove the credential identified by `cred', `which' from `id'.
757 *
758 * @ingroup krb5_ccache
759 */
760
761
762 krb5_error_code KRB5_LIB_FUNCTION
763 krb5_cc_remove_cred(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
764 krb5_ccache id,
765 krb5_flags which,
766 krb5_creds *cred)
767 {
768 if(id->ops->remove_cred == NULL) {
769 krb5_set_error_message(context,
770 EACCES,
771 "ccache %s does not support remove_cred",
772 id->ops->prefix);
773 return EACCES; /* XXX */
774 }
775 return (*id->ops->remove_cred)(context, id, which, cred);
776 }
777
778 /**
779 * Set the flags of `id' to `flags'.
780 *
781 * @ingroup krb5_ccache
782 */
783
784
785 krb5_error_code KRB5_LIB_FUNCTION
786 krb5_cc_set_flags(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
787 krb5_ccache id,
788 krb5_flags flags)
789 {
790 return (*id->ops->set_flags)(context, id, flags);
791 }
792
793 /**
794 * Get the flags of `id', store them in `flags'.
795 *
796 * @ingroup krb5_ccache
797 */
798
799 krb5_error_code KRB5_LIB_FUNCTION
800 krb5_cc_get_flags(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
801 krb5_ccache id,
802 krb5_flags *flags)
803 {
804 *flags = 0;
805 return 0;
806 }
807
808 /**
809 * Copy the contents of `from' to `to'.
810 *
811 * @ingroup krb5_ccache
812 */
813
814
815 krb5_error_code KRB5_LIB_FUNCTION
816 krb5_cc_copy_cache_match(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
817 const krb5_ccache from,
818 krb5_ccache to,
819 krb5_flags whichfields,
820 const krb5_creds * mcreds,
821 unsigned int *matched)
822 {
823 krb5_error_code ret;
824 krb5_cc_cursor cursor;
825 krb5_creds cred;
826 krb5_principal princ;
827
828 ret = krb5_cc_get_principal(context, from, &princ);
829 if (ret)
830 return ret;
831 ret = krb5_cc_initialize(context, to, princ);
832 if (ret) {
833 krb5_free_principal(context, princ);
834 return ret;
835 }
836 ret = krb5_cc_start_seq_get(context, from, &cursor);
837 if (ret) {
838 krb5_free_principal(context, princ);
839 return ret;
840 }
841 if (matched)
842 *matched = 0;
843 while (ret == 0 &&
844 krb5_cc_next_cred_match(context, from, &cursor, &cred,
845 whichfields, mcreds) == 0) {
846 if (matched)
847 (*matched)++;
848 ret = krb5_cc_store_cred(context, to, &cred);
849 krb5_free_cred_contents(context, &cred);
850 }
851 krb5_cc_end_seq_get(context, from, &cursor);
852 krb5_free_principal(context, princ);
853 return ret;
854 }
855
856
857 /**
858 * Just like krb5_cc_copy_cache_match, but copy everything.
859 *
860 * @ingroup @krb5_ccache
861 */
862
863 krb5_error_code KRB5_LIB_FUNCTION
864 krb5_cc_copy_cache(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
865 const krb5_ccache from,
866 krb5_ccache to)
867 {
868 return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
869 }
870
871 /**
872 * MIT compat glue
873 *
874 * @ingroup krb5_ccache
875 */
876
877 krb5_error_code KRB5_LIB_FUNCTION
878 krb5_cc_copy_creds(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
879 const krb5_ccache from,
880 krb5_ccache to)
881 {
882 return krb5_cc_copy_cache(context, from, to);
883 }
884
885 /**
886 * Return the version of `id'.
887 *
888 * @ingroup krb5_ccache
889 */
890
891
892 krb5_error_code KRB5_LIB_FUNCTION
893 krb5_cc_get_version(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
894 const krb5_ccache id)
895 {
896 if(id->ops->get_version)
897 return (*id->ops->get_version)(context, id);
898 else
899 return 0;
900 }
901
902 /**
903 * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
904 *
905 * @ingroup krb5_ccache
906 */
907
908
909 void KRB5_LIB_FUNCTION
910 krb5_cc_clear_mcred(krb5_creds *mcred)
/* [<][>][^][v][top][bottom][index][help] */
911 {
912 memset(mcred, 0, sizeof(*mcred));
913 }
914
915 /**
916 * Get the cc ops that is registered in `context' to handle the
917 * prefix. prefix can be a complete credential cache name or a
918 * prefix, the function will only use part up to the first colon (:)
919 * if there is one. If prefix the argument is NULL, the default ccache
920 * implemtation is returned.
921 *
922 * @return Returns NULL if ops not found.
923 *
924 * @ingroup krb5_ccache
925 */
926
927
928 const krb5_cc_ops *
929 krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
/* [<][>][^][v][top][bottom][index][help] */
930 {
931 char *p, *p1;
932 int i;
933
934 if (prefix == NULL)
935 return KRB5_DEFAULT_CCTYPE;
936 if (prefix[0] == '/')
937 return &krb5_fcc_ops;
938
939 p = strdup(prefix);
940 if (p == NULL) {
941 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
942 return NULL;
943 }
944 p1 = strchr(p, ':');
945 if (p1)
946 *p1 = '\0';
947
948 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
949 if(strcmp(context->cc_ops[i].prefix, p) == 0) {
950 free(p);
951 return &context->cc_ops[i];
952 }
953 }
954 free(p);
955 return NULL;
956 }
957
958 struct krb5_cc_cache_cursor_data {
959 const krb5_cc_ops *ops;
960 krb5_cc_cursor cursor;
961 };
962
963 /**
964 * Start iterating over all caches of specified type. See also
965 * krb5_cccol_cursor_new().
966
967 * @param context A Kerberos 5 context
968 * @param type optional type to iterate over, if NULL, the default cache is used.
969 * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get().
970 *
971 * @return Return an error code or 0, see krb5_get_error_message().
972 *
973 * @ingroup krb5_ccache
974 */
975
976
977 krb5_error_code KRB5_LIB_FUNCTION
978 krb5_cc_cache_get_first (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
979 const char *type,
980 krb5_cc_cache_cursor *cursor)
981 {
982 const krb5_cc_ops *ops;
983 krb5_error_code ret;
984
985 if (type == NULL)
986 type = krb5_cc_default_name(context);
987
988 ops = krb5_cc_get_prefix_ops(context, type);
989 if (ops == NULL) {
990 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
991 "Unknown type \"%s\" when iterating "
992 "trying to iterate the credential caches", type);
993 return KRB5_CC_UNKNOWN_TYPE;
994 }
995
996 if (ops->get_cache_first == NULL) {
997 krb5_set_error_message(context, KRB5_CC_NOSUPP,
998 N_("Credential cache type %s doesn't support "
999 "iterations over caches", "type"),
1000 ops->prefix);
1001 return KRB5_CC_NOSUPP;
1002 }
1003
1004 *cursor = calloc(1, sizeof(**cursor));
1005 if (*cursor == NULL) {
1006 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1007 return ENOMEM;
1008 }
1009
1010 (*cursor)->ops = ops;
1011
1012 ret = ops->get_cache_first(context, &(*cursor)->cursor);
1013 if (ret) {
1014 free(*cursor);
1015 *cursor = NULL;
1016 }
1017 return ret;
1018 }
1019
1020 /**
1021 * Retrieve the next cache pointed to by (`cursor') in `id'
1022 * and advance `cursor'.
1023 *
1024 * @return Return 0 or an error code. Returns KRB5_CC_END when the end
1025 * of caches is reached, see krb5_get_error_message().
1026 *
1027 * @ingroup krb5_ccache
1028 */
1029
1030
1031 krb5_error_code KRB5_LIB_FUNCTION
1032 krb5_cc_cache_next (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
1033 krb5_cc_cache_cursor cursor,
1034 krb5_ccache *id)
1035 {
1036 return cursor->ops->get_cache_next(context, cursor->cursor, id);
1037 }
1038
1039 /**
1040 * Destroy the cursor `cursor'.
1041 *
1042 * @return Return an error code or 0, see krb5_get_error_message().
1043 *
1044 * @ingroup krb5_ccache
1045 */
1046
1047
1048 krb5_error_code KRB5_LIB_FUNCTION
1049 krb5_cc_cache_end_seq_get (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
1050 krb5_cc_cache_cursor cursor)
1051 {
1052 krb5_error_code ret;
1053 ret = cursor->ops->end_cache_get(context, cursor->cursor);
1054 cursor->ops = NULL;
1055 free(cursor);
1056 return ret;
1057 }
1058
1059 /**
1060 * Search for a matching credential cache of type `type' that have the
1061 * `principal' as the default principal. On success, `id' needs to be
1062 * freed with krb5_cc_close() or krb5_cc_destroy().
1063 *
1064 * @return On failure, error code is returned and `id' is set to NULL.
1065 *
1066 * @ingroup krb5_ccache
1067 */
1068
1069
1070 krb5_error_code KRB5_LIB_FUNCTION
1071 krb5_cc_cache_match (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
1072 krb5_principal client,
1073 krb5_ccache *id)
1074 {
1075 krb5_cccol_cursor cursor;
1076 krb5_error_code ret;
1077 krb5_ccache cache = NULL;
1078
1079 *id = NULL;
1080
1081 ret = krb5_cccol_cursor_new (context, &cursor);
1082 if (ret)
1083 return ret;
1084
1085 while ((ret = krb5_cccol_cursor_next (context, cursor, &cache)) == 0) {
1086 krb5_principal principal;
1087
1088 ret = krb5_cc_get_principal(context, cache, &principal);
1089 if (ret == 0) {
1090 krb5_boolean match;
1091
1092 match = krb5_principal_compare(context, principal, client);
1093 krb5_free_principal(context, principal);
1094 if (match)
1095 break;
1096 }
1097
1098 krb5_cc_close(context, cache);
1099 cache = NULL;
1100 }
1101
1102 krb5_cccol_cursor_free(context, &cursor);
1103
1104 if (cache == NULL) {
1105 char *str;
1106
1107 krb5_unparse_name(context, client, &str);
1108
1109 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1110 N_("Principal %s not found in a "
1111 "credential cache", ""),
1112 str ? str : "<out of memory>");
1113 if (str)
1114 free(str);
1115 return KRB5_CC_NOTFOUND;
1116 }
1117 *id = cache;
1118
1119 return 0;
1120 }
1121
1122 /**
1123 * Move the content from one credential cache to another. The
1124 * operation is an atomic switch.
1125 *
1126 * @param context a Keberos context
1127 * @param from the credential cache to move the content from
1128 * @param to the credential cache to move the content to
1129
1130 * @return On sucess, from is freed. On failure, error code is
1131 * returned and from and to are both still allocated, see krb5_get_error_message().
1132 *
1133 * @ingroup krb5_ccache
1134 */
1135
1136 krb5_error_code
1137 krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
/* [<][>][^][v][top][bottom][index][help] */
1138 {
1139 krb5_error_code ret;
1140
1141 if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
1142 krb5_set_error_message(context, KRB5_CC_NOSUPP,
1143 N_("Moving credentials between diffrent "
1144 "types not yet supported", ""));
1145 return KRB5_CC_NOSUPP;
1146 }
1147
1148 ret = (*to->ops->move)(context, from, to);
1149 if (ret == 0) {
1150 memset(from, 0, sizeof(*from));
1151 free(from);
1152 }
1153 return ret;
1154 }
1155
1156 #define KRB5_CONF_NAME "krb5_ccache_conf_data"
1157 #define KRB5_REALM_NAME "X-CACHECONF:"
1158
1159 static krb5_error_code
1160 build_conf_principals(krb5_context context, krb5_ccache id,
/* [<][>][^][v][top][bottom][index][help] */
1161 krb5_const_principal principal,
1162 const char *name, krb5_creds *cred)
1163 {
1164 krb5_principal client;
1165 krb5_error_code ret;
1166 char *pname = NULL;
1167
1168 memset(cred, 0, sizeof(*cred));
1169
1170 ret = krb5_cc_get_principal(context, id, &client);
1171 if (ret)
1172 return ret;
1173
1174 if (principal) {
1175 ret = krb5_unparse_name(context, principal, &pname);
1176 if (ret)
1177 return ret;
1178 }
1179
1180 ret = krb5_make_principal(context, &cred->server,
1181 krb5_principal_get_realm(context, client),
1182 KRB5_CONF_NAME, name, pname, NULL);
1183 free(pname);
1184 if (ret) {
1185 krb5_free_principal(context, client);
1186 return ret;
1187 }
1188 ret = krb5_copy_principal(context, client, &cred->client);
1189 krb5_free_principal(context, client);
1190 return ret;
1191 }
1192
1193 /**
1194 * Return TRUE (non zero) if the principal is a configuration
1195 * principal (generated part of krb5_cc_set_config()). Returns FALSE
1196 * (zero) if not a configuration principal.
1197 *
1198 * @param context a Keberos context
1199 * @param principal principal to check if it a configuration principal
1200 *
1201 * @ingroup krb5_ccache
1202 */
1203
1204 krb5_boolean KRB5_LIB_FUNCTION
1205 krb5_is_config_principal(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
1206 krb5_const_principal principal)
1207 {
1208 if (strcmp(principal->realm, KRB5_REALM_NAME) != 0)
1209 return FALSE;
1210
1211 if (principal->name.name_string.len == 0 ||
1212 strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0)
1213 return FALSE;
1214
1215 return TRUE;
1216 }
1217
1218 /**
1219 * Store some configuration for the credential cache in the cache.
1220 * Existing configuration under the same name is over-written.
1221 *
1222 * @param context a Keberos context
1223 * @param id the credential cache to store the data for
1224 * @param principal configuration for a specific principal, if
1225 * NULL, global for the whole cache.
1226 * @param name name under which the configuraion is stored.
1227 * @param data data to store
1228 *
1229 * @ingroup krb5_ccache
1230 */
1231
1232 krb5_error_code KRB5_LIB_FUNCTION
1233 krb5_cc_set_config(krb5_context context, krb5_ccache id,
/* [<][>][^][v][top][bottom][index][help] */
1234 krb5_const_principal principal,
1235 const char *name, krb5_data *data)
1236 {
1237 krb5_error_code ret;
1238 krb5_creds cred;
1239
1240 ret = build_conf_principals(context, id, principal, name, &cred);
1241 if (ret)
1242 goto out;
1243
1244 /* Remove old configuration */
1245 ret = krb5_cc_remove_cred(context, id, 0, &cred);
1246 if (ret && ret != KRB5_CC_NOTFOUND)
1247 goto out;
1248
1249 /* not that anyone care when this expire */
1250 cred.times.authtime = time(NULL);
1251 cred.times.endtime = cred.times.authtime + 3600 * 24 * 30;
1252
1253 ret = krb5_data_copy(&cred.ticket, data->data, data->length);
1254 if (ret)
1255 goto out;
1256
1257 ret = krb5_cc_store_cred(context, id, &cred);
1258
1259 out:
1260 krb5_free_cred_contents (context, &cred);
1261 return ret;
1262 }
1263
1264 /**
1265 * Get some configuration for the credential cache in the cache.
1266 *
1267 * @param context a Keberos context
1268 * @param id the credential cache to store the data for
1269 * @param principal configuration for a specific principal, if
1270 * NULL, global for the whole cache.
1271 * @param name name under which the configuraion is stored.
1272 * @param data data to fetched, free with krb5_data_free()
1273 *
1274 * @ingroup krb5_ccache
1275 */
1276
1277
1278 krb5_error_code KRB5_LIB_FUNCTION
1279 krb5_cc_get_config(krb5_context context, krb5_ccache id,
/* [<][>][^][v][top][bottom][index][help] */
1280 krb5_const_principal principal,
1281 const char *name, krb5_data *data)
1282 {
1283 krb5_creds mcred, cred;
1284 krb5_error_code ret;
1285
1286 memset(&cred, 0, sizeof(cred));
1287 krb5_data_zero(data);
1288
1289 ret = build_conf_principals(context, id, principal, name, &mcred);
1290 if (ret)
1291 goto out;
1292
1293 ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
1294 if (ret)
1295 goto out;
1296
1297 ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length);
1298
1299 out:
1300 krb5_free_cred_contents (context, &cred);
1301 krb5_free_cred_contents (context, &mcred);
1302 return ret;
1303 }
1304
1305 /*
1306 *
1307 */
1308
1309 struct krb5_cccol_cursor {
1310 int idx;
1311 krb5_cc_cache_cursor cursor;
1312 };
1313
1314 /**
1315 * Get a new cache interation cursor that will interate over all
1316 * credentials caches independent of type.
1317 *
1318 * @param context a Keberos context
1319 * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
1320 *
1321 * @return Returns 0 or and error code, see krb5_get_error_message().
1322 *
1323 * @ingroup krb5_ccache
1324 */
1325
1326 krb5_error_code KRB5_LIB_FUNCTION
1327 krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor)
/* [<][>][^][v][top][bottom][index][help] */
1328 {
1329 *cursor = calloc(1, sizeof(**cursor));
1330 if (*cursor == NULL) {
1331 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1332 return ENOMEM;
1333 }
1334 (*cursor)->idx = 0;
1335 (*cursor)->cursor = NULL;
1336
1337 return 0;
1338 }
1339
1340 /**
1341 * Get next credential cache from the iteration.
1342 *
1343 * @param context A Kerberos 5 context
1344 * @param cursor the iteration cursor
1345 * @param cache the returned cursor, pointer is set to NULL on failure
1346 * and a cache on success. The returned cache needs to be freed
1347 * with krb5_cc_close() or destroyed with krb5_cc_destroy().
1348 *
1349 * @return Return 0 or and error, KRB5_CC_END is returned at the end
1350 * of iteration. See krb5_get_error_message().
1351 *
1352 * @ingroup krb5_ccache
1353 */
1354
1355
1356 krb5_error_code KRB5_LIB_FUNCTION
1357 krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor,
/* [<][>][^][v][top][bottom][index][help] */
1358 krb5_ccache *cache)
1359 {
1360 krb5_error_code ret;
1361
1362 *cache = NULL;
1363
1364 while (cursor->idx < context->num_cc_ops) {
1365
1366 if (cursor->cursor == NULL) {
1367 ret = krb5_cc_cache_get_first (context,
1368 context->cc_ops[cursor->idx].prefix,
1369 &cursor->cursor);
1370 if (ret) {
1371 cursor->idx++;
1372 continue;
1373 }
1374 }
1375 ret = krb5_cc_cache_next(context, cursor->cursor, cache);
1376 if (ret == 0)
1377 break;
1378
1379 krb5_cc_cache_end_seq_get(context, cursor->cursor);
1380 cursor->cursor = NULL;
1381 if (ret != KRB5_CC_END)
1382 break;
1383
1384 cursor->idx++;
1385 }
1386 if (cursor->idx >= context->num_cc_ops) {
1387 krb5_set_error_message(context, KRB5_CC_END,
1388 N_("Reached end of credential caches", ""));
1389 return KRB5_CC_END;
1390 }
1391
1392 return 0;
1393 }
1394
1395 /**
1396 * End an iteration and free all resources, can be done before end is reached.
1397 *
1398 * @param context A Kerberos 5 context
1399 * @param cursor the iteration cursor to be freed.
1400 *
1401 * @return Return 0 or and error, KRB5_CC_END is returned at the end
1402 * of iteration. See krb5_get_error_message().
1403 *
1404 * @ingroup krb5_ccache
1405 */
1406
1407 krb5_error_code KRB5_LIB_FUNCTION
1408 krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor)
/* [<][>][^][v][top][bottom][index][help] */
1409 {
1410 krb5_cccol_cursor c = *cursor;
1411
1412 *cursor = NULL;
1413 if (c) {
1414 if (c->cursor)
1415 krb5_cc_cache_end_seq_get(context, c->cursor);
1416 free(c);
1417 }
1418 return 0;
1419 }
1420
1421 /**
1422 * Return the last time the credential cache was modified.
1423 *
1424 * @param context A Kerberos 5 context
1425 * @param id The credential cache to probe
1426 * @param mtime the last modification time, set to 0 on error.
1427
1428 * @return Return 0 or and error. See krb5_get_error_message().
1429 *
1430 * @ingroup krb5_ccache
1431 */
1432
1433
1434 krb5_error_code KRB5_LIB_FUNCTION
1435 krb5_cc_last_change_time(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
1436 krb5_ccache id,
1437 krb5_timestamp *mtime)
1438 {
1439 *mtime = 0;
1440 return (*id->ops->lastchange)(context, id, mtime);
1441 }
1442
1443 /**
1444 * Return the last modfication time for a cache collection. The query
1445 * can be limited to a specific cache type. If the function return 0
1446 * and mtime is 0, there was no credentials in the caches.
1447 *
1448 * @param context A Kerberos 5 context
1449 * @param id The credential cache to probe
1450 * @param mtime the last modification time, set to 0 on error.
1451
1452 * @return Return 0 or and error. See krb5_get_error_message().
1453 *
1454 * @ingroup krb5_ccache
1455 */
1456
1457 krb5_error_code KRB5_LIB_FUNCTION
1458 krb5_cccol_last_change_time(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
1459 const char *type,
1460 krb5_timestamp *mtime)
1461 {
1462 krb5_cccol_cursor cursor;
1463 krb5_error_code ret;
1464 krb5_ccache id;
1465 krb5_timestamp t = 0;
1466
1467 *mtime = 0;
1468
1469 ret = krb5_cccol_cursor_new (context, &cursor);
1470 if (ret)
1471 return ret;
1472
1473 while ((ret = krb5_cccol_cursor_next (context, cursor, &id)) == 0) {
1474
1475 if (type && strcmp(krb5_cc_get_type(context, id), type) != 0)
1476 continue;
1477
1478 ret = krb5_cc_last_change_time(context, id, &t);
1479 krb5_cc_close(context, id);
1480 if (ret)
1481 continue;
1482 if (t > *mtime)
1483 *mtime = t;
1484 }
1485
1486 krb5_cccol_cursor_free(context, &cursor);
1487
1488 return 0;
1489 }