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