1 /* $NetBSD: cache.c,v 1.1.1.2 2014/04/24 12:45:49 pettai Exp $ */
2
3 /*
4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #include "krb5_locl.h"
39
40 /**
41 * @page krb5_ccache_intro The credential cache functions
42 * @section section_krb5_ccache Kerberos credential caches
43 *
44 * krb5_ccache structure holds a Kerberos credential cache.
45 *
46 * Heimdal support the follow types of credential caches:
47 *
48 * - SCC
49 * Store the credential in a database
50 * - FILE
51 * Store the credential in memory
52 * - MEMORY
53 * Store the credential in memory
54 * - API
55 * A credential cache server based solution for Mac OS X
56 * - KCM
57 * A credential cache server based solution for all platforms
58 *
59 * @subsection Example
60 *
61 * This is a minimalistic version of klist:
62 @code
63 #include <krb5/krb5.h>
64
65 int
66 main (int argc, char **argv)
67 {
68 krb5_context context;
69 krb5_cc_cursor cursor;
70 krb5_error_code ret;
71 krb5_ccache id;
72 krb5_creds creds;
73
74 if (krb5_init_context (&context) != 0)
75 errx(1, "krb5_context");
76
77 ret = krb5_cc_default (context, &id);
78 if (ret)
79 krb5_err(context, 1, ret, "krb5_cc_default");
80
81 ret = krb5_cc_start_seq_get(context, id, &cursor);
82 if (ret)
83 krb5_err(context, 1, ret, "krb5_cc_start_seq_get");
84
85 while((ret = krb5_cc_next_cred(context, id, &cursor, &creds)) == 0){
86 char *principal;
87
88 krb5_unparse_name(context, creds.server, &principal);
89 printf("principal: %s\\n", principal);
90 free(principal);
91 krb5_free_cred_contents (context, &creds);
92 }
93 ret = krb5_cc_end_seq_get(context, id, &cursor);
94 if (ret)
95 krb5_err(context, 1, ret, "krb5_cc_end_seq_get");
96
97 krb5_cc_close(context, id);
98
99 krb5_free_context(context);
100 return 0;
101 }
102 * @endcode
103 */
104
105 /**
106 * Add a new ccache type with operations `ops', overwriting any
107 * existing one if `override'.
108 *
109 * @param context a Keberos context
110 * @param ops type of plugin symbol
111 * @param override flag to select if the registration is to overide
112 * an existing ops with the same name.
113 *
114 * @return Return an error code or 0, see krb5_get_error_message().
115 *
116 * @ingroup krb5_ccache
117 */
118
119 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_register(krb5_context context,const krb5_cc_ops * ops,krb5_boolean override)120 krb5_cc_register(krb5_context context,
121 const krb5_cc_ops *ops,
122 krb5_boolean override)
123 {
124 int i;
125
126 for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
127 if(strcmp(context->cc_ops[i]->prefix, ops->prefix) == 0) {
128 if(!override) {
129 krb5_set_error_message(context,
130 KRB5_CC_TYPE_EXISTS,
131 N_("cache type %s already exists", "type"),
132 ops->prefix);
133 return KRB5_CC_TYPE_EXISTS;
134 }
135 break;
136 }
137 }
138 if(i == context->num_cc_ops) {
139 const krb5_cc_ops **o = realloc(rk_UNCONST(context->cc_ops),
140 (context->num_cc_ops + 1) *
141 sizeof(context->cc_ops[0]));
142 if(o == NULL) {
143 krb5_set_error_message(context, KRB5_CC_NOMEM,
144 N_("malloc: out of memory", ""));
145 return KRB5_CC_NOMEM;
146 }
147 context->cc_ops = o;
148 context->cc_ops[context->num_cc_ops] = NULL;
149 context->num_cc_ops++;
150 }
151 context->cc_ops[i] = ops;
152 return 0;
153 }
154
155 /*
156 * Allocate the memory for a `id' and the that function table to
157 * `ops'. Returns 0 or and error code.
158 */
159
160 krb5_error_code
_krb5_cc_allocate(krb5_context context,const krb5_cc_ops * ops,krb5_ccache * id)161 _krb5_cc_allocate(krb5_context context,
162 const krb5_cc_ops *ops,
163 krb5_ccache *id)
164 {
165 krb5_ccache p;
166
167 p = malloc (sizeof(*p));
168 if(p == NULL) {
169 krb5_set_error_message(context, KRB5_CC_NOMEM,
170 N_("malloc: out of memory", ""));
171 return KRB5_CC_NOMEM;
172 }
173 p->ops = ops;
174 *id = p;
175
176 return 0;
177 }
178
179 /*
180 * Allocate memory for a new ccache in `id' with operations `ops'
181 * and name `residual'. Return 0 or an error code.
182 */
183
184 static krb5_error_code
allocate_ccache(krb5_context context,const krb5_cc_ops * ops,const char * residual,krb5_ccache * id)185 allocate_ccache (krb5_context context,
186 const krb5_cc_ops *ops,
187 const char *residual,
188 krb5_ccache *id)
189 {
190 krb5_error_code ret;
191 #ifdef KRB5_USE_PATH_TOKENS
192 char * exp_residual = NULL;
193
194 ret = _krb5_expand_path_tokens(context, residual, &exp_residual);
195 if (ret)
196 return ret;
197
198 residual = exp_residual;
199 #endif
200
201 ret = _krb5_cc_allocate(context, ops, id);
202 if (ret) {
203 #ifdef KRB5_USE_PATH_TOKENS
204 if (exp_residual)
205 free(exp_residual);
206 #endif
207 return ret;
208 }
209
210 ret = (*id)->ops->resolve(context, id, residual);
211 if(ret) {
212 free(*id);
213 *id = NULL;
214 }
215
216 #ifdef KRB5_USE_PATH_TOKENS
217 if (exp_residual)
218 free(exp_residual);
219 #endif
220
221 return ret;
222 }
223
224 static int
is_possible_path_name(const char * name)225 is_possible_path_name(const char * name)
226 {
227 const char * colon;
228
229 if ((colon = strchr(name, ':')) == NULL)
230 return TRUE;
231
232 #ifdef _WIN32
233 /* <drive letter>:\path\to\cache ? */
234
235 if (colon == name + 1 &&
236 strchr(colon + 1, ':') == NULL)
237 return TRUE;
238 #endif
239
240 return FALSE;
241 }
242
243 /**
244 * Find and allocate a ccache in `id' from the specification in `residual'.
245 * If the ccache name doesn't contain any colon, interpret it as a file name.
246 *
247 * @param context a Keberos context.
248 * @param name string name of a credential cache.
249 * @param id return pointer to a found credential cache.
250 *
251 * @return Return 0 or an error code. In case of an error, id is set
252 * to NULL, see krb5_get_error_message().
253 *
254 * @ingroup krb5_ccache
255 */
256
257
258 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_resolve(krb5_context context,const char * name,krb5_ccache * id)259 krb5_cc_resolve(krb5_context context,
260 const char *name,
261 krb5_ccache *id)
262 {
263 int i;
264
265 *id = NULL;
266
267 for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
268 size_t prefix_len = strlen(context->cc_ops[i]->prefix);
269
270 if(strncmp(context->cc_ops[i]->prefix, name, prefix_len) == 0
271 && name[prefix_len] == ':') {
272 return allocate_ccache (context, context->cc_ops[i],
273 name + prefix_len + 1,
274 id);
275 }
276 }
277 if (is_possible_path_name(name))
278 return allocate_ccache (context, &krb5_fcc_ops, name, id);
279 else {
280 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
281 N_("unknown ccache type %s", "name"), name);
282 return KRB5_CC_UNKNOWN_TYPE;
283 }
284 }
285
286 /**
287 * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
288 * the library chooses the default credential cache type. The supplied
289 * `hint' (that can be NULL) is a string that the credential cache
290 * type can use to base the name of the credential on, this is to make
291 * it easier for the user to differentiate the credentials.
292 *
293 * @return Return an error code or 0, see krb5_get_error_message().
294 *
295 * @ingroup krb5_ccache
296 */
297
298 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_new_unique(krb5_context context,const char * type,const char * hint,krb5_ccache * id)299 krb5_cc_new_unique(krb5_context context, const char *type,
300 const char *hint, krb5_ccache *id)
301 {
302 const krb5_cc_ops *ops;
303 krb5_error_code ret;
304
305 ops = krb5_cc_get_prefix_ops(context, type);
306 if (ops == NULL) {
307 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
308 "Credential cache type %s is unknown", type);
309 return KRB5_CC_UNKNOWN_TYPE;
310 }
311
312 ret = _krb5_cc_allocate(context, ops, id);
313 if (ret)
314 return ret;
315 ret = (*id)->ops->gen_new(context, id);
316 if (ret) {
317 free(*id);
318 *id = NULL;
319 }
320 return ret;
321 }
322
323 /**
324 * Return the name of the ccache `id'
325 *
326 * @ingroup krb5_ccache
327 */
328
329
330 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_cc_get_name(krb5_context context,krb5_ccache id)331 krb5_cc_get_name(krb5_context context,
332 krb5_ccache id)
333 {
334 return id->ops->get_name(context, id);
335 }
336
337 /**
338 * Return the type of the ccache `id'.
339 *
340 * @ingroup krb5_ccache
341 */
342
343
344 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_cc_get_type(krb5_context context,krb5_ccache id)345 krb5_cc_get_type(krb5_context context,
346 krb5_ccache id)
347 {
348 return id->ops->prefix;
349 }
350
351 /**
352 * Return the complete resolvable name the cache
353
354 * @param context a Keberos context
355 * @param id return pointer to a found credential cache
356 * @param str the returned name of a credential cache, free with krb5_xfree()
357 *
358 * @return Returns 0 or an error (and then *str is set to NULL).
359 *
360 * @ingroup krb5_ccache
361 */
362
363
364 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_full_name(krb5_context context,krb5_ccache id,char ** str)365 krb5_cc_get_full_name(krb5_context context,
366 krb5_ccache id,
367 char **str)
368 {
369 const char *type, *name;
370
371 *str = NULL;
372
373 type = krb5_cc_get_type(context, id);
374 if (type == NULL) {
375 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
376 "cache have no name of type");
377 return KRB5_CC_UNKNOWN_TYPE;
378 }
379
380 name = krb5_cc_get_name(context, id);
381 if (name == NULL) {
382 krb5_set_error_message(context, KRB5_CC_BADNAME,
383 "cache of type %s have no name", type);
384 return KRB5_CC_BADNAME;
385 }
386
387 if (asprintf(str, "%s:%s", type, name) == -1) {
388 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
389 *str = NULL;
390 return ENOMEM;
391 }
392 return 0;
393 }
394
395 /**
396 * Return krb5_cc_ops of a the ccache `id'.
397 *
398 * @ingroup krb5_ccache
399 */
400
401
402 KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL
krb5_cc_get_ops(krb5_context context,krb5_ccache id)403 krb5_cc_get_ops(krb5_context context, krb5_ccache id)
404 {
405 return id->ops;
406 }
407
408 /*
409 * Expand variables in `str' into `res'
410 */
411
412 krb5_error_code
_krb5_expand_default_cc_name(krb5_context context,const char * str,char ** res)413 _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
414 {
415 return _krb5_expand_path_tokens(context, str, res);
416 }
417
418 /*
419 * Return non-zero if envirnoment that will determine default krb5cc
420 * name has changed.
421 */
422
423 static int
environment_changed(krb5_context context)424 environment_changed(krb5_context context)
425 {
426 const char *e;
427
428 /* if the cc name was set, don't change it */
429 if (context->default_cc_name_set)
430 return 0;
431
432 /* XXX performance: always ask KCM/API if default name has changed */
433 if (context->default_cc_name &&
434 (strncmp(context->default_cc_name, "KCM:", 4) == 0 ||
435 strncmp(context->default_cc_name, "API:", 4) == 0))
436 return 1;
437
438 if(issuid())
439 return 0;
440
441 e = getenv("KRB5CCNAME");
442 if (e == NULL) {
443 if (context->default_cc_name_env) {
444 free(context->default_cc_name_env);
445 context->default_cc_name_env = NULL;
446 return 1;
447 }
448 } else {
449 if (context->default_cc_name_env == NULL)
450 return 1;
451 if (strcmp(e, context->default_cc_name_env) != 0)
452 return 1;
453 }
454 return 0;
455 }
456
457 /**
458 * Switch the default default credential cache for a specific
459 * credcache type (and name for some implementations).
460 *
461 * @return Return an error code or 0, see krb5_get_error_message().
462 *
463 * @ingroup krb5_ccache
464 */
465
466 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_switch(krb5_context context,krb5_ccache id)467 krb5_cc_switch(krb5_context context, krb5_ccache id)
468 {
469 #ifdef _WIN32
470 _krb5_set_default_cc_name_to_registry(context, id);
471 #endif
472
473 if (id->ops->set_default == NULL)
474 return 0;
475
476 return (*id->ops->set_default)(context, id);
477 }
478
479 /**
480 * Return true if the default credential cache support switch
481 *
482 * @ingroup krb5_ccache
483 */
484
485 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_cc_support_switch(krb5_context context,const char * type)486 krb5_cc_support_switch(krb5_context context, const char *type)
487 {
488 const krb5_cc_ops *ops;
489
490 ops = krb5_cc_get_prefix_ops(context, type);
491 if (ops && ops->set_default)
492 return 1;
493 return FALSE;
494 }
495
496 /**
497 * Set the default cc name for `context' to `name'.
498 *
499 * @ingroup krb5_ccache
500 */
501
502 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_default_name(krb5_context context,const char * name)503 krb5_cc_set_default_name(krb5_context context, const char *name)
504 {
505 krb5_error_code ret = 0;
506 char *p = NULL, *exp_p = NULL;
507
508 if (name == NULL) {
509 const char *e = NULL;
510
511 if(!issuid()) {
512 e = getenv("KRB5CCNAME");
513 if (e) {
514 p = strdup(e);
515 if (context->default_cc_name_env)
516 free(context->default_cc_name_env);
517 context->default_cc_name_env = strdup(e);
518 }
519 }
520
521 #ifdef _WIN32
522 if (e == NULL) {
523 e = p = _krb5_get_default_cc_name_from_registry(context);
524 }
525 #endif
526 if (e == NULL) {
527 e = krb5_config_get_string(context, NULL, "libdefaults",
528 "default_cc_name", NULL);
529 if (e) {
530 ret = _krb5_expand_default_cc_name(context, e, &p);
531 if (ret)
532 return ret;
533 }
534 if (e == NULL) {
535 const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
536 e = krb5_config_get_string(context, NULL, "libdefaults",
537 "default_cc_type", NULL);
538 if (e) {
539 ops = krb5_cc_get_prefix_ops(context, e);
540 if (ops == NULL) {
541 krb5_set_error_message(context,
542 KRB5_CC_UNKNOWN_TYPE,
543 "Credential cache type %s "
544 "is unknown", e);
545 return KRB5_CC_UNKNOWN_TYPE;
546 }
547 }
548 ret = (*ops->get_default_name)(context, &p);
549 if (ret)
550 return ret;
551 }
552 }
553 context->default_cc_name_set = 0;
554 } else {
555 p = strdup(name);
556 context->default_cc_name_set = 1;
557 }
558
559 if (p == NULL) {
560 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
561 return ENOMEM;
562 }
563
564 ret = _krb5_expand_path_tokens(context, p, &exp_p);
565 free(p);
566 if (ret)
567 return ret;
568
569 if (context->default_cc_name)
570 free(context->default_cc_name);
571
572 context->default_cc_name = exp_p;
573
574 return 0;
575 }
576
577 /**
578 * Return a pointer to a context static string containing the default
579 * ccache name.
580 *
581 * @return String to the default credential cache name.
582 *
583 * @ingroup krb5_ccache
584 */
585
586
587 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_cc_default_name(krb5_context context)588 krb5_cc_default_name(krb5_context context)
589 {
590 if (context->default_cc_name == NULL || environment_changed(context))
591 krb5_cc_set_default_name(context, NULL);
592
593 return context->default_cc_name;
594 }
595
596 /**
597 * Open the default ccache in `id'.
598 *
599 * @return Return an error code or 0, see krb5_get_error_message().
600 *
601 * @ingroup krb5_ccache
602 */
603
604
605 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_default(krb5_context context,krb5_ccache * id)606 krb5_cc_default(krb5_context context,
607 krb5_ccache *id)
608 {
609 const char *p = krb5_cc_default_name(context);
610
611 if (p == NULL) {
612 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
613 return ENOMEM;
614 }
615 return krb5_cc_resolve(context, p, id);
616 }
617
618 /**
619 * Create a new ccache in `id' for `primary_principal'.
620 *
621 * @return Return an error code or 0, see krb5_get_error_message().
622 *
623 * @ingroup krb5_ccache
624 */
625
626
627 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_initialize(krb5_context context,krb5_ccache id,krb5_principal primary_principal)628 krb5_cc_initialize(krb5_context context,
629 krb5_ccache id,
630 krb5_principal primary_principal)
631 {
632 return (*id->ops->init)(context, id, primary_principal);
633 }
634
635
636 /**
637 * Remove the ccache `id'.
638 *
639 * @return Return an error code or 0, see krb5_get_error_message().
640 *
641 * @ingroup krb5_ccache
642 */
643
644
645 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_destroy(krb5_context context,krb5_ccache id)646 krb5_cc_destroy(krb5_context context,
647 krb5_ccache id)
648 {
649 krb5_error_code ret;
650
651 ret = (*id->ops->destroy)(context, id);
652 krb5_cc_close (context, id);
653 return ret;
654 }
655
656 /**
657 * Stop using the ccache `id' and free the related resources.
658 *
659 * @return Return an error code or 0, see krb5_get_error_message().
660 *
661 * @ingroup krb5_ccache
662 */
663
664
665 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_close(krb5_context context,krb5_ccache id)666 krb5_cc_close(krb5_context context,
667 krb5_ccache id)
668 {
669 krb5_error_code ret;
670 ret = (*id->ops->close)(context, id);
671 free(id);
672 return ret;
673 }
674
675 /**
676 * Store `creds' in the ccache `id'.
677 *
678 * @return Return an error code or 0, see krb5_get_error_message().
679 *
680 * @ingroup krb5_ccache
681 */
682
683
684 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_store_cred(krb5_context context,krb5_ccache id,krb5_creds * creds)685 krb5_cc_store_cred(krb5_context context,
686 krb5_ccache id,
687 krb5_creds *creds)
688 {
689 return (*id->ops->store)(context, id, creds);
690 }
691
692 /**
693 * Retrieve the credential identified by `mcreds' (and `whichfields')
694 * from `id' in `creds'. 'creds' must be free by the caller using
695 * krb5_free_cred_contents.
696 *
697 * @param context A Kerberos 5 context
698 * @param id a Kerberos 5 credential cache
699 * @param whichfields what fields to use for matching credentials, same
700 * flags as whichfields in krb5_compare_creds()
701 * @param mcreds template credential to use for comparing
702 * @param creds returned credential, free with krb5_free_cred_contents()
703 *
704 * @return Return an error code or 0, see krb5_get_error_message().
705 *
706 * @ingroup krb5_ccache
707 */
708
709
710 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_retrieve_cred(krb5_context context,krb5_ccache id,krb5_flags whichfields,const krb5_creds * mcreds,krb5_creds * creds)711 krb5_cc_retrieve_cred(krb5_context context,
712 krb5_ccache id,
713 krb5_flags whichfields,
714 const krb5_creds *mcreds,
715 krb5_creds *creds)
716 {
717 krb5_error_code ret;
718 krb5_cc_cursor cursor;
719
720 if (id->ops->retrieve != NULL) {
721 return (*id->ops->retrieve)(context, id, whichfields,
722 mcreds, creds);
723 }
724
725 ret = krb5_cc_start_seq_get(context, id, &cursor);
726 if (ret)
727 return ret;
728 while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
729 if(krb5_compare_creds(context, whichfields, mcreds, creds)){
730 ret = 0;
731 break;
732 }
733 krb5_free_cred_contents (context, creds);
734 }
735 krb5_cc_end_seq_get(context, id, &cursor);
736 return ret;
737 }
738
739 /**
740 * Return the principal of `id' in `principal'.
741 *
742 * @return Return an error code or 0, see krb5_get_error_message().
743 *
744 * @ingroup krb5_ccache
745 */
746
747
748 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * principal)749 krb5_cc_get_principal(krb5_context context,
750 krb5_ccache id,
751 krb5_principal *principal)
752 {
753 return (*id->ops->get_princ)(context, id, principal);
754 }
755
756 /**
757 * Start iterating over `id', `cursor' is initialized to the
758 * beginning. Caller must free the cursor with krb5_cc_end_seq_get().
759 *
760 * @return Return an error code or 0, see krb5_get_error_message().
761 *
762 * @ingroup krb5_ccache
763 */
764
765
766 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_start_seq_get(krb5_context context,const krb5_ccache id,krb5_cc_cursor * cursor)767 krb5_cc_start_seq_get (krb5_context context,
768 const krb5_ccache id,
769 krb5_cc_cursor *cursor)
770 {
771 return (*id->ops->get_first)(context, id, cursor);
772 }
773
774 /**
775 * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
776 * and advance `cursor'.
777 *
778 * @return Return an error code or 0, see krb5_get_error_message().
779 *
780 * @ingroup krb5_ccache
781 */
782
783
784 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_next_cred(krb5_context context,const krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)785 krb5_cc_next_cred (krb5_context context,
786 const krb5_ccache id,
787 krb5_cc_cursor *cursor,
788 krb5_creds *creds)
789 {
790 return (*id->ops->get_next)(context, id, cursor, creds);
791 }
792
793 /**
794 * Destroy the cursor `cursor'.
795 *
796 * @ingroup krb5_ccache
797 */
798
799
800 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_end_seq_get(krb5_context context,const krb5_ccache id,krb5_cc_cursor * cursor)801 krb5_cc_end_seq_get (krb5_context context,
802 const krb5_ccache id,
803 krb5_cc_cursor *cursor)
804 {
805 return (*id->ops->end_get)(context, id, cursor);
806 }
807
808 /**
809 * Remove the credential identified by `cred', `which' from `id'.
810 *
811 * @ingroup krb5_ccache
812 */
813
814
815 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_remove_cred(krb5_context context,krb5_ccache id,krb5_flags which,krb5_creds * cred)816 krb5_cc_remove_cred(krb5_context context,
817 krb5_ccache id,
818 krb5_flags which,
819 krb5_creds *cred)
820 {
821 if(id->ops->remove_cred == NULL) {
822 krb5_set_error_message(context,
823 EACCES,
824 "ccache %s does not support remove_cred",
825 id->ops->prefix);
826 return EACCES; /* XXX */
827 }
828 return (*id->ops->remove_cred)(context, id, which, cred);
829 }
830
831 /**
832 * Set the flags of `id' to `flags'.
833 *
834 * @ingroup krb5_ccache
835 */
836
837
838 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)839 krb5_cc_set_flags(krb5_context context,
840 krb5_ccache id,
841 krb5_flags flags)
842 {
843 return (*id->ops->set_flags)(context, id, flags);
844 }
845
846 /**
847 * Get the flags of `id', store them in `flags'.
848 *
849 * @ingroup krb5_ccache
850 */
851
852 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_flags(krb5_context context,krb5_ccache id,krb5_flags * flags)853 krb5_cc_get_flags(krb5_context context,
854 krb5_ccache id,
855 krb5_flags *flags)
856 {
857 *flags = 0;
858 return 0;
859 }
860
861 /**
862 * Copy the contents of `from' to `to' if the given match function
863 * return true.
864 *
865 * @param context A Kerberos 5 context.
866 * @param from the cache to copy data from.
867 * @param to the cache to copy data to.
868 * @param match a match function that should return TRUE if cred argument should be copied, if NULL, all credentials are copied.
869 * @param matchctx context passed to match function.
870 * @param matched set to true if there was a credential that matched, may be NULL.
871 *
872 * @return Return an error code or 0, see krb5_get_error_message().
873 *
874 * @ingroup krb5_ccache
875 */
876
877 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_copy_match_f(krb5_context context,const krb5_ccache from,krb5_ccache to,krb5_boolean (* match)(krb5_context,void *,const krb5_creds *),void * matchctx,unsigned int * matched)878 krb5_cc_copy_match_f(krb5_context context,
879 const krb5_ccache from,
880 krb5_ccache to,
881 krb5_boolean (*match)(krb5_context, void *, const krb5_creds *),
882 void *matchctx,
883 unsigned int *matched)
884 {
885 krb5_error_code ret;
886 krb5_cc_cursor cursor;
887 krb5_creds cred;
888 krb5_principal princ;
889
890 if (matched)
891 *matched = 0;
892
893 ret = krb5_cc_get_principal(context, from, &princ);
894 if (ret)
895 return ret;
896 ret = krb5_cc_initialize(context, to, princ);
897 if (ret) {
898 krb5_free_principal(context, princ);
899 return ret;
900 }
901 ret = krb5_cc_start_seq_get(context, from, &cursor);
902 if (ret) {
903 krb5_free_principal(context, princ);
904 return ret;
905 }
906
907 while ((ret = krb5_cc_next_cred(context, from, &cursor, &cred)) == 0) {
908 if (match == NULL || (*match)(context, matchctx, &cred) == 0) {
909 if (matched)
910 (*matched)++;
911 ret = krb5_cc_store_cred(context, to, &cred);
912 if (ret)
913 break;
914 }
915 krb5_free_cred_contents(context, &cred);
916 }
917 krb5_cc_end_seq_get(context, from, &cursor);
918 krb5_free_principal(context, princ);
919 if (ret == KRB5_CC_END)
920 ret = 0;
921 return ret;
922 }
923
924 /**
925 * Just like krb5_cc_copy_match_f(), but copy everything.
926 *
927 * @ingroup @krb5_ccache
928 */
929
930 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_copy_cache(krb5_context context,const krb5_ccache from,krb5_ccache to)931 krb5_cc_copy_cache(krb5_context context,
932 const krb5_ccache from,
933 krb5_ccache to)
934 {
935 return krb5_cc_copy_match_f(context, from, to, NULL, NULL, NULL);
936 }
937
938 /**
939 * Return the version of `id'.
940 *
941 * @ingroup krb5_ccache
942 */
943
944
945 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_version(krb5_context context,const krb5_ccache id)946 krb5_cc_get_version(krb5_context context,
947 const krb5_ccache id)
948 {
949 if(id->ops->get_version)
950 return (*id->ops->get_version)(context, id);
951 else
952 return 0;
953 }
954
955 /**
956 * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
957 *
958 * @ingroup krb5_ccache
959 */
960
961
962 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_cc_clear_mcred(krb5_creds * mcred)963 krb5_cc_clear_mcred(krb5_creds *mcred)
964 {
965 memset(mcred, 0, sizeof(*mcred));
966 }
967
968 /**
969 * Get the cc ops that is registered in `context' to handle the
970 * prefix. prefix can be a complete credential cache name or a
971 * prefix, the function will only use part up to the first colon (:)
972 * if there is one. If prefix the argument is NULL, the default ccache
973 * implemtation is returned.
974 *
975 * @return Returns NULL if ops not found.
976 *
977 * @ingroup krb5_ccache
978 */
979
980
981 KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL
krb5_cc_get_prefix_ops(krb5_context context,const char * prefix)982 krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
983 {
984 char *p, *p1;
985 int i;
986
987 if (prefix == NULL)
988 return KRB5_DEFAULT_CCTYPE;
989 if (prefix[0] == '/')
990 return &krb5_fcc_ops;
991
992 p = strdup(prefix);
993 if (p == NULL) {
994 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
995 return NULL;
996 }
997 p1 = strchr(p, ':');
998 if (p1)
999 *p1 = '\0';
1000
1001 for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
1002 if(strcmp(context->cc_ops[i]->prefix, p) == 0) {
1003 free(p);
1004 return context->cc_ops[i];
1005 }
1006 }
1007 free(p);
1008 return NULL;
1009 }
1010
1011 struct krb5_cc_cache_cursor_data {
1012 const krb5_cc_ops *ops;
1013 krb5_cc_cursor cursor;
1014 };
1015
1016 /**
1017 * Start iterating over all caches of specified type. See also
1018 * krb5_cccol_cursor_new().
1019
1020 * @param context A Kerberos 5 context
1021 * @param type optional type to iterate over, if NULL, the default cache is used.
1022 * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get().
1023 *
1024 * @return Return an error code or 0, see krb5_get_error_message().
1025 *
1026 * @ingroup krb5_ccache
1027 */
1028
1029
1030 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_get_first(krb5_context context,const char * type,krb5_cc_cache_cursor * cursor)1031 krb5_cc_cache_get_first (krb5_context context,
1032 const char *type,
1033 krb5_cc_cache_cursor *cursor)
1034 {
1035 const krb5_cc_ops *ops;
1036 krb5_error_code ret;
1037
1038 if (type == NULL)
1039 type = krb5_cc_default_name(context);
1040
1041 ops = krb5_cc_get_prefix_ops(context, type);
1042 if (ops == NULL) {
1043 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
1044 "Unknown type \"%s\" when iterating "
1045 "trying to iterate the credential caches", type);
1046 return KRB5_CC_UNKNOWN_TYPE;
1047 }
1048
1049 if (ops->get_cache_first == NULL) {
1050 krb5_set_error_message(context, KRB5_CC_NOSUPP,
1051 N_("Credential cache type %s doesn't support "
1052 "iterations over caches", "type"),
1053 ops->prefix);
1054 return KRB5_CC_NOSUPP;
1055 }
1056
1057 *cursor = calloc(1, sizeof(**cursor));
1058 if (*cursor == NULL) {
1059 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1060 return ENOMEM;
1061 }
1062
1063 (*cursor)->ops = ops;
1064
1065 ret = ops->get_cache_first(context, &(*cursor)->cursor);
1066 if (ret) {
1067 free(*cursor);
1068 *cursor = NULL;
1069 }
1070 return ret;
1071 }
1072
1073 /**
1074 * Retrieve the next cache pointed to by (`cursor') in `id'
1075 * and advance `cursor'.
1076 *
1077 * @param context A Kerberos 5 context
1078 * @param cursor the iterator cursor, returned by krb5_cc_cache_get_first()
1079 * @param id next ccache
1080 *
1081 * @return Return 0 or an error code. Returns KRB5_CC_END when the end
1082 * of caches is reached, see krb5_get_error_message().
1083 *
1084 * @ingroup krb5_ccache
1085 */
1086
1087
1088 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_next(krb5_context context,krb5_cc_cache_cursor cursor,krb5_ccache * id)1089 krb5_cc_cache_next (krb5_context context,
1090 krb5_cc_cache_cursor cursor,
1091 krb5_ccache *id)
1092 {
1093 return cursor->ops->get_cache_next(context, cursor->cursor, id);
1094 }
1095
1096 /**
1097 * Destroy the cursor `cursor'.
1098 *
1099 * @return Return an error code or 0, see krb5_get_error_message().
1100 *
1101 * @ingroup krb5_ccache
1102 */
1103
1104
1105 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_end_seq_get(krb5_context context,krb5_cc_cache_cursor cursor)1106 krb5_cc_cache_end_seq_get (krb5_context context,
1107 krb5_cc_cache_cursor cursor)
1108 {
1109 krb5_error_code ret;
1110 ret = cursor->ops->end_cache_get(context, cursor->cursor);
1111 cursor->ops = NULL;
1112 free(cursor);
1113 return ret;
1114 }
1115
1116 /**
1117 * Search for a matching credential cache that have the
1118 * `principal' as the default principal. On success, `id' needs to be
1119 * freed with krb5_cc_close() or krb5_cc_destroy().
1120 *
1121 * @param context A Kerberos 5 context
1122 * @param client The principal to search for
1123 * @param id the returned credential cache
1124 *
1125 * @return On failure, error code is returned and `id' is set to NULL.
1126 *
1127 * @ingroup krb5_ccache
1128 */
1129
1130
1131 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_match(krb5_context context,krb5_principal client,krb5_ccache * id)1132 krb5_cc_cache_match (krb5_context context,
1133 krb5_principal client,
1134 krb5_ccache *id)
1135 {
1136 krb5_cccol_cursor cursor;
1137 krb5_error_code ret;
1138 krb5_ccache cache = NULL;
1139
1140 *id = NULL;
1141
1142 ret = krb5_cccol_cursor_new (context, &cursor);
1143 if (ret)
1144 return ret;
1145
1146 while (krb5_cccol_cursor_next (context, cursor, &cache) == 0 && cache != NULL) {
1147 krb5_principal principal;
1148
1149 ret = krb5_cc_get_principal(context, cache, &principal);
1150 if (ret == 0) {
1151 krb5_boolean match;
1152
1153 match = krb5_principal_compare(context, principal, client);
1154 krb5_free_principal(context, principal);
1155 if (match)
1156 break;
1157 }
1158
1159 krb5_cc_close(context, cache);
1160 cache = NULL;
1161 }
1162
1163 krb5_cccol_cursor_free(context, &cursor);
1164
1165 if (cache == NULL) {
1166 char *str;
1167
1168 krb5_unparse_name(context, client, &str);
1169
1170 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1171 N_("Principal %s not found in any "
1172 "credential cache", ""),
1173 str ? str : "<out of memory>");
1174 if (str)
1175 free(str);
1176 return KRB5_CC_NOTFOUND;
1177 }
1178 *id = cache;
1179
1180 return 0;
1181 }
1182
1183 /**
1184 * Move the content from one credential cache to another. The
1185 * operation is an atomic switch.
1186 *
1187 * @param context a Keberos context
1188 * @param from the credential cache to move the content from
1189 * @param to the credential cache to move the content to
1190
1191 * @return On sucess, from is freed. On failure, error code is
1192 * returned and from and to are both still allocated, see krb5_get_error_message().
1193 *
1194 * @ingroup krb5_ccache
1195 */
1196
1197 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_move(krb5_context context,krb5_ccache from,krb5_ccache to)1198 krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1199 {
1200 krb5_error_code ret;
1201
1202 if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
1203 krb5_set_error_message(context, KRB5_CC_NOSUPP,
1204 N_("Moving credentials between diffrent "
1205 "types not yet supported", ""));
1206 return KRB5_CC_NOSUPP;
1207 }
1208
1209 ret = (*to->ops->move)(context, from, to);
1210 if (ret == 0) {
1211 memset(from, 0, sizeof(*from));
1212 free(from);
1213 }
1214 return ret;
1215 }
1216
1217 #define KRB5_CONF_NAME "krb5_ccache_conf_data"
1218 #define KRB5_REALM_NAME "X-CACHECONF:"
1219
1220 static krb5_error_code
build_conf_principals(krb5_context context,krb5_ccache id,krb5_const_principal principal,const char * name,krb5_creds * cred)1221 build_conf_principals(krb5_context context, krb5_ccache id,
1222 krb5_const_principal principal,
1223 const char *name, krb5_creds *cred)
1224 {
1225 krb5_principal client;
1226 krb5_error_code ret;
1227 char *pname = NULL;
1228
1229 memset(cred, 0, sizeof(*cred));
1230
1231 ret = krb5_cc_get_principal(context, id, &client);
1232 if (ret)
1233 return ret;
1234
1235 if (principal) {
1236 ret = krb5_unparse_name(context, principal, &pname);
1237 if (ret)
1238 return ret;
1239 }
1240
1241 ret = krb5_make_principal(context, &cred->server,
1242 KRB5_REALM_NAME,
1243 KRB5_CONF_NAME, name, pname, NULL);
1244 free(pname);
1245 if (ret) {
1246 krb5_free_principal(context, client);
1247 return ret;
1248 }
1249 ret = krb5_copy_principal(context, client, &cred->client);
1250 krb5_free_principal(context, client);
1251 return ret;
1252 }
1253
1254 /**
1255 * Return TRUE (non zero) if the principal is a configuration
1256 * principal (generated part of krb5_cc_set_config()). Returns FALSE
1257 * (zero) if not a configuration principal.
1258 *
1259 * @param context a Keberos context
1260 * @param principal principal to check if it a configuration principal
1261 *
1262 * @ingroup krb5_ccache
1263 */
1264
1265 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_is_config_principal(krb5_context context,krb5_const_principal principal)1266 krb5_is_config_principal(krb5_context context,
1267 krb5_const_principal principal)
1268 {
1269 if (strcmp(principal->realm, KRB5_REALM_NAME) != 0)
1270 return FALSE;
1271
1272 if (principal->name.name_string.len == 0 ||
1273 strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0)
1274 return FALSE;
1275
1276 return TRUE;
1277 }
1278
1279 /**
1280 * Store some configuration for the credential cache in the cache.
1281 * Existing configuration under the same name is over-written.
1282 *
1283 * @param context a Keberos context
1284 * @param id the credential cache to store the data for
1285 * @param principal configuration for a specific principal, if
1286 * NULL, global for the whole cache.
1287 * @param name name under which the configuraion is stored.
1288 * @param data data to store, if NULL, configure is removed.
1289 *
1290 * @ingroup krb5_ccache
1291 */
1292
1293 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_config(krb5_context context,krb5_ccache id,krb5_const_principal principal,const char * name,krb5_data * data)1294 krb5_cc_set_config(krb5_context context, krb5_ccache id,
1295 krb5_const_principal principal,
1296 const char *name, krb5_data *data)
1297 {
1298 krb5_error_code ret;
1299 krb5_creds cred;
1300
1301 ret = build_conf_principals(context, id, principal, name, &cred);
1302 if (ret)
1303 goto out;
1304
1305 /* Remove old configuration */
1306 ret = krb5_cc_remove_cred(context, id, 0, &cred);
1307 if (ret && ret != KRB5_CC_NOTFOUND)
1308 goto out;
1309
1310 if (data) {
1311 /* not that anyone care when this expire */
1312 cred.times.authtime = time(NULL);
1313 cred.times.endtime = cred.times.authtime + 3600 * 24 * 30;
1314
1315 ret = krb5_data_copy(&cred.ticket, data->data, data->length);
1316 if (ret)
1317 goto out;
1318
1319 ret = krb5_cc_store_cred(context, id, &cred);
1320 }
1321
1322 out:
1323 krb5_free_cred_contents (context, &cred);
1324 return ret;
1325 }
1326
1327 /**
1328 * Get some configuration for the credential cache in the cache.
1329 *
1330 * @param context a Keberos context
1331 * @param id the credential cache to store the data for
1332 * @param principal configuration for a specific principal, if
1333 * NULL, global for the whole cache.
1334 * @param name name under which the configuraion is stored.
1335 * @param data data to fetched, free with krb5_data_free()
1336 *
1337 * @ingroup krb5_ccache
1338 */
1339
1340
1341 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_config(krb5_context context,krb5_ccache id,krb5_const_principal principal,const char * name,krb5_data * data)1342 krb5_cc_get_config(krb5_context context, krb5_ccache id,
1343 krb5_const_principal principal,
1344 const char *name, krb5_data *data)
1345 {
1346 krb5_creds mcred, cred;
1347 krb5_error_code ret;
1348
1349 memset(&cred, 0, sizeof(cred));
1350 krb5_data_zero(data);
1351
1352 ret = build_conf_principals(context, id, principal, name, &mcred);
1353 if (ret)
1354 goto out;
1355
1356 ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
1357 if (ret)
1358 goto out;
1359
1360 ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length);
1361
1362 out:
1363 krb5_free_cred_contents (context, &cred);
1364 krb5_free_cred_contents (context, &mcred);
1365 return ret;
1366 }
1367
1368 /*
1369 *
1370 */
1371
1372 struct krb5_cccol_cursor_data {
1373 int idx;
1374 krb5_cc_cache_cursor cursor;
1375 };
1376
1377 /**
1378 * Get a new cache interation cursor that will interate over all
1379 * credentials caches independent of type.
1380 *
1381 * @param context a Keberos context
1382 * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
1383 *
1384 * @return Returns 0 or and error code, see krb5_get_error_message().
1385 *
1386 * @ingroup krb5_ccache
1387 */
1388
1389 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_cursor_new(krb5_context context,krb5_cccol_cursor * cursor)1390 krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor)
1391 {
1392 *cursor = calloc(1, sizeof(**cursor));
1393 if (*cursor == NULL) {
1394 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1395 return ENOMEM;
1396 }
1397 (*cursor)->idx = 0;
1398 (*cursor)->cursor = NULL;
1399
1400 return 0;
1401 }
1402
1403 /**
1404 * Get next credential cache from the iteration.
1405 *
1406 * @param context A Kerberos 5 context
1407 * @param cursor the iteration cursor
1408 * @param cache the returned cursor, pointer is set to NULL on failure
1409 * and a cache on success. The returned cache needs to be freed
1410 * with krb5_cc_close() or destroyed with krb5_cc_destroy().
1411 * MIT Kerberos behavies slightly diffrent and sets cache to NULL
1412 * when all caches are iterated over and return 0.
1413 *
1414 * @return Return 0 or and error, KRB5_CC_END is returned at the end
1415 * of iteration. See krb5_get_error_message().
1416 *
1417 * @ingroup krb5_ccache
1418 */
1419
1420
1421 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_cursor_next(krb5_context context,krb5_cccol_cursor cursor,krb5_ccache * cache)1422 krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor,
1423 krb5_ccache *cache)
1424 {
1425 krb5_error_code ret;
1426
1427 *cache = NULL;
1428
1429 while (cursor->idx < context->num_cc_ops) {
1430
1431 if (cursor->cursor == NULL) {
1432 ret = krb5_cc_cache_get_first (context,
1433 context->cc_ops[cursor->idx]->prefix,
1434 &cursor->cursor);
1435 if (ret) {
1436 cursor->idx++;
1437 continue;
1438 }
1439 }
1440 ret = krb5_cc_cache_next(context, cursor->cursor, cache);
1441 if (ret == 0)
1442 break;
1443
1444 krb5_cc_cache_end_seq_get(context, cursor->cursor);
1445 cursor->cursor = NULL;
1446 if (ret != KRB5_CC_END)
1447 break;
1448
1449 cursor->idx++;
1450 }
1451 if (cursor->idx >= context->num_cc_ops) {
1452 krb5_set_error_message(context, KRB5_CC_END,
1453 N_("Reached end of credential caches", ""));
1454 return KRB5_CC_END;
1455 }
1456
1457 return 0;
1458 }
1459
1460 /**
1461 * End an iteration and free all resources, can be done before end is reached.
1462 *
1463 * @param context A Kerberos 5 context
1464 * @param cursor the iteration cursor to be freed.
1465 *
1466 * @return Return 0 or and error, KRB5_CC_END is returned at the end
1467 * of iteration. See krb5_get_error_message().
1468 *
1469 * @ingroup krb5_ccache
1470 */
1471
1472 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_cursor_free(krb5_context context,krb5_cccol_cursor * cursor)1473 krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor)
1474 {
1475 krb5_cccol_cursor c = *cursor;
1476
1477 *cursor = NULL;
1478 if (c) {
1479 if (c->cursor)
1480 krb5_cc_cache_end_seq_get(context, c->cursor);
1481 free(c);
1482 }
1483 return 0;
1484 }
1485
1486 /**
1487 * Return the last time the credential cache was modified.
1488 *
1489 * @param context A Kerberos 5 context
1490 * @param id The credential cache to probe
1491 * @param mtime the last modification time, set to 0 on error.
1492
1493 * @return Return 0 or and error. See krb5_get_error_message().
1494 *
1495 * @ingroup krb5_ccache
1496 */
1497
1498
1499 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_last_change_time(krb5_context context,krb5_ccache id,krb5_timestamp * mtime)1500 krb5_cc_last_change_time(krb5_context context,
1501 krb5_ccache id,
1502 krb5_timestamp *mtime)
1503 {
1504 *mtime = 0;
1505 return (*id->ops->lastchange)(context, id, mtime);
1506 }
1507
1508 /**
1509 * Return the last modfication time for a cache collection. The query
1510 * can be limited to a specific cache type. If the function return 0
1511 * and mtime is 0, there was no credentials in the caches.
1512 *
1513 * @param context A Kerberos 5 context
1514 * @param type The credential cache to probe, if NULL, all type are traversed.
1515 * @param mtime the last modification time, set to 0 on error.
1516
1517 * @return Return 0 or and error. See krb5_get_error_message().
1518 *
1519 * @ingroup krb5_ccache
1520 */
1521
1522 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_last_change_time(krb5_context context,const char * type,krb5_timestamp * mtime)1523 krb5_cccol_last_change_time(krb5_context context,
1524 const char *type,
1525 krb5_timestamp *mtime)
1526 {
1527 krb5_cccol_cursor cursor;
1528 krb5_error_code ret;
1529 krb5_ccache id;
1530 krb5_timestamp t = 0;
1531
1532 *mtime = 0;
1533
1534 ret = krb5_cccol_cursor_new (context, &cursor);
1535 if (ret)
1536 return ret;
1537
1538 while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) {
1539
1540 if (type && strcmp(krb5_cc_get_type(context, id), type) != 0)
1541 continue;
1542
1543 ret = krb5_cc_last_change_time(context, id, &t);
1544 krb5_cc_close(context, id);
1545 if (ret)
1546 continue;
1547 if (t > *mtime)
1548 *mtime = t;
1549 }
1550
1551 krb5_cccol_cursor_free(context, &cursor);
1552
1553 return 0;
1554 }
1555 /**
1556 * Return a friendly name on credential cache. Free the result with krb5_xfree().
1557 *
1558 * @return Return an error code or 0, see krb5_get_error_message().
1559 *
1560 * @ingroup krb5_ccache
1561 */
1562
1563 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_friendly_name(krb5_context context,krb5_ccache id,char ** name)1564 krb5_cc_get_friendly_name(krb5_context context,
1565 krb5_ccache id,
1566 char **name)
1567 {
1568 krb5_error_code ret;
1569 krb5_data data;
1570
1571 ret = krb5_cc_get_config(context, id, NULL, "FriendlyName", &data);
1572 if (ret) {
1573 krb5_principal principal;
1574 ret = krb5_cc_get_principal(context, id, &principal);
1575 if (ret)
1576 return ret;
1577 ret = krb5_unparse_name(context, principal, name);
1578 krb5_free_principal(context, principal);
1579 } else {
1580 ret = asprintf(name, "%.*s", (int)data.length, (char *)data.data);
1581 krb5_data_free(&data);
1582 if (ret <= 0) {
1583 ret = ENOMEM;
1584 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1585 } else
1586 ret = 0;
1587 }
1588
1589 return ret;
1590 }
1591
1592 /**
1593 * Set the friendly name on credential cache.
1594 *
1595 * @return Return an error code or 0, see krb5_get_error_message().
1596 *
1597 * @ingroup krb5_ccache
1598 */
1599
1600 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_friendly_name(krb5_context context,krb5_ccache id,const char * name)1601 krb5_cc_set_friendly_name(krb5_context context,
1602 krb5_ccache id,
1603 const char *name)
1604 {
1605 krb5_data data;
1606
1607 data.data = rk_UNCONST(name);
1608 data.length = strlen(name);
1609
1610 return krb5_cc_set_config(context, id, NULL, "FriendlyName", &data);
1611 }
1612
1613 /**
1614 * Get the lifetime of the initial ticket in the cache
1615 *
1616 * Get the lifetime of the initial ticket in the cache, if the initial
1617 * ticket was not found, the error code KRB5_CC_END is returned.
1618 *
1619 * @param context A Kerberos 5 context.
1620 * @param id a credential cache
1621 * @param t the relative lifetime of the initial ticket
1622 *
1623 * @return Return an error code or 0, see krb5_get_error_message().
1624 *
1625 * @ingroup krb5_ccache
1626 */
1627
1628 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_lifetime(krb5_context context,krb5_ccache id,time_t * t)1629 krb5_cc_get_lifetime(krb5_context context, krb5_ccache id, time_t *t)
1630 {
1631 krb5_cc_cursor cursor;
1632 krb5_error_code ret;
1633 krb5_creds cred;
1634 time_t now;
1635
1636 *t = 0;
1637 now = time(NULL);
1638
1639 ret = krb5_cc_start_seq_get(context, id, &cursor);
1640 if (ret)
1641 return ret;
1642
1643 while ((ret = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
1644 if (cred.flags.b.initial) {
1645 if (now < cred.times.endtime)
1646 *t = cred.times.endtime - now;
1647 krb5_free_cred_contents(context, &cred);
1648 break;
1649 }
1650 krb5_free_cred_contents(context, &cred);
1651 }
1652
1653 krb5_cc_end_seq_get(context, id, &cursor);
1654
1655 return ret;
1656 }
1657
1658 /**
1659 * Set the time offset betwen the client and the KDC
1660 *
1661 * If the backend doesn't support KDC offset, use the context global setting.
1662 *
1663 * @param context A Kerberos 5 context.
1664 * @param id a credential cache
1665 * @param offset the offset in seconds
1666 *
1667 * @return Return an error code or 0, see krb5_get_error_message().
1668 *
1669 * @ingroup krb5_ccache
1670 */
1671
1672 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat offset)1673 krb5_cc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset)
1674 {
1675 if (id->ops->set_kdc_offset == NULL) {
1676 context->kdc_sec_offset = offset;
1677 context->kdc_usec_offset = 0;
1678 return 0;
1679 }
1680 return (*id->ops->set_kdc_offset)(context, id, offset);
1681 }
1682
1683 /**
1684 * Get the time offset betwen the client and the KDC
1685 *
1686 * If the backend doesn't support KDC offset, use the context global setting.
1687 *
1688 * @param context A Kerberos 5 context.
1689 * @param id a credential cache
1690 * @param offset the offset in seconds
1691 *
1692 * @return Return an error code or 0, see krb5_get_error_message().
1693 *
1694 * @ingroup krb5_ccache
1695 */
1696
1697 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat * offset)1698 krb5_cc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *offset)
1699 {
1700 if (id->ops->get_kdc_offset == NULL) {
1701 *offset = context->kdc_sec_offset;
1702 return 0;
1703 }
1704 return (*id->ops->get_kdc_offset)(context, id, offset);
1705 }
1706
1707
1708 #ifdef _WIN32
1709
1710 #define REGPATH_MIT_KRB5 "SOFTWARE\\MIT\\Kerberos5"
1711 char *
_krb5_get_default_cc_name_from_registry(krb5_context context)1712 _krb5_get_default_cc_name_from_registry(krb5_context context)
1713 {
1714 HKEY hk_k5 = 0;
1715 LONG code;
1716 char * ccname = NULL;
1717
1718 code = RegOpenKeyEx(HKEY_CURRENT_USER,
1719 REGPATH_MIT_KRB5,
1720 0, KEY_READ, &hk_k5);
1721
1722 if (code != ERROR_SUCCESS)
1723 return NULL;
1724
1725 ccname = _krb5_parse_reg_value_as_string(context, hk_k5, "ccname",
1726 REG_NONE, 0);
1727
1728 RegCloseKey(hk_k5);
1729
1730 return ccname;
1731 }
1732
1733 int
_krb5_set_default_cc_name_to_registry(krb5_context context,krb5_ccache id)1734 _krb5_set_default_cc_name_to_registry(krb5_context context, krb5_ccache id)
1735 {
1736 HKEY hk_k5 = 0;
1737 LONG code;
1738 int ret = -1;
1739 char * ccname = NULL;
1740
1741 code = RegOpenKeyEx(HKEY_CURRENT_USER,
1742 REGPATH_MIT_KRB5,
1743 0, KEY_READ|KEY_WRITE, &hk_k5);
1744
1745 if (code != ERROR_SUCCESS)
1746 return -1;
1747
1748 ret = asprintf(&ccname, "%s:%s", krb5_cc_get_type(context, id), krb5_cc_get_name(context, id));
1749 if (ret < 0)
1750 goto cleanup;
1751
1752 ret = _krb5_store_string_to_reg_value(context, hk_k5, "ccname",
1753 REG_SZ, ccname, -1, 0);
1754
1755 cleanup:
1756
1757 if (ccname)
1758 free(ccname);
1759
1760 RegCloseKey(hk_k5);
1761
1762 return ret;
1763 }
1764
1765 #endif
1766