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