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