2 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 #include "krb5_locl.h"
39 * Add a new ccache type with operations `ops', overwriting any
40 * existing one if `override'.
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.
47 * @return Return an error code or 0.
49 * @ingroup krb5_ccache
52 krb5_error_code KRB5_LIB_FUNCTION
53 krb5_cc_register(krb5_context context
,
54 const krb5_cc_ops
*ops
,
55 krb5_boolean override
)
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) {
62 krb5_set_error_string(context
,
63 "ccache type %s already exists",
65 return KRB5_CC_TYPE_EXISTS
;
70 if(i
== context
->num_cc_ops
) {
71 krb5_cc_ops
*o
= realloc(context
->cc_ops
,
72 (context
->num_cc_ops
+ 1) *
73 sizeof(*context
->cc_ops
));
75 krb5_set_error_string(context
, "malloc: out of memory");
78 context
->num_cc_ops
++;
80 memset(context
->cc_ops
+ i
, 0,
81 (context
->num_cc_ops
- i
) * sizeof(*context
->cc_ops
));
83 memcpy(&context
->cc_ops
[i
], ops
, sizeof(context
->cc_ops
[i
]));
88 * Allocate the memory for a `id' and the that function table to
89 * `ops'. Returns 0 or and error code.
93 _krb5_cc_allocate(krb5_context context
,
94 const krb5_cc_ops
*ops
,
99 p
= malloc (sizeof(*p
));
101 krb5_set_error_string(context
, "malloc: out of memory");
102 return KRB5_CC_NOMEM
;
111 * Allocate memory for a new ccache in `id' with operations `ops'
112 * and name `residual'. Return 0 or an error code.
115 static krb5_error_code
116 allocate_ccache (krb5_context context
,
117 const krb5_cc_ops
*ops
,
118 const char *residual
,
123 ret
= _krb5_cc_allocate(context
, ops
, id
);
126 ret
= (*id
)->ops
->resolve(context
, id
, residual
);
133 * Find and allocate a ccache in `id' from the specification in `residual'.
134 * If the ccache name doesn't contain any colon, interpret it as a file name.
136 * @param context a Keberos context.
137 * @param name string name of a credential cache.
138 * @param id return pointer to a found credential cache.
140 * @return Return 0 or an error code. In case of an error, id is set
143 * @ingroup krb5_ccache
147 krb5_error_code KRB5_LIB_FUNCTION
148 krb5_cc_resolve(krb5_context context
,
156 for(i
= 0; i
< context
->num_cc_ops
&& context
->cc_ops
[i
].prefix
; i
++) {
157 size_t prefix_len
= strlen(context
->cc_ops
[i
].prefix
);
159 if(strncmp(context
->cc_ops
[i
].prefix
, name
, prefix_len
) == 0
160 && name
[prefix_len
] == ':') {
161 return allocate_ccache (context
, &context
->cc_ops
[i
],
162 name
+ prefix_len
+ 1,
166 if (strchr (name
, ':') == NULL
)
167 return allocate_ccache (context
, &krb5_fcc_ops
, name
, id
);
169 krb5_set_error_string(context
, "unknown ccache type %s", name
);
170 return KRB5_CC_UNKNOWN_TYPE
;
175 * Generate a new ccache of type `ops' in `id'.
177 * @return Return 0 or an error code.
179 * @ingroup krb5_ccache
183 krb5_error_code KRB5_LIB_FUNCTION
184 krb5_cc_gen_new(krb5_context context
,
185 const krb5_cc_ops
*ops
,
188 return krb5_cc_new_unique(context
, ops
->prefix
, NULL
, id
);
192 * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
193 * the library chooses the default credential cache type. The supplied
194 * `hint' (that can be NULL) is a string that the credential cache
195 * type can use to base the name of the credential on, this is to make
196 * it easier for the user to differentiate the credentials.
198 * @return Returns 0 or an error code.
200 * @ingroup krb5_ccache
203 krb5_error_code KRB5_LIB_FUNCTION
204 krb5_cc_new_unique(krb5_context context
, const char *type
,
205 const char *hint
, krb5_ccache
*id
)
207 const krb5_cc_ops
*ops
;
210 ops
= krb5_cc_get_prefix_ops(context
, type
);
212 krb5_set_error_string(context
,
213 "Credential cache type %s is unknown", type
);
214 return KRB5_CC_UNKNOWN_TYPE
;
217 ret
= _krb5_cc_allocate(context
, ops
, id
);
220 return (*id
)->ops
->gen_new(context
, id
);
224 * Return the name of the ccache `id'
226 * @ingroup krb5_ccache
230 const char* KRB5_LIB_FUNCTION
231 krb5_cc_get_name(krb5_context context
,
234 return id
->ops
->get_name(context
, id
);
238 * Return the type of the ccache `id'.
240 * @ingroup krb5_ccache
244 const char* KRB5_LIB_FUNCTION
245 krb5_cc_get_type(krb5_context context
,
248 return id
->ops
->prefix
;
252 * Return the complete resolvable name the ccache `id' in `str´.
253 * `str` should be freed with free(3).
254 * Returns 0 or an error (and then *str is set to NULL).
256 * @ingroup krb5_ccache
260 krb5_error_code KRB5_LIB_FUNCTION
261 krb5_cc_get_full_name(krb5_context context
,
265 const char *type
, *name
;
269 type
= krb5_cc_get_type(context
, id
);
271 krb5_set_error_string(context
, "cache have no name of type");
272 return KRB5_CC_UNKNOWN_TYPE
;
275 name
= krb5_cc_get_name(context
, id
);
277 krb5_set_error_string(context
, "cache of type %s have no name", type
);
278 return KRB5_CC_BADNAME
;
281 if (asprintf(str
, "%s:%s", type
, name
) == -1) {
282 krb5_set_error_string(context
, "malloc - out of memory");
290 * Return krb5_cc_ops of a the ccache `id'.
292 * @ingroup krb5_ccache
297 krb5_cc_get_ops(krb5_context context
, krb5_ccache id
)
303 * Expand variables in `str' into `res'
307 _krb5_expand_default_cc_name(krb5_context context
, const char *str
, char **res
)
309 size_t tlen
, len
= 0;
310 char *tmp
, *tmp2
, *append
;
314 while (str
&& *str
) {
315 tmp
= strstr(str
, "%{");
316 if (tmp
&& tmp
!= str
) {
317 append
= malloc((tmp
- str
) + 1);
319 memcpy(append
, str
, tmp
- str
);
320 append
[tmp
- str
] = '\0';
324 tmp2
= strchr(tmp
, '}');
328 krb5_set_error_string(context
, "variable missing }");
329 return KRB5_CONFIG_BADFORMAT
;
331 if (strncasecmp(tmp
, "%{uid}", 6) == 0)
332 asprintf(&append
, "%u", (unsigned)getuid());
333 else if (strncasecmp(tmp
, "%{null}", 7) == 0)
338 krb5_set_error_string(context
,
339 "expand default cache unknown "
341 (int)(tmp2
- tmp
) - 2, tmp
+ 2);
342 return KRB5_CONFIG_BADFORMAT
;
346 append
= strdup(str
);
349 if (append
== NULL
) {
352 krb5_set_error_string(context
, "malloc - out of memory");
356 tlen
= strlen(append
);
357 tmp
= realloc(*res
, len
+ tlen
+ 1);
362 krb5_set_error_string(context
, "malloc - out of memory");
366 memcpy(*res
+ len
, append
, tlen
+ 1);
374 * Return non-zero if envirnoment that will determine default krb5cc
379 environment_changed(krb5_context context
)
383 /* if the cc name was set, don't change it */
384 if (context
->default_cc_name_set
)
390 e
= getenv("KRB5CCNAME");
392 if (context
->default_cc_name_env
) {
393 free(context
->default_cc_name_env
);
394 context
->default_cc_name_env
= NULL
;
398 if (context
->default_cc_name_env
== NULL
)
400 if (strcmp(e
, context
->default_cc_name_env
) != 0)
407 * Switch the default default credential cache for a specific
408 * credcache type (and name for some implementations).
410 * @return Returns 0 or an error code.
412 * @ingroup krb5_ccache
416 krb5_cc_switch(krb5_context context
, krb5_ccache id
)
419 if (id
->ops
->set_default
== NULL
)
422 return (*id
->ops
->set_default
)(context
, id
);
426 * Set the default cc name for `context' to `name'.
428 * @ingroup krb5_ccache
431 krb5_error_code KRB5_LIB_FUNCTION
432 krb5_cc_set_default_name(krb5_context context
, const char *name
)
434 krb5_error_code ret
= 0;
438 const char *e
= NULL
;
441 e
= getenv("KRB5CCNAME");
444 if (context
->default_cc_name_env
)
445 free(context
->default_cc_name_env
);
446 context
->default_cc_name_env
= strdup(e
);
450 e
= krb5_config_get_string(context
, NULL
, "libdefaults",
451 "default_cc_name", NULL
);
453 ret
= _krb5_expand_default_cc_name(context
, e
, &p
);
458 const krb5_cc_ops
*ops
= KRB5_DEFAULT_CCTYPE
;
459 ret
= (*ops
->get_default_name
)(context
, &p
);
464 context
->default_cc_name_set
= 0;
467 context
->default_cc_name_set
= 1;
471 krb5_set_error_string(context
, "malloc - out of memory");
475 if (context
->default_cc_name
)
476 free(context
->default_cc_name
);
478 context
->default_cc_name
= p
;
484 * Return a pointer to a context static string containing the default
487 * @return String to the default credential cache name.
489 * @ingroup krb5_ccache
493 const char* KRB5_LIB_FUNCTION
494 krb5_cc_default_name(krb5_context context
)
496 if (context
->default_cc_name
== NULL
|| environment_changed(context
))
497 krb5_cc_set_default_name(context
, NULL
);
499 return context
->default_cc_name
;
503 * Open the default ccache in `id'.
505 * @return Return 0 or an error code.
507 * @ingroup krb5_ccache
511 krb5_error_code KRB5_LIB_FUNCTION
512 krb5_cc_default(krb5_context context
,
515 const char *p
= krb5_cc_default_name(context
);
518 krb5_set_error_string(context
, "malloc - out of memory");
521 return krb5_cc_resolve(context
, p
, id
);
525 * Create a new ccache in `id' for `primary_principal'.
527 * @return Return 0 or an error code.
529 * @ingroup krb5_ccache
533 krb5_error_code KRB5_LIB_FUNCTION
534 krb5_cc_initialize(krb5_context context
,
536 krb5_principal primary_principal
)
538 return (*id
->ops
->init
)(context
, id
, primary_principal
);
543 * Remove the ccache `id'.
545 * @return Return 0 or an error code.
547 * @ingroup krb5_ccache
551 krb5_error_code KRB5_LIB_FUNCTION
552 krb5_cc_destroy(krb5_context context
,
557 ret
= (*id
->ops
->destroy
)(context
, id
);
558 krb5_cc_close (context
, id
);
563 * Stop using the ccache `id' and free the related resources.
565 * @return Return 0 or an error code.
567 * @ingroup krb5_ccache
571 krb5_error_code KRB5_LIB_FUNCTION
572 krb5_cc_close(krb5_context context
,
576 ret
= (*id
->ops
->close
)(context
, id
);
582 * Store `creds' in the ccache `id'.
584 * @return Return 0 or an error code.
586 * @ingroup krb5_ccache
590 krb5_error_code KRB5_LIB_FUNCTION
591 krb5_cc_store_cred(krb5_context context
,
595 return (*id
->ops
->store
)(context
, id
, creds
);
599 * Retrieve the credential identified by `mcreds' (and `whichfields')
600 * from `id' in `creds'. 'creds' must be free by the caller using
601 * krb5_free_cred_contents.
603 * @return Return 0 or an error code.
605 * @ingroup krb5_ccache
609 krb5_error_code KRB5_LIB_FUNCTION
610 krb5_cc_retrieve_cred(krb5_context context
,
612 krb5_flags whichfields
,
613 const krb5_creds
*mcreds
,
617 krb5_cc_cursor cursor
;
619 if (id
->ops
->retrieve
!= NULL
) {
620 return (*id
->ops
->retrieve
)(context
, id
, whichfields
,
624 ret
= krb5_cc_start_seq_get(context
, id
, &cursor
);
627 while((ret
= krb5_cc_next_cred(context
, id
, &cursor
, creds
)) == 0){
628 if(krb5_compare_creds(context
, whichfields
, mcreds
, creds
)){
632 krb5_free_cred_contents (context
, creds
);
634 krb5_cc_end_seq_get(context
, id
, &cursor
);
639 * Return the principal of `id' in `principal'.
641 * @return Return 0 or an error code.
643 * @ingroup krb5_ccache
647 krb5_error_code KRB5_LIB_FUNCTION
648 krb5_cc_get_principal(krb5_context context
,
650 krb5_principal
*principal
)
652 return (*id
->ops
->get_princ
)(context
, id
, principal
);
656 * Start iterating over `id', `cursor' is initialized to the
659 * @return Return 0 or an error code.
661 * @ingroup krb5_ccache
665 krb5_error_code KRB5_LIB_FUNCTION
666 krb5_cc_start_seq_get (krb5_context context
,
667 const krb5_ccache id
,
668 krb5_cc_cursor
*cursor
)
670 return (*id
->ops
->get_first
)(context
, id
, cursor
);
674 * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
675 * and advance `cursor'.
677 * @return Return 0 or an error code.
679 * @ingroup krb5_ccache
683 krb5_error_code KRB5_LIB_FUNCTION
684 krb5_cc_next_cred (krb5_context context
,
685 const krb5_ccache id
,
686 krb5_cc_cursor
*cursor
,
689 return (*id
->ops
->get_next
)(context
, id
, cursor
, creds
);
693 * Like krb5_cc_next_cred, but allow for selective retrieval
695 * @ingroup krb5_ccache
699 krb5_error_code KRB5_LIB_FUNCTION
700 krb5_cc_next_cred_match(krb5_context context
,
701 const krb5_ccache id
,
702 krb5_cc_cursor
* cursor
,
704 krb5_flags whichfields
,
705 const krb5_creds
* mcreds
)
709 ret
= krb5_cc_next_cred(context
, id
, cursor
, creds
);
712 if (mcreds
== NULL
|| krb5_compare_creds(context
, whichfields
, mcreds
, creds
))
714 krb5_free_cred_contents(context
, creds
);
719 * Destroy the cursor `cursor'.
721 * @ingroup krb5_ccache
725 krb5_error_code KRB5_LIB_FUNCTION
726 krb5_cc_end_seq_get (krb5_context context
,
727 const krb5_ccache id
,
728 krb5_cc_cursor
*cursor
)
730 return (*id
->ops
->end_get
)(context
, id
, cursor
);
734 * Remove the credential identified by `cred', `which' from `id'.
736 * @ingroup krb5_ccache
740 krb5_error_code KRB5_LIB_FUNCTION
741 krb5_cc_remove_cred(krb5_context context
,
746 if(id
->ops
->remove_cred
== NULL
) {
747 krb5_set_error_string(context
,
748 "ccache %s does not support remove_cred",
750 return EACCES
; /* XXX */
752 return (*id
->ops
->remove_cred
)(context
, id
, which
, cred
);
756 * Set the flags of `id' to `flags'.
758 * @ingroup krb5_ccache
762 krb5_error_code KRB5_LIB_FUNCTION
763 krb5_cc_set_flags(krb5_context context
,
767 return (*id
->ops
->set_flags
)(context
, id
, flags
);
771 * Copy the contents of `from' to `to'.
773 * @ingroup krb5_ccache
777 krb5_error_code KRB5_LIB_FUNCTION
778 krb5_cc_copy_cache_match(krb5_context context
,
779 const krb5_ccache from
,
781 krb5_flags whichfields
,
782 const krb5_creds
* mcreds
,
783 unsigned int *matched
)
786 krb5_cc_cursor cursor
;
788 krb5_principal princ
;
790 ret
= krb5_cc_get_principal(context
, from
, &princ
);
793 ret
= krb5_cc_initialize(context
, to
, princ
);
795 krb5_free_principal(context
, princ
);
798 ret
= krb5_cc_start_seq_get(context
, from
, &cursor
);
800 krb5_free_principal(context
, princ
);
806 krb5_cc_next_cred_match(context
, from
, &cursor
, &cred
,
807 whichfields
, mcreds
) == 0) {
810 ret
= krb5_cc_store_cred(context
, to
, &cred
);
811 krb5_free_cred_contents(context
, &cred
);
813 krb5_cc_end_seq_get(context
, from
, &cursor
);
814 krb5_free_principal(context
, princ
);
819 * Just like krb5_cc_copy_cache_match, but copy everything.
821 * @ingroup krb5_ccache
825 krb5_error_code KRB5_LIB_FUNCTION
826 krb5_cc_copy_cache(krb5_context context
,
827 const krb5_ccache from
,
830 return krb5_cc_copy_cache_match(context
, from
, to
, 0, NULL
, NULL
);
834 * Return the version of `id'.
836 * @ingroup krb5_ccache
840 krb5_error_code KRB5_LIB_FUNCTION
841 krb5_cc_get_version(krb5_context context
,
842 const krb5_ccache id
)
844 if(id
->ops
->get_version
)
845 return (*id
->ops
->get_version
)(context
, id
);
851 * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
853 * @ingroup krb5_ccache
857 void KRB5_LIB_FUNCTION
858 krb5_cc_clear_mcred(krb5_creds
*mcred
)
860 memset(mcred
, 0, sizeof(*mcred
));
864 * Get the cc ops that is registered in `context' to handle the
865 * prefix. prefix can be a complete credential cache name or a
866 * prefix, the function will only use part up to the first colon (:)
867 * if there is one. If prefix the argument is NULL, the default ccache
868 * implemtation is returned.
870 * @return Returns NULL if ops not found.
872 * @ingroup krb5_ccache
877 krb5_cc_get_prefix_ops(krb5_context context
, const char *prefix
)
883 return KRB5_DEFAULT_CCTYPE
;
884 if (prefix
[0] == '/')
885 return &krb5_fcc_ops
;
889 krb5_set_error_string(context
, "malloc - out of memory");
896 for(i
= 0; i
< context
->num_cc_ops
&& context
->cc_ops
[i
].prefix
; i
++) {
897 if(strcmp(context
->cc_ops
[i
].prefix
, p
) == 0) {
899 return &context
->cc_ops
[i
];
906 struct krb5_cc_cache_cursor_data
{
907 const krb5_cc_ops
*ops
;
908 krb5_cc_cursor cursor
;
912 * Start iterating over all caches of `type'. If `type' is NULL, the
913 * default type is * used. `cursor' is initialized to the beginning.
915 * @return Return 0 or an error code.
917 * @ingroup krb5_ccache
921 krb5_error_code KRB5_LIB_FUNCTION
922 krb5_cc_cache_get_first (krb5_context context
,
924 krb5_cc_cache_cursor
*cursor
)
926 const krb5_cc_ops
*ops
;
930 type
= krb5_cc_default_name(context
);
932 ops
= krb5_cc_get_prefix_ops(context
, type
);
934 krb5_set_error_string(context
, "Unknown type \"%s\" when iterating "
935 "trying to iterate the credential caches", type
);
936 return KRB5_CC_UNKNOWN_TYPE
;
939 if (ops
->get_cache_first
== NULL
) {
940 krb5_set_error_string(context
, "Credential cache type %s doesn't support "
941 "iterations over caches", ops
->prefix
);
942 return KRB5_CC_NOSUPP
;
945 *cursor
= calloc(1, sizeof(**cursor
));
946 if (*cursor
== NULL
) {
947 krb5_set_error_string(context
, "malloc - out of memory");
951 (*cursor
)->ops
= ops
;
953 ret
= ops
->get_cache_first(context
, &(*cursor
)->cursor
);
962 * Retrieve the next cache pointed to by (`cursor') in `id'
963 * and advance `cursor'.
965 * @return Return 0 or an error code.
967 * @ingroup krb5_ccache
971 krb5_error_code KRB5_LIB_FUNCTION
972 krb5_cc_cache_next (krb5_context context
,
973 krb5_cc_cache_cursor cursor
,
976 return cursor
->ops
->get_cache_next(context
, cursor
->cursor
, id
);
980 * Destroy the cursor `cursor'.
982 * @return Return 0 or an error code.
984 * @ingroup krb5_ccache
988 krb5_error_code KRB5_LIB_FUNCTION
989 krb5_cc_cache_end_seq_get (krb5_context context
,
990 krb5_cc_cache_cursor cursor
)
993 ret
= cursor
->ops
->end_cache_get(context
, cursor
->cursor
);
1000 * Search for a matching credential cache of type `type' that have the
1001 * `principal' as the default principal. If NULL is used for `type',
1002 * the default type is used. On success, `id' needs to be freed with
1003 * krb5_cc_close or krb5_cc_destroy.
1005 * @return On failure, error code is returned and `id' is set to NULL.
1007 * @ingroup krb5_ccache
1011 krb5_error_code KRB5_LIB_FUNCTION
1012 krb5_cc_cache_match (krb5_context context
,
1013 krb5_principal client
,
1017 krb5_cc_cache_cursor cursor
;
1018 krb5_error_code ret
;
1019 krb5_ccache cache
= NULL
;
1023 ret
= krb5_cc_cache_get_first (context
, type
, &cursor
);
1027 while ((ret
= krb5_cc_cache_next (context
, cursor
, &cache
)) == 0) {
1028 krb5_principal principal
;
1030 ret
= krb5_cc_get_principal(context
, cache
, &principal
);
1034 match
= krb5_principal_compare(context
, principal
, client
);
1035 krb5_free_principal(context
, principal
);
1040 krb5_cc_close(context
, cache
);
1044 krb5_cc_cache_end_seq_get(context
, cursor
);
1046 if (cache
== NULL
) {
1049 krb5_unparse_name(context
, client
, &str
);
1051 krb5_set_error_string(context
, "Principal %s not found in a "
1052 "credential cache", str
? str
: "<out of memory>");
1055 return KRB5_CC_NOTFOUND
;
1063 * Move the content from one credential cache to another. The
1064 * operation is an atomic switch.
1066 * @param context a Keberos context
1067 * @param from the credential cache to move the content from
1068 * @param to the credential cache to move the content to
1070 * @return On sucess, from is freed. On failure, error code is
1071 * returned and from and to are both still allocated.
1073 * @ingroup krb5_ccache
1077 krb5_cc_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
1079 krb5_error_code ret
;
1081 if (strcmp(from
->ops
->prefix
, to
->ops
->prefix
) != 0) {
1082 krb5_set_error_string(context
, "Moving credentials between diffrent "
1083 "types not yet supported");
1084 return KRB5_CC_NOSUPP
;
1087 ret
= (*to
->ops
->move
)(context
, from
, to
);
1089 memset(from
, 0, sizeof(*from
));