1 /*        $NetBSD: acache.c,v 1.2 2017/01/28 21:31:49 christos Exp $  */
2 
3 /*
4  * Copyright (c) 2004 - 2007 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 #include <krb5/krb5_ccapi.h>
40 #ifdef HAVE_DLFCN_H
41 #include <dlfcn.h>
42 #endif
43 
44 #ifndef KCM_IS_API_CACHE
45 
46 static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
47 static cc_initialize_func init_func;
48 static void (KRB5_CALLCONV *set_target_uid)(uid_t);
49 static void (KRB5_CALLCONV *clear_target)(void);
50 
51 #ifdef HAVE_DLOPEN
52 static void *cc_handle;
53 #endif
54 
55 typedef struct krb5_acc {
56     char *cache_name;
57     cc_context_t context;
58     cc_ccache_t ccache;
59 } krb5_acc;
60 
61 static krb5_error_code KRB5_CALLCONV acc_close(krb5_context, krb5_ccache);
62 
63 #define ACACHE(X) ((krb5_acc *)(X)->data.data)
64 
65 static const struct {
66     cc_int32 error;
67     krb5_error_code ret;
68 } cc_errors[] = {
69     { ccErrBadName,           KRB5_CC_BADNAME },
70     { ccErrCredentialsNotFound,         KRB5_CC_NOTFOUND },
71     { ccErrCCacheNotFound,    KRB5_FCC_NOFILE },
72     { ccErrContextNotFound,   KRB5_CC_NOTFOUND },
73     { ccIteratorEnd,                    KRB5_CC_END },
74     { ccErrNoMem,             KRB5_CC_NOMEM },
75     { ccErrServerUnavailable, KRB5_CC_NOSUPP },
76     { ccErrInvalidCCache,     KRB5_CC_BADNAME },
77     { ccNoError,              0 }
78 };
79 
80 static krb5_error_code
translate_cc_error(krb5_context context,cc_int32 error)81 translate_cc_error(krb5_context context, cc_int32 error)
82 {
83     size_t i;
84     krb5_clear_error_message(context);
85     for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
86           if (cc_errors[i].error == error)
87               return cc_errors[i].ret;
88     return KRB5_FCC_INTERNAL;
89 }
90 
91 static krb5_error_code
init_ccapi(krb5_context context)92 init_ccapi(krb5_context context)
93 {
94     const char *lib = NULL;
95 
96     HEIMDAL_MUTEX_lock(&acc_mutex);
97     if (init_func) {
98           HEIMDAL_MUTEX_unlock(&acc_mutex);
99           if (context)
100               krb5_clear_error_message(context);
101           return 0;
102     }
103 
104     if (context)
105           lib = krb5_config_get_string(context, NULL,
106                                              "libdefaults", "ccapi_library",
107                                              NULL);
108     if (lib == NULL) {
109 #ifdef __APPLE__
110           lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
111 #elif defined(KRB5_USE_PATH_TOKENS) && defined(_WIN32)
112           lib = "%{LIBDIR}/libkrb5_cc.dll";
113 #else
114           lib = "/usr/lib/libkrb5_cc.so";
115 #endif
116     }
117 
118 #ifdef HAVE_DLOPEN
119 
120 #ifndef RTLD_LAZY
121 #define RTLD_LAZY 0
122 #endif
123 #ifndef RTLD_LOCAL
124 #define RTLD_LOCAL 0
125 #endif
126 
127 #ifdef KRB5_USE_PATH_TOKENS
128     {
129       char * explib = NULL;
130       if (_krb5_expand_path_tokens(context, lib, 0, &explib) == 0) {
131           cc_handle = dlopen(explib, RTLD_LAZY|RTLD_LOCAL);
132           free(explib);
133       }
134     }
135 #else
136     cc_handle = dlopen(lib, RTLD_LAZY|RTLD_LOCAL);
137 #endif
138 
139     if (cc_handle == NULL) {
140           HEIMDAL_MUTEX_unlock(&acc_mutex);
141           if (context)
142               krb5_set_error_message(context, KRB5_CC_NOSUPP,
143                                            N_("Failed to load API cache module %s", "file"),
144                                            lib);
145           return KRB5_CC_NOSUPP;
146     }
147 
148     init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
149     set_target_uid = (void (KRB5_CALLCONV *)(uid_t))
150           dlsym(cc_handle, "krb5_ipc_client_set_target_uid");
151     clear_target = (void (KRB5_CALLCONV *)(void))
152           dlsym(cc_handle, "krb5_ipc_client_clear_target");
153     HEIMDAL_MUTEX_unlock(&acc_mutex);
154     if (init_func == NULL) {
155           if (context)
156               krb5_set_error_message(context, KRB5_CC_NOSUPP,
157                                            N_("Failed to find cc_initialize"
158                                               "in %s: %s", "file, error"), lib, dlerror());
159           dlclose(cc_handle);
160           return KRB5_CC_NOSUPP;
161     }
162 
163     return 0;
164 #else
165     HEIMDAL_MUTEX_unlock(&acc_mutex);
166     if (context)
167           krb5_set_error_message(context, KRB5_CC_NOSUPP,
168                                      N_("no support for shared object", ""));
169     return KRB5_CC_NOSUPP;
170 #endif
171 }
172 
173 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_heim_krb5_ipc_client_set_target_uid(uid_t uid)174 _heim_krb5_ipc_client_set_target_uid(uid_t uid)
175 {
176     init_ccapi(NULL);
177     if (set_target_uid != NULL)
178         (*set_target_uid)(uid);
179 }
180 
181 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_heim_krb5_ipc_client_clear_target(void)182 _heim_krb5_ipc_client_clear_target(void)
183 {
184     init_ccapi(NULL);
185     if (clear_target != NULL)
186         (*clear_target)();
187 }
188 
189 static krb5_error_code
make_cred_from_ccred(krb5_context context,const cc_credentials_v5_t * incred,krb5_creds * cred)190 make_cred_from_ccred(krb5_context context,
191                          const cc_credentials_v5_t *incred,
192                          krb5_creds *cred)
193 {
194     krb5_error_code ret;
195     unsigned int i;
196 
197     memset(cred, 0, sizeof(*cred));
198 
199     ret = krb5_parse_name(context, incred->client, &cred->client);
200     if (ret)
201           goto fail;
202 
203     ret = krb5_parse_name(context, incred->server, &cred->server);
204     if (ret)
205           goto fail;
206 
207     cred->session.keytype = incred->keyblock.type;
208     cred->session.keyvalue.length = incred->keyblock.length;
209     cred->session.keyvalue.data = malloc(incred->keyblock.length);
210     if (cred->session.keyvalue.data == NULL)
211           goto nomem;
212     memcpy(cred->session.keyvalue.data, incred->keyblock.data,
213              incred->keyblock.length);
214 
215     cred->times.authtime = incred->authtime;
216     cred->times.starttime = incred->starttime;
217     cred->times.endtime = incred->endtime;
218     cred->times.renew_till = incred->renew_till;
219 
220     ret = krb5_data_copy(&cred->ticket,
221                                incred->ticket.data,
222                                incred->ticket.length);
223     if (ret)
224           goto nomem;
225 
226     ret = krb5_data_copy(&cred->second_ticket,
227                                incred->second_ticket.data,
228                                incred->second_ticket.length);
229     if (ret)
230           goto nomem;
231 
232     cred->authdata.val = NULL;
233     cred->authdata.len = 0;
234 
235     cred->addresses.val = NULL;
236     cred->addresses.len = 0;
237 
238     for (i = 0; incred->authdata && incred->authdata[i]; i++)
239           ;
240 
241     if (i) {
242           cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
243           if (cred->authdata.val == NULL)
244               goto nomem;
245           cred->authdata.len = i;
246           for (i = 0; i < cred->authdata.len; i++) {
247               cred->authdata.val[i].ad_type = incred->authdata[i]->type;
248               ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
249                                          incred->authdata[i]->data,
250                                          incred->authdata[i]->length);
251               if (ret)
252                     goto nomem;
253           }
254     }
255 
256     for (i = 0; incred->addresses && incred->addresses[i]; i++)
257           ;
258 
259     if (i) {
260           cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
261           if (cred->addresses.val == NULL)
262               goto nomem;
263           cred->addresses.len = i;
264 
265           for (i = 0; i < cred->addresses.len; i++) {
266               cred->addresses.val[i].addr_type = incred->addresses[i]->type;
267               ret = krb5_data_copy(&cred->addresses.val[i].address,
268                                          incred->addresses[i]->data,
269                                          incred->addresses[i]->length);
270               if (ret)
271                     goto nomem;
272           }
273     }
274 
275     cred->flags.i = 0;
276     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
277           cred->flags.b.forwardable = 1;
278     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
279           cred->flags.b.forwarded = 1;
280     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
281           cred->flags.b.proxiable = 1;
282     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
283           cred->flags.b.proxy = 1;
284     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
285           cred->flags.b.may_postdate = 1;
286     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
287           cred->flags.b.postdated = 1;
288     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
289           cred->flags.b.invalid = 1;
290     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
291           cred->flags.b.renewable = 1;
292     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
293           cred->flags.b.initial = 1;
294     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
295           cred->flags.b.pre_authent = 1;
296     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
297           cred->flags.b.hw_authent = 1;
298     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
299           cred->flags.b.transited_policy_checked = 1;
300     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
301           cred->flags.b.ok_as_delegate = 1;
302     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
303           cred->flags.b.anonymous = 1;
304 
305     return 0;
306 
307 nomem:
308     ret = krb5_enomem(context);
309 
310 fail:
311     krb5_free_cred_contents(context, cred);
312     return ret;
313 }
314 
315 static void
free_ccred(cc_credentials_v5_t * cred)316 free_ccred(cc_credentials_v5_t *cred)
317 {
318     int i;
319 
320     if (cred->addresses) {
321           for (i = 0; cred->addresses[i] != 0; i++) {
322               if (cred->addresses[i]->data)
323                     free(cred->addresses[i]->data);
324               free(cred->addresses[i]);
325           }
326           free(cred->addresses);
327     }
328     if (cred->server)
329           free(cred->server);
330     if (cred->client)
331           free(cred->client);
332     memset(cred, 0, sizeof(*cred));
333 }
334 
335 static krb5_error_code
make_ccred_from_cred(krb5_context context,const krb5_creds * incred,cc_credentials_v5_t * cred)336 make_ccred_from_cred(krb5_context context,
337                          const krb5_creds *incred,
338                          cc_credentials_v5_t *cred)
339 {
340     krb5_error_code ret;
341     size_t i;
342 
343     memset(cred, 0, sizeof(*cred));
344 
345     ret = krb5_unparse_name(context, incred->client, &cred->client);
346     if (ret)
347           goto fail;
348 
349     ret = krb5_unparse_name(context, incred->server, &cred->server);
350     if (ret)
351           goto fail;
352 
353     cred->keyblock.type = incred->session.keytype;
354     cred->keyblock.length = incred->session.keyvalue.length;
355     cred->keyblock.data = incred->session.keyvalue.data;
356 
357     cred->authtime = incred->times.authtime;
358     cred->starttime = incred->times.starttime;
359     cred->endtime = incred->times.endtime;
360     cred->renew_till = incred->times.renew_till;
361 
362     cred->ticket.length = incred->ticket.length;
363     cred->ticket.data = incred->ticket.data;
364 
365     cred->second_ticket.length = incred->second_ticket.length;
366     cred->second_ticket.data = incred->second_ticket.data;
367 
368     /* XXX this one should also be filled in */
369     cred->authdata = NULL;
370 
371     cred->addresses = calloc(incred->addresses.len + 1,
372                                    sizeof(cred->addresses[0]));
373     if (cred->addresses == NULL) {
374 
375           ret = ENOMEM;
376           goto fail;
377     }
378 
379     for (i = 0; i < incred->addresses.len; i++) {
380           cc_data *addr;
381           addr = malloc(sizeof(*addr));
382           if (addr == NULL) {
383               ret = ENOMEM;
384               goto fail;
385           }
386           addr->type = incred->addresses.val[i].addr_type;
387           addr->length = incred->addresses.val[i].address.length;
388           addr->data = malloc(addr->length);
389           if (addr->data == NULL) {
390               free(addr);
391               ret = ENOMEM;
392               goto fail;
393           }
394           memcpy(addr->data, incred->addresses.val[i].address.data,
395                  addr->length);
396           cred->addresses[i] = addr;
397     }
398     cred->addresses[i] = NULL;
399 
400     cred->ticket_flags = 0;
401     if (incred->flags.b.forwardable)
402           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
403     if (incred->flags.b.forwarded)
404           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
405     if (incred->flags.b.proxiable)
406           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
407     if (incred->flags.b.proxy)
408           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
409     if (incred->flags.b.may_postdate)
410           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
411     if (incred->flags.b.postdated)
412           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
413     if (incred->flags.b.invalid)
414           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
415     if (incred->flags.b.renewable)
416           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
417     if (incred->flags.b.initial)
418           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
419     if (incred->flags.b.pre_authent)
420           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
421     if (incred->flags.b.hw_authent)
422           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
423     if (incred->flags.b.transited_policy_checked)
424           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
425     if (incred->flags.b.ok_as_delegate)
426           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
427     if (incred->flags.b.anonymous)
428           cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
429 
430     return 0;
431 
432 fail:
433     free_ccred(cred);
434 
435     krb5_clear_error_message(context);
436     return ret;
437 }
438 
439 static cc_int32
get_cc_name(krb5_acc * a)440 get_cc_name(krb5_acc *a)
441 {
442     cc_string_t name;
443     cc_int32 error;
444 
445     error = (*a->ccache->func->get_name)(a->ccache, &name);
446     if (error)
447           return error;
448 
449     a->cache_name = strdup(name->data);
450     (*name->func->release)(name);
451     if (a->cache_name == NULL)
452           return ccErrNoMem;
453     return ccNoError;
454 }
455 
456 
457 static const char* KRB5_CALLCONV
acc_get_name(krb5_context context,krb5_ccache id)458 acc_get_name(krb5_context context,
459                krb5_ccache id)
460 {
461     krb5_acc *a = ACACHE(id);
462     int32_t error;
463 
464     if (a->cache_name == NULL) {
465           krb5_error_code ret;
466           krb5_principal principal;
467           char *name;
468 
469           ret = _krb5_get_default_principal_local(context, &principal);
470           if (ret)
471               return NULL;
472 
473           ret = krb5_unparse_name(context, principal, &name);
474           krb5_free_principal(context, principal);
475           if (ret)
476               return NULL;
477 
478           error = (*a->context->func->create_new_ccache)(a->context,
479                                                                    cc_credentials_v5,
480                                                                    name,
481                                                                    &a->ccache);
482           krb5_xfree(name);
483           if (error)
484               return NULL;
485 
486           error = get_cc_name(a);
487           if (error)
488               return NULL;
489     }
490 
491     return a->cache_name;
492 }
493 
494 static krb5_error_code KRB5_CALLCONV
acc_alloc(krb5_context context,krb5_ccache * id)495 acc_alloc(krb5_context context, krb5_ccache *id)
496 {
497     krb5_error_code ret;
498     cc_int32 error;
499     krb5_acc *a;
500 
501     ret = init_ccapi(context);
502     if (ret)
503           return ret;
504 
505     ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
506     if (ret) {
507           krb5_clear_error_message(context);
508           return ret;
509     }
510 
511     a = ACACHE(*id);
512 
513     error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
514     if (error) {
515           krb5_data_free(&(*id)->data);
516           return translate_cc_error(context, error);
517     }
518 
519     a->cache_name = NULL;
520 
521     return 0;
522 }
523 
524 static krb5_error_code KRB5_CALLCONV
acc_resolve(krb5_context context,krb5_ccache * id,const char * res)525 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
526 {
527     krb5_error_code ret;
528     cc_int32 error;
529     krb5_acc *a;
530 
531     ret = acc_alloc(context, id);
532     if (ret)
533           return ret;
534 
535     a = ACACHE(*id);
536 
537     error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
538     if (error == ccNoError) {
539           cc_time_t offset;
540           error = get_cc_name(a);
541           if (error != ccNoError) {
542               acc_close(context, *id);
543               *id = NULL;
544               return translate_cc_error(context, error);
545           }
546 
547           error = (*a->ccache->func->get_kdc_time_offset)(a->ccache,
548                                                                       cc_credentials_v5,
549                                                                       &offset);
550           if (error == 0)
551               context->kdc_sec_offset = offset;
552 
553     } else if (error == ccErrCCacheNotFound) {
554           a->ccache = NULL;
555           a->cache_name = NULL;
556     } else {
557           *id = NULL;
558           return translate_cc_error(context, error);
559     }
560 
561     return 0;
562 }
563 
564 static krb5_error_code KRB5_CALLCONV
acc_gen_new(krb5_context context,krb5_ccache * id)565 acc_gen_new(krb5_context context, krb5_ccache *id)
566 {
567     krb5_error_code ret;
568     krb5_acc *a;
569 
570     ret = acc_alloc(context, id);
571     if (ret)
572           return ret;
573 
574     a = ACACHE(*id);
575 
576     a->ccache = NULL;
577     a->cache_name = NULL;
578 
579     return 0;
580 }
581 
582 static krb5_error_code KRB5_CALLCONV
acc_initialize(krb5_context context,krb5_ccache id,krb5_principal primary_principal)583 acc_initialize(krb5_context context,
584                  krb5_ccache id,
585                  krb5_principal primary_principal)
586 {
587     krb5_acc *a = ACACHE(id);
588     krb5_error_code ret;
589     int32_t error;
590     char *name;
591 
592     ret = krb5_unparse_name(context, primary_principal, &name);
593     if (ret)
594           return ret;
595 
596     if (a->cache_name == NULL) {
597           error = (*a->context->func->create_new_ccache)(a->context,
598                                                                    cc_credentials_v5,
599                                                                    name,
600                                                                    &a->ccache);
601           free(name);
602           if (error == ccNoError)
603               error = get_cc_name(a);
604     } else {
605           cc_credentials_iterator_t iter;
606           cc_credentials_t ccred;
607 
608           error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
609           if (error) {
610               free(name);
611               return translate_cc_error(context, error);
612           }
613 
614           while (1) {
615               error = (*iter->func->next)(iter, &ccred);
616               if (error)
617                     break;
618               (*a->ccache->func->remove_credentials)(a->ccache, ccred);
619               (*ccred->func->release)(ccred);
620           }
621           (*iter->func->release)(iter);
622 
623           error = (*a->ccache->func->set_principal)(a->ccache,
624                                                               cc_credentials_v5,
625                                                               name);
626     }
627 
628     if (error == 0 && context->kdc_sec_offset)
629           error = (*a->ccache->func->set_kdc_time_offset)(a->ccache,
630                                                                       cc_credentials_v5,
631                                                                       context->kdc_sec_offset);
632 
633     return translate_cc_error(context, error);
634 }
635 
636 static krb5_error_code KRB5_CALLCONV
acc_close(krb5_context context,krb5_ccache id)637 acc_close(krb5_context context,
638             krb5_ccache id)
639 {
640     krb5_acc *a = ACACHE(id);
641 
642     if (a->ccache) {
643           (*a->ccache->func->release)(a->ccache);
644           a->ccache = NULL;
645     }
646     if (a->cache_name) {
647           free(a->cache_name);
648           a->cache_name = NULL;
649     }
650     if (a->context) {
651           (*a->context->func->release)(a->context);
652           a->context = NULL;
653     }
654     krb5_data_free(&id->data);
655     return 0;
656 }
657 
658 static krb5_error_code KRB5_CALLCONV
acc_destroy(krb5_context context,krb5_ccache id)659 acc_destroy(krb5_context context,
660               krb5_ccache id)
661 {
662     krb5_acc *a = ACACHE(id);
663     cc_int32 error = 0;
664 
665     if (a->ccache) {
666           error = (*a->ccache->func->destroy)(a->ccache);
667           a->ccache = NULL;
668     }
669     if (a->context) {
670           error = (a->context->func->release)(a->context);
671           a->context = NULL;
672     }
673     return translate_cc_error(context, error);
674 }
675 
676 static krb5_error_code KRB5_CALLCONV
acc_store_cred(krb5_context context,krb5_ccache id,krb5_creds * creds)677 acc_store_cred(krb5_context context,
678                  krb5_ccache id,
679                  krb5_creds *creds)
680 {
681     krb5_acc *a = ACACHE(id);
682     cc_credentials_union cred;
683     cc_credentials_v5_t v5cred;
684     krb5_error_code ret;
685     cc_int32 error;
686 
687     if (a->ccache == NULL) {
688           krb5_set_error_message(context, KRB5_CC_NOTFOUND,
689                                      N_("No API credential found", ""));
690           return KRB5_CC_NOTFOUND;
691     }
692 
693     cred.version = cc_credentials_v5;
694     cred.credentials.credentials_v5 = &v5cred;
695 
696     ret = make_ccred_from_cred(context,
697                                      creds,
698                                      &v5cred);
699     if (ret)
700           return ret;
701 
702     error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
703     if (error)
704           ret = translate_cc_error(context, error);
705 
706     free_ccred(&v5cred);
707 
708     return ret;
709 }
710 
711 static krb5_error_code KRB5_CALLCONV
acc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * principal)712 acc_get_principal(krb5_context context,
713                       krb5_ccache id,
714                       krb5_principal *principal)
715 {
716     krb5_acc *a = ACACHE(id);
717     krb5_error_code ret;
718     int32_t error;
719     cc_string_t name;
720 
721     if (a->ccache == NULL) {
722           krb5_set_error_message(context, KRB5_CC_NOTFOUND,
723                                      N_("No API credential found", ""));
724           return KRB5_CC_NOTFOUND;
725     }
726 
727     error = (*a->ccache->func->get_principal)(a->ccache,
728                                                         cc_credentials_v5,
729                                                         &name);
730     if (error)
731           return translate_cc_error(context, error);
732 
733     ret = krb5_parse_name(context, name->data, principal);
734 
735     (*name->func->release)(name);
736     return ret;
737 }
738 
739 static krb5_error_code KRB5_CALLCONV
acc_get_first(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)740 acc_get_first (krb5_context context,
741                  krb5_ccache id,
742                  krb5_cc_cursor *cursor)
743 {
744     cc_credentials_iterator_t iter;
745     krb5_acc *a = ACACHE(id);
746     int32_t error;
747 
748     if (a->ccache == NULL) {
749           krb5_set_error_message(context, KRB5_CC_NOTFOUND,
750                                      N_("No API credential found", ""));
751           return KRB5_CC_NOTFOUND;
752     }
753 
754     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
755     if (error) {
756           krb5_clear_error_message(context);
757           return ENOENT;
758     }
759     *cursor = iter;
760     return 0;
761 }
762 
763 
764 static krb5_error_code KRB5_CALLCONV
acc_get_next(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)765 acc_get_next (krb5_context context,
766                 krb5_ccache id,
767                 krb5_cc_cursor *cursor,
768                 krb5_creds *creds)
769 {
770     cc_credentials_iterator_t iter = *cursor;
771     cc_credentials_t cred;
772     krb5_error_code ret;
773     int32_t error;
774 
775     while (1) {
776           error = (*iter->func->next)(iter, &cred);
777           if (error)
778               return translate_cc_error(context, error);
779           if (cred->data->version == cc_credentials_v5)
780               break;
781           (*cred->func->release)(cred);
782     }
783 
784     ret = make_cred_from_ccred(context,
785                                      cred->data->credentials.credentials_v5,
786                                      creds);
787     (*cred->func->release)(cred);
788     return ret;
789 }
790 
791 static krb5_error_code KRB5_CALLCONV
acc_end_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)792 acc_end_get (krb5_context context,
793                krb5_ccache id,
794                krb5_cc_cursor *cursor)
795 {
796     cc_credentials_iterator_t iter = *cursor;
797     (*iter->func->release)(iter);
798     return 0;
799 }
800 
801 static krb5_error_code KRB5_CALLCONV
acc_remove_cred(krb5_context context,krb5_ccache id,krb5_flags which,krb5_creds * cred)802 acc_remove_cred(krb5_context context,
803                     krb5_ccache id,
804                     krb5_flags which,
805                     krb5_creds *cred)
806 {
807     cc_credentials_iterator_t iter;
808     krb5_acc *a = ACACHE(id);
809     cc_credentials_t ccred;
810     krb5_error_code ret;
811     cc_int32 error;
812     char *client, *server;
813 
814     if (a->ccache == NULL) {
815           krb5_set_error_message(context, KRB5_CC_NOTFOUND,
816                                      N_("No API credential found", ""));
817           return KRB5_CC_NOTFOUND;
818     }
819 
820     if (cred->client) {
821           ret = krb5_unparse_name(context, cred->client, &client);
822           if (ret)
823               return ret;
824     } else
825           client = NULL;
826 
827     ret = krb5_unparse_name(context, cred->server, &server);
828     if (ret) {
829           free(client);
830           return ret;
831     }
832 
833     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
834     if (error) {
835           free(server);
836           free(client);
837           return translate_cc_error(context, error);
838     }
839 
840     ret = KRB5_CC_NOTFOUND;
841     while (1) {
842           cc_credentials_v5_t *v5cred;
843 
844           error = (*iter->func->next)(iter, &ccred);
845           if (error)
846               break;
847 
848           if (ccred->data->version != cc_credentials_v5)
849               goto next;
850 
851           v5cred = ccred->data->credentials.credentials_v5;
852 
853           if (client && strcmp(v5cred->client, client) != 0)
854               goto next;
855 
856           if (strcmp(v5cred->server, server) != 0)
857               goto next;
858 
859           (*a->ccache->func->remove_credentials)(a->ccache, ccred);
860           ret = 0;
861     next:
862           (*ccred->func->release)(ccred);
863     }
864 
865     (*iter->func->release)(iter);
866 
867     if (ret)
868           krb5_set_error_message(context, ret,
869                                      N_("Can't find credential %s in cache",
870                                          "principal"), server);
871     free(server);
872     free(client);
873 
874     return ret;
875 }
876 
877 static krb5_error_code KRB5_CALLCONV
acc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)878 acc_set_flags(krb5_context context,
879                 krb5_ccache id,
880                 krb5_flags flags)
881 {
882     return 0;
883 }
884 
885 static int KRB5_CALLCONV
acc_get_version(krb5_context context,krb5_ccache id)886 acc_get_version(krb5_context context,
887                     krb5_ccache id)
888 {
889     return 0;
890 }
891 
892 struct cache_iter {
893     cc_context_t context;
894     cc_ccache_iterator_t iter;
895 };
896 
897 static krb5_error_code KRB5_CALLCONV
acc_get_cache_first(krb5_context context,krb5_cc_cursor * cursor)898 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
899 {
900     struct cache_iter *iter;
901     krb5_error_code ret;
902     cc_int32 error;
903 
904     ret = init_ccapi(context);
905     if (ret)
906           return ret;
907 
908     iter = calloc(1, sizeof(*iter));
909     if (iter == NULL)
910           return krb5_enomem(context);
911 
912     error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
913     if (error) {
914           free(iter);
915           return translate_cc_error(context, error);
916     }
917 
918     error = (*iter->context->func->new_ccache_iterator)(iter->context,
919                                                                       &iter->iter);
920     if (error) {
921           free(iter);
922           krb5_clear_error_message(context);
923           return ENOENT;
924     }
925     *cursor = iter;
926     return 0;
927 }
928 
929 static krb5_error_code KRB5_CALLCONV
acc_get_cache_next(krb5_context context,krb5_cc_cursor cursor,krb5_ccache * id)930 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
931 {
932     struct cache_iter *iter = cursor;
933     cc_ccache_t cache;
934     krb5_acc *a;
935     krb5_error_code ret;
936     int32_t error;
937 
938     error = (*iter->iter->func->next)(iter->iter, &cache);
939     if (error)
940           return translate_cc_error(context, error);
941 
942     ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
943     if (ret) {
944           (*cache->func->release)(cache);
945           return ret;
946     }
947 
948     ret = acc_alloc(context, id);
949     if (ret) {
950           (*cache->func->release)(cache);
951           free(*id);
952           return ret;
953     }
954 
955     a = ACACHE(*id);
956     a->ccache = cache;
957 
958     error = get_cc_name(a);
959     if (error) {
960           acc_close(context, *id);
961           *id = NULL;
962           return translate_cc_error(context, error);
963     }
964     return 0;
965 }
966 
967 static krb5_error_code KRB5_CALLCONV
acc_end_cache_get(krb5_context context,krb5_cc_cursor cursor)968 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
969 {
970     struct cache_iter *iter = cursor;
971 
972     (*iter->iter->func->release)(iter->iter);
973     iter->iter = NULL;
974     (*iter->context->func->release)(iter->context);
975     iter->context = NULL;
976     free(iter);
977     return 0;
978 }
979 
980 static krb5_error_code KRB5_CALLCONV
acc_move(krb5_context context,krb5_ccache from,krb5_ccache to)981 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
982 {
983     krb5_acc *afrom = ACACHE(from);
984     krb5_acc *ato = ACACHE(to);
985     int32_t error;
986 
987     if (ato->ccache == NULL) {
988           cc_string_t name;
989 
990           error = (*afrom->ccache->func->get_principal)(afrom->ccache,
991                                                                   cc_credentials_v5,
992                                                                   &name);
993           if (error)
994               return translate_cc_error(context, error);
995 
996           error = (*ato->context->func->create_new_ccache)(ato->context,
997                                                                        cc_credentials_v5,
998                                                                        name->data,
999                                                                        &ato->ccache);
1000           (*name->func->release)(name);
1001           if (error)
1002               return translate_cc_error(context, error);
1003     }
1004 
1005     error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
1006 
1007     acc_destroy(context, from);
1008 
1009     return translate_cc_error(context, error);
1010 }
1011 
1012 static krb5_error_code KRB5_CALLCONV
acc_get_default_name(krb5_context context,char ** str)1013 acc_get_default_name(krb5_context context, char **str)
1014 {
1015     krb5_error_code ret;
1016     cc_context_t cc;
1017     cc_string_t name;
1018     int32_t error;
1019 
1020     ret = init_ccapi(context);
1021     if (ret)
1022           return ret;
1023 
1024     error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
1025     if (error)
1026           return translate_cc_error(context, error);
1027 
1028     error = (*cc->func->get_default_ccache_name)(cc, &name);
1029     if (error) {
1030           (*cc->func->release)(cc);
1031           return translate_cc_error(context, error);
1032     }
1033 
1034     error = asprintf(str, "API:%s", name->data);
1035     (*name->func->release)(name);
1036     (*cc->func->release)(cc);
1037 
1038     if (error < 0 || *str == NULL)
1039           return krb5_enomem(context);
1040     return 0;
1041 }
1042 
1043 static krb5_error_code KRB5_CALLCONV
acc_set_default(krb5_context context,krb5_ccache id)1044 acc_set_default(krb5_context context, krb5_ccache id)
1045 {
1046     krb5_acc *a = ACACHE(id);
1047     cc_int32 error;
1048 
1049     if (a->ccache == NULL) {
1050           krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1051                                      N_("No API credential found", ""));
1052           return KRB5_CC_NOTFOUND;
1053     }
1054 
1055     error = (*a->ccache->func->set_default)(a->ccache);
1056     if (error)
1057           return translate_cc_error(context, error);
1058 
1059     return 0;
1060 }
1061 
1062 static krb5_error_code KRB5_CALLCONV
acc_lastchange(krb5_context context,krb5_ccache id,krb5_timestamp * mtime)1063 acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1064 {
1065     krb5_acc *a = ACACHE(id);
1066     cc_int32 error;
1067     cc_time_t t;
1068 
1069     if (a->ccache == NULL) {
1070           krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1071                                      N_("No API credential found", ""));
1072           return KRB5_CC_NOTFOUND;
1073     }
1074 
1075     error = (*a->ccache->func->get_change_time)(a->ccache, &t);
1076     if (error)
1077           return translate_cc_error(context, error);
1078 
1079     *mtime = t;
1080 
1081     return 0;
1082 }
1083 
1084 /**
1085  * Variable containing the API based credential cache implemention.
1086  *
1087  * @ingroup krb5_ccache
1088  */
1089 
1090 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
1091     KRB5_CC_OPS_VERSION,
1092     "API",
1093     acc_get_name,
1094     acc_resolve,
1095     acc_gen_new,
1096     acc_initialize,
1097     acc_destroy,
1098     acc_close,
1099     acc_store_cred,
1100     NULL, /* acc_retrieve */
1101     acc_get_principal,
1102     acc_get_first,
1103     acc_get_next,
1104     acc_end_get,
1105     acc_remove_cred,
1106     acc_set_flags,
1107     acc_get_version,
1108     acc_get_cache_first,
1109     acc_get_cache_next,
1110     acc_end_cache_get,
1111     acc_move,
1112     acc_get_default_name,
1113     acc_set_default,
1114     acc_lastchange,
1115     NULL,
1116     NULL,
1117 };
1118 
1119 #endif
1120