Coverage Report

Created: 2025-07-23 07:04

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