2 * Copyright (c) 2004 - 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"
35 #include <krb5_ccapi.h>
42 /* XXX should we fetch these for each open ? */
43 static HEIMDAL_MUTEX acc_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
44 static cc_initialize_func init_func
;
47 static void *cc_handle
;
50 typedef struct krb5_acc
{
56 static krb5_error_code
acc_close(krb5_context
, krb5_ccache
);
58 #define ACACHE(X) ((krb5_acc *)(X)->data.data)
64 { ccErrBadName
, KRB5_CC_BADNAME
},
65 { ccErrCredentialsNotFound
, KRB5_CC_NOTFOUND
},
66 { ccErrCCacheNotFound
, KRB5_FCC_NOFILE
},
67 { ccErrContextNotFound
, KRB5_CC_NOTFOUND
},
68 { ccIteratorEnd
, KRB5_CC_END
},
69 { ccErrNoMem
, KRB5_CC_NOMEM
},
70 { ccErrServerUnavailable
, KRB5_CC_NOSUPP
},
74 static krb5_error_code
75 translate_cc_error(krb5_context context
, cc_int32 error
)
78 krb5_clear_error_string(context
);
79 for(i
= 0; i
< sizeof(cc_errors
)/sizeof(cc_errors
[0]); i
++)
80 if (cc_errors
[i
].error
== error
)
81 return cc_errors
[i
].ret
;
82 return KRB5_FCC_INTERNAL
;
85 static krb5_error_code
86 init_ccapi(krb5_context context
)
90 HEIMDAL_MUTEX_lock(&acc_mutex
);
92 HEIMDAL_MUTEX_unlock(&acc_mutex
);
93 krb5_clear_error_string(context
);
97 lib
= krb5_config_get_string(context
, NULL
,
98 "libdefaults", "ccapi_library",
102 lib
= "/System/Library/Frameworks/Kerberos.framework/Kerberos";
104 lib
= "/usr/lib/libkrb5_cc.so";
114 cc_handle
= dlopen(lib
, RTLD_LAZY
);
115 if (cc_handle
== NULL
) {
116 HEIMDAL_MUTEX_unlock(&acc_mutex
);
117 krb5_set_error_string(context
, "Failed to load %s", lib
);
118 return KRB5_CC_NOSUPP
;
121 init_func
= (cc_initialize_func
)dlsym(cc_handle
, "cc_initialize");
122 HEIMDAL_MUTEX_unlock(&acc_mutex
);
123 if (init_func
== NULL
) {
124 krb5_set_error_string(context
, "Failed to find cc_initialize"
125 "in %s: %s", lib
, dlerror());
127 return KRB5_CC_NOSUPP
;
132 HEIMDAL_MUTEX_unlock(&acc_mutex
);
133 krb5_set_error_string(context
, "no support for shared object");
134 return KRB5_CC_NOSUPP
;
138 static krb5_error_code
139 make_cred_from_ccred(krb5_context context
,
140 const cc_credentials_v5_t
*incred
,
146 memset(cred
, 0, sizeof(*cred
));
148 ret
= krb5_parse_name(context
, incred
->client
, &cred
->client
);
152 ret
= krb5_parse_name(context
, incred
->server
, &cred
->server
);
156 cred
->session
.keytype
= incred
->keyblock
.type
;
157 cred
->session
.keyvalue
.length
= incred
->keyblock
.length
;
158 cred
->session
.keyvalue
.data
= malloc(incred
->keyblock
.length
);
159 if (cred
->session
.keyvalue
.data
== NULL
)
161 memcpy(cred
->session
.keyvalue
.data
, incred
->keyblock
.data
,
162 incred
->keyblock
.length
);
164 cred
->times
.authtime
= incred
->authtime
;
165 cred
->times
.starttime
= incred
->starttime
;
166 cred
->times
.endtime
= incred
->endtime
;
167 cred
->times
.renew_till
= incred
->renew_till
;
169 ret
= krb5_data_copy(&cred
->ticket
,
171 incred
->ticket
.length
);
175 ret
= krb5_data_copy(&cred
->second_ticket
,
176 incred
->second_ticket
.data
,
177 incred
->second_ticket
.length
);
181 cred
->authdata
.val
= NULL
;
182 cred
->authdata
.len
= 0;
184 cred
->addresses
.val
= NULL
;
185 cred
->addresses
.len
= 0;
187 for (i
= 0; incred
->authdata
&& incred
->authdata
[i
]; i
++)
191 cred
->authdata
.val
= calloc(i
, sizeof(cred
->authdata
.val
[0]));
192 if (cred
->authdata
.val
== NULL
)
194 cred
->authdata
.len
= i
;
195 for (i
= 0; i
< cred
->authdata
.len
; i
++) {
196 cred
->authdata
.val
[i
].ad_type
= incred
->authdata
[i
]->type
;
197 ret
= krb5_data_copy(&cred
->authdata
.val
[i
].ad_data
,
198 incred
->authdata
[i
]->data
,
199 incred
->authdata
[i
]->length
);
205 for (i
= 0; incred
->addresses
&& incred
->addresses
[i
]; i
++)
209 cred
->addresses
.val
= calloc(i
, sizeof(cred
->addresses
.val
[0]));
210 if (cred
->addresses
.val
== NULL
)
212 cred
->addresses
.len
= i
;
214 for (i
= 0; i
< cred
->addresses
.len
; i
++) {
215 cred
->addresses
.val
[i
].addr_type
= incred
->addresses
[i
]->type
;
216 ret
= krb5_data_copy(&cred
->addresses
.val
[i
].address
,
217 incred
->addresses
[i
]->data
,
218 incred
->addresses
[i
]->length
);
225 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_FORWARDABLE
)
226 cred
->flags
.b
.forwardable
= 1;
227 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_FORWARDED
)
228 cred
->flags
.b
.forwarded
= 1;
229 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_PROXIABLE
)
230 cred
->flags
.b
.proxiable
= 1;
231 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_PROXY
)
232 cred
->flags
.b
.proxy
= 1;
233 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_MAY_POSTDATE
)
234 cred
->flags
.b
.may_postdate
= 1;
235 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_POSTDATED
)
236 cred
->flags
.b
.postdated
= 1;
237 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_INVALID
)
238 cred
->flags
.b
.invalid
= 1;
239 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_RENEWABLE
)
240 cred
->flags
.b
.renewable
= 1;
241 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_INITIAL
)
242 cred
->flags
.b
.initial
= 1;
243 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_PRE_AUTH
)
244 cred
->flags
.b
.pre_authent
= 1;
245 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_HW_AUTH
)
246 cred
->flags
.b
.hw_authent
= 1;
247 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED
)
248 cred
->flags
.b
.transited_policy_checked
= 1;
249 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE
)
250 cred
->flags
.b
.ok_as_delegate
= 1;
251 if (incred
->ticket_flags
& KRB5_CCAPI_TKT_FLG_ANONYMOUS
)
252 cred
->flags
.b
.anonymous
= 1;
258 krb5_set_error_string(context
, "malloc - out of memory");
261 krb5_free_cred_contents(context
, cred
);
266 free_ccred(cc_credentials_v5_t
*cred
)
270 if (cred
->addresses
) {
271 for (i
= 0; cred
->addresses
[i
] != 0; i
++) {
272 if (cred
->addresses
[i
]->data
)
273 free(cred
->addresses
[i
]->data
);
274 free(cred
->addresses
[i
]);
276 free(cred
->addresses
);
282 memset(cred
, 0, sizeof(*cred
));
285 static krb5_error_code
286 make_ccred_from_cred(krb5_context context
,
287 const krb5_creds
*incred
,
288 cc_credentials_v5_t
*cred
)
293 memset(cred
, 0, sizeof(*cred
));
295 ret
= krb5_unparse_name(context
, incred
->client
, &cred
->client
);
299 ret
= krb5_unparse_name(context
, incred
->server
, &cred
->server
);
303 cred
->keyblock
.type
= incred
->session
.keytype
;
304 cred
->keyblock
.length
= incred
->session
.keyvalue
.length
;
305 cred
->keyblock
.data
= incred
->session
.keyvalue
.data
;
307 cred
->authtime
= incred
->times
.authtime
;
308 cred
->starttime
= incred
->times
.starttime
;
309 cred
->endtime
= incred
->times
.endtime
;
310 cred
->renew_till
= incred
->times
.renew_till
;
312 cred
->ticket
.length
= incred
->ticket
.length
;
313 cred
->ticket
.data
= incred
->ticket
.data
;
315 cred
->second_ticket
.length
= incred
->second_ticket
.length
;
316 cred
->second_ticket
.data
= incred
->second_ticket
.data
;
318 /* XXX this one should also be filled in */
319 cred
->authdata
= NULL
;
321 cred
->addresses
= calloc(incred
->addresses
.len
+ 1,
322 sizeof(cred
->addresses
[0]));
323 if (cred
->addresses
== NULL
) {
329 for (i
= 0; i
< incred
->addresses
.len
; i
++) {
331 addr
= malloc(sizeof(*addr
));
336 addr
->type
= incred
->addresses
.val
[i
].addr_type
;
337 addr
->length
= incred
->addresses
.val
[i
].address
.length
;
338 addr
->data
= malloc(addr
->length
);
339 if (addr
->data
== NULL
) {
343 memcpy(addr
->data
, incred
->addresses
.val
[i
].address
.data
,
345 cred
->addresses
[i
] = addr
;
347 cred
->addresses
[i
] = NULL
;
349 cred
->ticket_flags
= 0;
350 if (incred
->flags
.b
.forwardable
)
351 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_FORWARDABLE
;
352 if (incred
->flags
.b
.forwarded
)
353 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_FORWARDED
;
354 if (incred
->flags
.b
.proxiable
)
355 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_PROXIABLE
;
356 if (incred
->flags
.b
.proxy
)
357 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_PROXY
;
358 if (incred
->flags
.b
.may_postdate
)
359 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE
;
360 if (incred
->flags
.b
.postdated
)
361 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_POSTDATED
;
362 if (incred
->flags
.b
.invalid
)
363 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_INVALID
;
364 if (incred
->flags
.b
.renewable
)
365 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_RENEWABLE
;
366 if (incred
->flags
.b
.initial
)
367 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_INITIAL
;
368 if (incred
->flags
.b
.pre_authent
)
369 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_PRE_AUTH
;
370 if (incred
->flags
.b
.hw_authent
)
371 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_HW_AUTH
;
372 if (incred
->flags
.b
.transited_policy_checked
)
373 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED
;
374 if (incred
->flags
.b
.ok_as_delegate
)
375 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE
;
376 if (incred
->flags
.b
.anonymous
)
377 cred
->ticket_flags
|= KRB5_CCAPI_TKT_FLG_ANONYMOUS
;
384 krb5_clear_error_string(context
);
389 get_cc_name(cc_ccache_t cache
)
395 error
= (*cache
->func
->get_name
)(cache
, &name
);
399 str
= strdup(name
->data
);
400 (*name
->func
->release
)(name
);
406 acc_get_name(krb5_context context
,
409 krb5_acc
*a
= ACACHE(id
);
413 name
= get_cc_name(a
->ccache
);
415 krb5_set_error_string(context
, "malloc: out of memory");
418 strlcpy(n
, name
, sizeof(n
));
423 static krb5_error_code
424 acc_alloc(krb5_context context
, krb5_ccache
*id
)
430 ret
= init_ccapi(context
);
434 ret
= krb5_data_alloc(&(*id
)->data
, sizeof(*a
));
436 krb5_clear_error_string(context
);
442 error
= (*init_func
)(&a
->context
, ccapi_version_3
, NULL
, NULL
);
444 krb5_data_free(&(*id
)->data
);
445 return translate_cc_error(context
, error
);
448 a
->cache_name
= NULL
;
453 static krb5_error_code
454 acc_resolve(krb5_context context
, krb5_ccache
*id
, const char *res
)
460 ret
= acc_alloc(context
, id
);
466 error
= (*a
->context
->func
->open_ccache
)(a
->context
, res
,
469 a
->cache_name
= get_cc_name(a
->ccache
);
470 if (a
->cache_name
== NULL
) {
471 acc_close(context
, *id
);
473 krb5_set_error_string(context
, "malloc: out of memory");
476 } else if (error
== ccErrCCacheNotFound
) {
478 a
->cache_name
= NULL
;
482 return translate_cc_error(context
, error
);
488 static krb5_error_code
489 acc_gen_new(krb5_context context
, krb5_ccache
*id
)
494 ret
= acc_alloc(context
, id
);
501 a
->cache_name
= NULL
;
506 static krb5_error_code
507 acc_initialize(krb5_context context
,
509 krb5_principal primary_principal
)
511 krb5_acc
*a
= ACACHE(id
);
516 ret
= krb5_unparse_name(context
, primary_principal
, &name
);
520 error
= (*a
->context
->func
->create_new_ccache
)(a
->context
,
526 return translate_cc_error(context
, error
);
529 static krb5_error_code
530 acc_close(krb5_context context
,
533 krb5_acc
*a
= ACACHE(id
);
536 (*a
->ccache
->func
->release
)(a
->ccache
);
541 a
->cache_name
= NULL
;
543 (*a
->context
->func
->release
)(a
->context
);
545 krb5_data_free(&id
->data
);
549 static krb5_error_code
550 acc_destroy(krb5_context context
,
553 krb5_acc
*a
= ACACHE(id
);
557 error
= (*a
->ccache
->func
->destroy
)(a
->ccache
);
561 error
= (a
->context
->func
->release
)(a
->context
);
564 return translate_cc_error(context
, error
);
567 static krb5_error_code
568 acc_store_cred(krb5_context context
,
572 krb5_acc
*a
= ACACHE(id
);
573 cc_credentials_union cred
;
574 cc_credentials_v5_t v5cred
;
578 if (a
->ccache
== NULL
) {
579 krb5_set_error_string(context
, "No API credential found");
580 return KRB5_CC_NOTFOUND
;
583 cred
.version
= cc_credentials_v5
;
584 cred
.credentials
.credentials_v5
= &v5cred
;
586 ret
= make_ccred_from_cred(context
,
592 error
= (*a
->ccache
->func
->store_credentials
)(a
->ccache
, &cred
);
594 ret
= translate_cc_error(context
, error
);
601 static krb5_error_code
602 acc_get_principal(krb5_context context
,
604 krb5_principal
*principal
)
606 krb5_acc
*a
= ACACHE(id
);
611 if (a
->ccache
== NULL
) {
612 krb5_set_error_string(context
, "No API credential found");
613 return KRB5_CC_NOTFOUND
;
616 error
= (*a
->ccache
->func
->get_principal
)(a
->ccache
,
620 return translate_cc_error(context
, error
);
622 ret
= krb5_parse_name(context
, name
->data
, principal
);
624 (*name
->func
->release
)(name
);
628 static krb5_error_code
629 acc_get_first (krb5_context context
,
631 krb5_cc_cursor
*cursor
)
633 cc_credentials_iterator_t iter
;
634 krb5_acc
*a
= ACACHE(id
);
637 if (a
->ccache
== NULL
) {
638 krb5_set_error_string(context
, "No API credential found");
639 return KRB5_CC_NOTFOUND
;
642 error
= (*a
->ccache
->func
->new_credentials_iterator
)(a
->ccache
, &iter
);
644 krb5_clear_error_string(context
);
652 static krb5_error_code
653 acc_get_next (krb5_context context
,
655 krb5_cc_cursor
*cursor
,
658 cc_credentials_iterator_t iter
= *cursor
;
659 cc_credentials_t cred
;
664 error
= (*iter
->func
->next
)(iter
, &cred
);
666 return translate_cc_error(context
, error
);
667 if (cred
->data
->version
== cc_credentials_v5
)
669 (*cred
->func
->release
)(cred
);
672 ret
= make_cred_from_ccred(context
,
673 cred
->data
->credentials
.credentials_v5
,
675 (*cred
->func
->release
)(cred
);
679 static krb5_error_code
680 acc_end_get (krb5_context context
,
682 krb5_cc_cursor
*cursor
)
684 cc_credentials_iterator_t iter
= *cursor
;
685 (*iter
->func
->release
)(iter
);
689 static krb5_error_code
690 acc_remove_cred(krb5_context context
,
695 cc_credentials_iterator_t iter
;
696 krb5_acc
*a
= ACACHE(id
);
697 cc_credentials_t ccred
;
700 char *client
, *server
;
702 if (a
->ccache
== NULL
) {
703 krb5_set_error_string(context
, "No API credential found");
704 return KRB5_CC_NOTFOUND
;
708 ret
= krb5_unparse_name(context
, cred
->client
, &client
);
714 ret
= krb5_unparse_name(context
, cred
->server
, &server
);
720 error
= (*a
->ccache
->func
->new_credentials_iterator
)(a
->ccache
, &iter
);
724 return translate_cc_error(context
, error
);
727 ret
= KRB5_CC_NOTFOUND
;
729 cc_credentials_v5_t
*v5cred
;
731 error
= (*iter
->func
->next
)(iter
, &ccred
);
735 if (ccred
->data
->version
!= cc_credentials_v5
)
738 v5cred
= ccred
->data
->credentials
.credentials_v5
;
740 if (client
&& strcmp(v5cred
->client
, client
) != 0)
743 if (strcmp(v5cred
->server
, server
) != 0)
746 (*a
->ccache
->func
->remove_credentials
)(a
->ccache
, ccred
);
749 (*ccred
->func
->release
)(ccred
);
752 (*iter
->func
->release
)(iter
);
755 krb5_set_error_string(context
, "Can't find credential %s in cache",
763 static krb5_error_code
764 acc_set_flags(krb5_context context
,
771 static krb5_error_code
772 acc_get_version(krb5_context context
,
779 cc_context_t context
;
780 cc_ccache_iterator_t iter
;
783 static krb5_error_code
784 acc_get_cache_first(krb5_context context
, krb5_cc_cursor
*cursor
)
786 struct cache_iter
*iter
;
790 ret
= init_ccapi(context
);
794 iter
= calloc(1, sizeof(*iter
));
796 krb5_set_error_string(context
, "malloc - out of memory");
800 error
= (*init_func
)(&iter
->context
, ccapi_version_3
, NULL
, NULL
);
803 return translate_cc_error(context
, error
);
806 error
= (*iter
->context
->func
->new_ccache_iterator
)(iter
->context
,
810 krb5_clear_error_string(context
);
817 static krb5_error_code
818 acc_get_cache_next(krb5_context context
, krb5_cc_cursor cursor
, krb5_ccache
*id
)
820 struct cache_iter
*iter
= cursor
;
826 error
= (*iter
->iter
->func
->next
)(iter
->iter
, &cache
);
828 return translate_cc_error(context
, error
);
830 ret
= _krb5_cc_allocate(context
, &krb5_acc_ops
, id
);
832 (*cache
->func
->release
)(cache
);
836 ret
= acc_alloc(context
, id
);
838 (*cache
->func
->release
)(cache
);
846 a
->cache_name
= get_cc_name(a
->ccache
);
847 if (a
->cache_name
== NULL
) {
848 acc_close(context
, *id
);
850 krb5_set_error_string(context
, "malloc: out of memory");
856 static krb5_error_code
857 acc_end_cache_get(krb5_context context
, krb5_cc_cursor cursor
)
859 struct cache_iter
*iter
= cursor
;
861 (*iter
->iter
->func
->release
)(iter
->iter
);
863 (*iter
->context
->func
->release
)(iter
->context
);
864 iter
->context
= NULL
;
869 static krb5_error_code
870 acc_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
872 krb5_acc
*afrom
= ACACHE(from
);
873 krb5_acc
*ato
= ACACHE(to
);
876 if (ato
->ccache
== NULL
) {
879 error
= (*afrom
->ccache
->func
->get_principal
)(afrom
->ccache
,
883 return translate_cc_error(context
, error
);
885 error
= (*ato
->context
->func
->create_new_ccache
)(ato
->context
,
889 (*name
->func
->release
)(name
);
891 return translate_cc_error(context
, error
);
895 error
= (*ato
->ccache
->func
->move
)(afrom
->ccache
, ato
->ccache
);
896 return translate_cc_error(context
, error
);
899 static krb5_error_code
900 acc_default_name(krb5_context context
, char **str
)
907 ret
= init_ccapi(context
);
911 error
= (*init_func
)(&cc
, ccapi_version_3
, NULL
, NULL
);
913 return translate_cc_error(context
, error
);
915 error
= (*cc
->func
->get_default_ccache_name
)(cc
, &name
);
917 (*cc
->func
->release
)(cc
);
918 return translate_cc_error(context
, error
);
921 asprintf(str
, "API:%s", name
->data
);
922 (*name
->func
->release
)(name
);
923 (*cc
->func
->release
)(cc
);
926 krb5_set_error_string(context
, "out of memory");
934 * Variable containing the API based credential cache implemention.
936 * @ingroup krb5_ccache
939 const krb5_cc_ops krb5_acc_ops
= {
948 NULL
, /* acc_retrieve */