xref: /minix3/crypto/external/bsd/heimdal/dist/lib/krb5/acache.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1 /*	$NetBSD: acache.c,v 1.1.1.2 2014/04/24 12:45:49 pettai 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, &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 void
_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 void
_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 = ENOMEM;
309     krb5_set_error_message(context, ret, N_("malloc: out of memory", "malloc"));
310 
311 fail:
312     krb5_free_cred_contents(context, cred);
313     return ret;
314 }
315 
316 static void
free_ccred(cc_credentials_v5_t * cred)317 free_ccred(cc_credentials_v5_t *cred)
318 {
319     int i;
320 
321     if (cred->addresses) {
322 	for (i = 0; cred->addresses[i] != 0; i++) {
323 	    if (cred->addresses[i]->data)
324 		free(cred->addresses[i]->data);
325 	    free(cred->addresses[i]);
326 	}
327 	free(cred->addresses);
328     }
329     if (cred->server)
330 	free(cred->server);
331     if (cred->client)
332 	free(cred->client);
333     memset(cred, 0, sizeof(*cred));
334 }
335 
336 static krb5_error_code
make_ccred_from_cred(krb5_context context,const krb5_creds * incred,cc_credentials_v5_t * cred)337 make_ccred_from_cred(krb5_context context,
338 		     const krb5_creds *incred,
339 		     cc_credentials_v5_t *cred)
340 {
341     krb5_error_code ret;
342     size_t i;
343 
344     memset(cred, 0, sizeof(*cred));
345 
346     ret = krb5_unparse_name(context, incred->client, &cred->client);
347     if (ret)
348 	goto fail;
349 
350     ret = krb5_unparse_name(context, incred->server, &cred->server);
351     if (ret)
352 	goto fail;
353 
354     cred->keyblock.type = incred->session.keytype;
355     cred->keyblock.length = incred->session.keyvalue.length;
356     cred->keyblock.data = incred->session.keyvalue.data;
357 
358     cred->authtime = incred->times.authtime;
359     cred->starttime = incred->times.starttime;
360     cred->endtime = incred->times.endtime;
361     cred->renew_till = incred->times.renew_till;
362 
363     cred->ticket.length = incred->ticket.length;
364     cred->ticket.data = incred->ticket.data;
365 
366     cred->second_ticket.length = incred->second_ticket.length;
367     cred->second_ticket.data = incred->second_ticket.data;
368 
369     /* XXX this one should also be filled in */
370     cred->authdata = NULL;
371 
372     cred->addresses = calloc(incred->addresses.len + 1,
373 			     sizeof(cred->addresses[0]));
374     if (cred->addresses == NULL) {
375 
376 	ret = ENOMEM;
377 	goto fail;
378     }
379 
380     for (i = 0; i < incred->addresses.len; i++) {
381 	cc_data *addr;
382 	addr = malloc(sizeof(*addr));
383 	if (addr == NULL) {
384 	    ret = ENOMEM;
385 	    goto fail;
386 	}
387 	addr->type = incred->addresses.val[i].addr_type;
388 	addr->length = incred->addresses.val[i].address.length;
389 	addr->data = malloc(addr->length);
390 	if (addr->data == NULL) {
391 	    free(addr);
392 	    ret = ENOMEM;
393 	    goto fail;
394 	}
395 	memcpy(addr->data, incred->addresses.val[i].address.data,
396 	       addr->length);
397 	cred->addresses[i] = addr;
398     }
399     cred->addresses[i] = NULL;
400 
401     cred->ticket_flags = 0;
402     if (incred->flags.b.forwardable)
403 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
404     if (incred->flags.b.forwarded)
405 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
406     if (incred->flags.b.proxiable)
407 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
408     if (incred->flags.b.proxy)
409 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
410     if (incred->flags.b.may_postdate)
411 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
412     if (incred->flags.b.postdated)
413 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
414     if (incred->flags.b.invalid)
415 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
416     if (incred->flags.b.renewable)
417 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
418     if (incred->flags.b.initial)
419 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
420     if (incred->flags.b.pre_authent)
421 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
422     if (incred->flags.b.hw_authent)
423 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
424     if (incred->flags.b.transited_policy_checked)
425 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
426     if (incred->flags.b.ok_as_delegate)
427 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
428     if (incred->flags.b.anonymous)
429 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
430 
431     return 0;
432 
433 fail:
434     free_ccred(cred);
435 
436     krb5_clear_error_message(context);
437     return ret;
438 }
439 
440 static cc_int32
get_cc_name(krb5_acc * a)441 get_cc_name(krb5_acc *a)
442 {
443     cc_string_t name;
444     cc_int32 error;
445 
446     error = (*a->ccache->func->get_name)(a->ccache, &name);
447     if (error)
448 	return error;
449 
450     a->cache_name = strdup(name->data);
451     (*name->func->release)(name);
452     if (a->cache_name == NULL)
453 	return ccErrNoMem;
454     return ccNoError;
455 }
456 
457 
458 static const char* KRB5_CALLCONV
acc_get_name(krb5_context context,krb5_ccache id)459 acc_get_name(krb5_context context,
460 	     krb5_ccache id)
461 {
462     krb5_acc *a = ACACHE(id);
463     int32_t error;
464 
465     if (a->cache_name == NULL) {
466 	krb5_error_code ret;
467 	krb5_principal principal;
468 	char *name;
469 
470 	ret = _krb5_get_default_principal_local(context, &principal);
471 	if (ret)
472 	    return NULL;
473 
474 	ret = krb5_unparse_name(context, principal, &name);
475 	krb5_free_principal(context, principal);
476 	if (ret)
477 	    return NULL;
478 
479 	error = (*a->context->func->create_new_ccache)(a->context,
480 						       cc_credentials_v5,
481 						       name,
482 						       &a->ccache);
483 	krb5_xfree(name);
484 	if (error)
485 	    return NULL;
486 
487 	error = get_cc_name(a);
488 	if (error)
489 	    return NULL;
490     }
491 
492     return a->cache_name;
493 }
494 
495 static krb5_error_code KRB5_CALLCONV
acc_alloc(krb5_context context,krb5_ccache * id)496 acc_alloc(krb5_context context, krb5_ccache *id)
497 {
498     krb5_error_code ret;
499     cc_int32 error;
500     krb5_acc *a;
501 
502     ret = init_ccapi(context);
503     if (ret)
504 	return ret;
505 
506     ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
507     if (ret) {
508 	krb5_clear_error_message(context);
509 	return ret;
510     }
511 
512     a = ACACHE(*id);
513 
514     error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
515     if (error) {
516 	krb5_data_free(&(*id)->data);
517 	return translate_cc_error(context, error);
518     }
519 
520     a->cache_name = NULL;
521 
522     return 0;
523 }
524 
525 static krb5_error_code KRB5_CALLCONV
acc_resolve(krb5_context context,krb5_ccache * id,const char * res)526 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
527 {
528     krb5_error_code ret;
529     cc_int32 error;
530     krb5_acc *a;
531 
532     ret = acc_alloc(context, id);
533     if (ret)
534 	return ret;
535 
536     a = ACACHE(*id);
537 
538     error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
539     if (error == ccNoError) {
540 	cc_time_t offset;
541 	error = get_cc_name(a);
542 	if (error != ccNoError) {
543 	    acc_close(context, *id);
544 	    *id = NULL;
545 	    return translate_cc_error(context, error);
546 	}
547 
548 	error = (*a->ccache->func->get_kdc_time_offset)(a->ccache,
549 							cc_credentials_v5,
550 							&offset);
551 	if (error == 0)
552 	    context->kdc_sec_offset = offset;
553 
554     } else if (error == ccErrCCacheNotFound) {
555 	a->ccache = NULL;
556 	a->cache_name = NULL;
557     } else {
558 	*id = NULL;
559 	return translate_cc_error(context, error);
560     }
561 
562     return 0;
563 }
564 
565 static krb5_error_code KRB5_CALLCONV
acc_gen_new(krb5_context context,krb5_ccache * id)566 acc_gen_new(krb5_context context, krb5_ccache *id)
567 {
568     krb5_error_code ret;
569     krb5_acc *a;
570 
571     ret = acc_alloc(context, id);
572     if (ret)
573 	return ret;
574 
575     a = ACACHE(*id);
576 
577     a->ccache = NULL;
578     a->cache_name = NULL;
579 
580     return 0;
581 }
582 
583 static krb5_error_code KRB5_CALLCONV
acc_initialize(krb5_context context,krb5_ccache id,krb5_principal primary_principal)584 acc_initialize(krb5_context context,
585 	       krb5_ccache id,
586 	       krb5_principal primary_principal)
587 {
588     krb5_acc *a = ACACHE(id);
589     krb5_error_code ret;
590     int32_t error;
591     char *name;
592 
593     ret = krb5_unparse_name(context, primary_principal, &name);
594     if (ret)
595 	return ret;
596 
597     if (a->cache_name == NULL) {
598 	error = (*a->context->func->create_new_ccache)(a->context,
599 						       cc_credentials_v5,
600 						       name,
601 						       &a->ccache);
602 	free(name);
603 	if (error == ccNoError)
604 	    error = get_cc_name(a);
605     } else {
606 	cc_credentials_iterator_t iter;
607 	cc_credentials_t ccred;
608 
609 	error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
610 	if (error) {
611 	    free(name);
612 	    return translate_cc_error(context, error);
613 	}
614 
615 	while (1) {
616 	    error = (*iter->func->next)(iter, &ccred);
617 	    if (error)
618 		break;
619 	    (*a->ccache->func->remove_credentials)(a->ccache, ccred);
620 	    (*ccred->func->release)(ccred);
621 	}
622 	(*iter->func->release)(iter);
623 
624 	error = (*a->ccache->func->set_principal)(a->ccache,
625 						  cc_credentials_v5,
626 						  name);
627     }
628 
629     if (error == 0 && context->kdc_sec_offset)
630 	error = (*a->ccache->func->set_kdc_time_offset)(a->ccache,
631 							cc_credentials_v5,
632 							context->kdc_sec_offset);
633 
634     return translate_cc_error(context, error);
635 }
636 
637 static krb5_error_code KRB5_CALLCONV
acc_close(krb5_context context,krb5_ccache id)638 acc_close(krb5_context context,
639 	  krb5_ccache id)
640 {
641     krb5_acc *a = ACACHE(id);
642 
643     if (a->ccache) {
644 	(*a->ccache->func->release)(a->ccache);
645 	a->ccache = NULL;
646     }
647     if (a->cache_name) {
648 	free(a->cache_name);
649 	a->cache_name = NULL;
650     }
651     if (a->context) {
652 	(*a->context->func->release)(a->context);
653 	a->context = NULL;
654     }
655     krb5_data_free(&id->data);
656     return 0;
657 }
658 
659 static krb5_error_code KRB5_CALLCONV
acc_destroy(krb5_context context,krb5_ccache id)660 acc_destroy(krb5_context context,
661 	    krb5_ccache id)
662 {
663     krb5_acc *a = ACACHE(id);
664     cc_int32 error = 0;
665 
666     if (a->ccache) {
667 	error = (*a->ccache->func->destroy)(a->ccache);
668 	a->ccache = NULL;
669     }
670     if (a->context) {
671 	error = (a->context->func->release)(a->context);
672 	a->context = NULL;
673     }
674     return translate_cc_error(context, error);
675 }
676 
677 static krb5_error_code KRB5_CALLCONV
acc_store_cred(krb5_context context,krb5_ccache id,krb5_creds * creds)678 acc_store_cred(krb5_context context,
679 	       krb5_ccache id,
680 	       krb5_creds *creds)
681 {
682     krb5_acc *a = ACACHE(id);
683     cc_credentials_union cred;
684     cc_credentials_v5_t v5cred;
685     krb5_error_code ret;
686     cc_int32 error;
687 
688     if (a->ccache == NULL) {
689 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
690 			       N_("No API credential found", ""));
691 	return KRB5_CC_NOTFOUND;
692     }
693 
694     cred.version = cc_credentials_v5;
695     cred.credentials.credentials_v5 = &v5cred;
696 
697     ret = make_ccred_from_cred(context,
698 			       creds,
699 			       &v5cred);
700     if (ret)
701 	return ret;
702 
703     error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
704     if (error)
705 	ret = translate_cc_error(context, error);
706 
707     free_ccred(&v5cred);
708 
709     return ret;
710 }
711 
712 static krb5_error_code KRB5_CALLCONV
acc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * principal)713 acc_get_principal(krb5_context context,
714 		  krb5_ccache id,
715 		  krb5_principal *principal)
716 {
717     krb5_acc *a = ACACHE(id);
718     krb5_error_code ret;
719     int32_t error;
720     cc_string_t name;
721 
722     if (a->ccache == NULL) {
723 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
724 			       N_("No API credential found", ""));
725 	return KRB5_CC_NOTFOUND;
726     }
727 
728     error = (*a->ccache->func->get_principal)(a->ccache,
729 					      cc_credentials_v5,
730 					      &name);
731     if (error)
732 	return translate_cc_error(context, error);
733 
734     ret = krb5_parse_name(context, name->data, principal);
735 
736     (*name->func->release)(name);
737     return ret;
738 }
739 
740 static krb5_error_code KRB5_CALLCONV
acc_get_first(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)741 acc_get_first (krb5_context context,
742 	       krb5_ccache id,
743 	       krb5_cc_cursor *cursor)
744 {
745     cc_credentials_iterator_t iter;
746     krb5_acc *a = ACACHE(id);
747     int32_t error;
748 
749     if (a->ccache == NULL) {
750 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
751 			       N_("No API credential found", ""));
752 	return KRB5_CC_NOTFOUND;
753     }
754 
755     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
756     if (error) {
757 	krb5_clear_error_message(context);
758 	return ENOENT;
759     }
760     *cursor = iter;
761     return 0;
762 }
763 
764 
765 static krb5_error_code KRB5_CALLCONV
acc_get_next(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)766 acc_get_next (krb5_context context,
767 	      krb5_ccache id,
768 	      krb5_cc_cursor *cursor,
769 	      krb5_creds *creds)
770 {
771     cc_credentials_iterator_t iter = *cursor;
772     cc_credentials_t cred;
773     krb5_error_code ret;
774     int32_t error;
775 
776     while (1) {
777 	error = (*iter->func->next)(iter, &cred);
778 	if (error)
779 	    return translate_cc_error(context, error);
780 	if (cred->data->version == cc_credentials_v5)
781 	    break;
782 	(*cred->func->release)(cred);
783     }
784 
785     ret = make_cred_from_ccred(context,
786 			       cred->data->credentials.credentials_v5,
787 			       creds);
788     (*cred->func->release)(cred);
789     return ret;
790 }
791 
792 static krb5_error_code KRB5_CALLCONV
acc_end_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)793 acc_end_get (krb5_context context,
794 	     krb5_ccache id,
795 	     krb5_cc_cursor *cursor)
796 {
797     cc_credentials_iterator_t iter = *cursor;
798     (*iter->func->release)(iter);
799     return 0;
800 }
801 
802 static krb5_error_code KRB5_CALLCONV
acc_remove_cred(krb5_context context,krb5_ccache id,krb5_flags which,krb5_creds * cred)803 acc_remove_cred(krb5_context context,
804 		krb5_ccache id,
805 		krb5_flags which,
806 		krb5_creds *cred)
807 {
808     cc_credentials_iterator_t iter;
809     krb5_acc *a = ACACHE(id);
810     cc_credentials_t ccred;
811     krb5_error_code ret;
812     cc_int32 error;
813     char *client, *server;
814 
815     if (a->ccache == NULL) {
816 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
817 			       N_("No API credential found", ""));
818 	return KRB5_CC_NOTFOUND;
819     }
820 
821     if (cred->client) {
822 	ret = krb5_unparse_name(context, cred->client, &client);
823 	if (ret)
824 	    return ret;
825     } else
826 	client = NULL;
827 
828     ret = krb5_unparse_name(context, cred->server, &server);
829     if (ret) {
830 	free(client);
831 	return ret;
832     }
833 
834     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
835     if (error) {
836 	free(server);
837 	free(client);
838 	return translate_cc_error(context, error);
839     }
840 
841     ret = KRB5_CC_NOTFOUND;
842     while (1) {
843 	cc_credentials_v5_t *v5cred;
844 
845 	error = (*iter->func->next)(iter, &ccred);
846 	if (error)
847 	    break;
848 
849 	if (ccred->data->version != cc_credentials_v5)
850 	    goto next;
851 
852 	v5cred = ccred->data->credentials.credentials_v5;
853 
854 	if (client && strcmp(v5cred->client, client) != 0)
855 	    goto next;
856 
857 	if (strcmp(v5cred->server, server) != 0)
858 	    goto next;
859 
860 	(*a->ccache->func->remove_credentials)(a->ccache, ccred);
861 	ret = 0;
862     next:
863 	(*ccred->func->release)(ccred);
864     }
865 
866     (*iter->func->release)(iter);
867 
868     if (ret)
869 	krb5_set_error_message(context, ret,
870 			       N_("Can't find credential %s in cache",
871 				 "principal"), server);
872     free(server);
873     free(client);
874 
875     return ret;
876 }
877 
878 static krb5_error_code KRB5_CALLCONV
acc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)879 acc_set_flags(krb5_context context,
880 	      krb5_ccache id,
881 	      krb5_flags flags)
882 {
883     return 0;
884 }
885 
886 static int KRB5_CALLCONV
acc_get_version(krb5_context context,krb5_ccache id)887 acc_get_version(krb5_context context,
888 		krb5_ccache id)
889 {
890     return 0;
891 }
892 
893 struct cache_iter {
894     cc_context_t context;
895     cc_ccache_iterator_t iter;
896 };
897 
898 static krb5_error_code KRB5_CALLCONV
acc_get_cache_first(krb5_context context,krb5_cc_cursor * cursor)899 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
900 {
901     struct cache_iter *iter;
902     krb5_error_code ret;
903     cc_int32 error;
904 
905     ret = init_ccapi(context);
906     if (ret)
907 	return ret;
908 
909     iter = calloc(1, sizeof(*iter));
910     if (iter == NULL) {
911 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
912 	return ENOMEM;
913     }
914 
915     error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
916     if (error) {
917 	free(iter);
918 	return translate_cc_error(context, error);
919     }
920 
921     error = (*iter->context->func->new_ccache_iterator)(iter->context,
922 							&iter->iter);
923     if (error) {
924 	free(iter);
925 	krb5_clear_error_message(context);
926 	return ENOENT;
927     }
928     *cursor = iter;
929     return 0;
930 }
931 
932 static krb5_error_code KRB5_CALLCONV
acc_get_cache_next(krb5_context context,krb5_cc_cursor cursor,krb5_ccache * id)933 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
934 {
935     struct cache_iter *iter = cursor;
936     cc_ccache_t cache;
937     krb5_acc *a;
938     krb5_error_code ret;
939     int32_t error;
940 
941     error = (*iter->iter->func->next)(iter->iter, &cache);
942     if (error)
943 	return translate_cc_error(context, error);
944 
945     ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
946     if (ret) {
947 	(*cache->func->release)(cache);
948 	return ret;
949     }
950 
951     ret = acc_alloc(context, id);
952     if (ret) {
953 	(*cache->func->release)(cache);
954 	free(*id);
955 	return ret;
956     }
957 
958     a = ACACHE(*id);
959     a->ccache = cache;
960 
961     error = get_cc_name(a);
962     if (error) {
963 	acc_close(context, *id);
964 	*id = NULL;
965 	return translate_cc_error(context, error);
966     }
967     return 0;
968 }
969 
970 static krb5_error_code KRB5_CALLCONV
acc_end_cache_get(krb5_context context,krb5_cc_cursor cursor)971 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
972 {
973     struct cache_iter *iter = cursor;
974 
975     (*iter->iter->func->release)(iter->iter);
976     iter->iter = NULL;
977     (*iter->context->func->release)(iter->context);
978     iter->context = NULL;
979     free(iter);
980     return 0;
981 }
982 
983 static krb5_error_code KRB5_CALLCONV
acc_move(krb5_context context,krb5_ccache from,krb5_ccache to)984 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
985 {
986     krb5_acc *afrom = ACACHE(from);
987     krb5_acc *ato = ACACHE(to);
988     int32_t error;
989 
990     if (ato->ccache == NULL) {
991 	cc_string_t name;
992 
993 	error = (*afrom->ccache->func->get_principal)(afrom->ccache,
994 						      cc_credentials_v5,
995 						      &name);
996 	if (error)
997 	    return translate_cc_error(context, error);
998 
999 	error = (*ato->context->func->create_new_ccache)(ato->context,
1000 							 cc_credentials_v5,
1001 							 name->data,
1002 							 &ato->ccache);
1003 	(*name->func->release)(name);
1004 	if (error)
1005 	    return translate_cc_error(context, error);
1006     }
1007 
1008     error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
1009 
1010     acc_destroy(context, from);
1011 
1012     return translate_cc_error(context, error);
1013 }
1014 
1015 static krb5_error_code KRB5_CALLCONV
acc_get_default_name(krb5_context context,char ** str)1016 acc_get_default_name(krb5_context context, char **str)
1017 {
1018     krb5_error_code ret;
1019     cc_context_t cc;
1020     cc_string_t name;
1021     int32_t error;
1022 
1023     ret = init_ccapi(context);
1024     if (ret)
1025 	return ret;
1026 
1027     error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
1028     if (error)
1029 	return translate_cc_error(context, error);
1030 
1031     error = (*cc->func->get_default_ccache_name)(cc, &name);
1032     if (error) {
1033 	(*cc->func->release)(cc);
1034 	return translate_cc_error(context, error);
1035     }
1036 
1037     error = asprintf(str, "API:%s", name->data);
1038     (*name->func->release)(name);
1039     (*cc->func->release)(cc);
1040 
1041     if (error < 0 || *str == NULL) {
1042 	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1043 	return ENOMEM;
1044     }
1045     return 0;
1046 }
1047 
1048 static krb5_error_code KRB5_CALLCONV
acc_set_default(krb5_context context,krb5_ccache id)1049 acc_set_default(krb5_context context, krb5_ccache id)
1050 {
1051     krb5_acc *a = ACACHE(id);
1052     cc_int32 error;
1053 
1054     if (a->ccache == NULL) {
1055 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1056 			       N_("No API credential found", ""));
1057 	return KRB5_CC_NOTFOUND;
1058     }
1059 
1060     error = (*a->ccache->func->set_default)(a->ccache);
1061     if (error)
1062 	return translate_cc_error(context, error);
1063 
1064     return 0;
1065 }
1066 
1067 static krb5_error_code KRB5_CALLCONV
acc_lastchange(krb5_context context,krb5_ccache id,krb5_timestamp * mtime)1068 acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1069 {
1070     krb5_acc *a = ACACHE(id);
1071     cc_int32 error;
1072     cc_time_t t;
1073 
1074     if (a->ccache == NULL) {
1075 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1076 			       N_("No API credential found", ""));
1077 	return KRB5_CC_NOTFOUND;
1078     }
1079 
1080     error = (*a->ccache->func->get_change_time)(a->ccache, &t);
1081     if (error)
1082 	return translate_cc_error(context, error);
1083 
1084     *mtime = t;
1085 
1086     return 0;
1087 }
1088 
1089 /**
1090  * Variable containing the API based credential cache implemention.
1091  *
1092  * @ingroup krb5_ccache
1093  */
1094 
1095 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
1096     KRB5_CC_OPS_VERSION,
1097     "API",
1098     acc_get_name,
1099     acc_resolve,
1100     acc_gen_new,
1101     acc_initialize,
1102     acc_destroy,
1103     acc_close,
1104     acc_store_cred,
1105     NULL, /* acc_retrieve */
1106     acc_get_principal,
1107     acc_get_first,
1108     acc_get_next,
1109     acc_end_get,
1110     acc_remove_cred,
1111     acc_set_flags,
1112     acc_get_version,
1113     acc_get_cache_first,
1114     acc_get_cache_next,
1115     acc_end_cache_get,
1116     acc_move,
1117     acc_get_default_name,
1118     acc_set_default,
1119     acc_lastchange,
1120     NULL,
1121     NULL,
1122 };
1123 
1124 #endif
1125