Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/kcm/cache.c
34869 views
1
/*
2
* Copyright (c) 2005, PADL Software Pty Ltd.
3
* All rights reserved.
4
*
5
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
*
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
*
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* 3. Neither the name of PADL Software nor the names of its contributors
19
* may be used to endorse or promote products derived from this software
20
* without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
* ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
* SUCH DAMAGE.
33
*/
34
35
#include "kcm_locl.h"
36
37
HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER;
38
kcm_ccache_data *ccache_head = NULL;
39
static unsigned int ccache_nextid = 0;
40
41
char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid)
42
{
43
unsigned n;
44
char *name;
45
46
HEIMDAL_MUTEX_lock(&ccache_mutex);
47
n = ++ccache_nextid;
48
HEIMDAL_MUTEX_unlock(&ccache_mutex);
49
50
asprintf(&name, "%ld:%u", (long)uid, n);
51
52
return name;
53
}
54
55
krb5_error_code
56
kcm_ccache_resolve(krb5_context context,
57
const char *name,
58
kcm_ccache *ccache)
59
{
60
kcm_ccache p;
61
krb5_error_code ret;
62
63
*ccache = NULL;
64
65
ret = KRB5_FCC_NOFILE;
66
67
HEIMDAL_MUTEX_lock(&ccache_mutex);
68
69
for (p = ccache_head; p != NULL; p = p->next) {
70
if ((p->flags & KCM_FLAGS_VALID) == 0)
71
continue;
72
if (strcmp(p->name, name) == 0) {
73
ret = 0;
74
break;
75
}
76
}
77
78
if (ret == 0) {
79
kcm_retain_ccache(context, p);
80
*ccache = p;
81
}
82
83
HEIMDAL_MUTEX_unlock(&ccache_mutex);
84
85
return ret;
86
}
87
88
krb5_error_code
89
kcm_ccache_resolve_by_uuid(krb5_context context,
90
kcmuuid_t uuid,
91
kcm_ccache *ccache)
92
{
93
kcm_ccache p;
94
krb5_error_code ret;
95
96
*ccache = NULL;
97
98
ret = KRB5_FCC_NOFILE;
99
100
HEIMDAL_MUTEX_lock(&ccache_mutex);
101
102
for (p = ccache_head; p != NULL; p = p->next) {
103
if ((p->flags & KCM_FLAGS_VALID) == 0)
104
continue;
105
if (memcmp(p->uuid, uuid, sizeof(kcmuuid_t)) == 0) {
106
ret = 0;
107
break;
108
}
109
}
110
111
if (ret == 0) {
112
kcm_retain_ccache(context, p);
113
*ccache = p;
114
}
115
116
HEIMDAL_MUTEX_unlock(&ccache_mutex);
117
118
return ret;
119
}
120
121
krb5_error_code
122
kcm_ccache_get_uuids(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *sp)
123
{
124
krb5_error_code ret;
125
kcm_ccache p;
126
127
ret = KRB5_FCC_NOFILE;
128
129
HEIMDAL_MUTEX_lock(&ccache_mutex);
130
131
for (p = ccache_head; p != NULL; p = p->next) {
132
if ((p->flags & KCM_FLAGS_VALID) == 0)
133
continue;
134
ret = kcm_access(context, client, opcode, p);
135
if (ret) {
136
ret = 0;
137
continue;
138
}
139
krb5_storage_write(sp, p->uuid, sizeof(p->uuid));
140
}
141
142
HEIMDAL_MUTEX_unlock(&ccache_mutex);
143
144
return ret;
145
}
146
147
148
krb5_error_code kcm_debug_ccache(krb5_context context)
149
{
150
kcm_ccache p;
151
152
for (p = ccache_head; p != NULL; p = p->next) {
153
char *cpn = NULL, *spn = NULL;
154
int ncreds = 0;
155
struct kcm_creds *k;
156
157
if ((p->flags & KCM_FLAGS_VALID) == 0) {
158
kcm_log(7, "cache %08x: empty slot");
159
continue;
160
}
161
162
KCM_ASSERT_VALID(p);
163
164
for (k = p->creds; k != NULL; k = k->next)
165
ncreds++;
166
167
if (p->client != NULL)
168
krb5_unparse_name(context, p->client, &cpn);
169
if (p->server != NULL)
170
krb5_unparse_name(context, p->server, &spn);
171
172
kcm_log(7, "cache %08x: name %s refcnt %d flags %04x mode %04o "
173
"uid %d gid %d client %s server %s ncreds %d",
174
p, p->name, p->refcnt, p->flags, p->mode, p->uid, p->gid,
175
(cpn == NULL) ? "<none>" : cpn,
176
(spn == NULL) ? "<none>" : spn,
177
ncreds);
178
179
if (cpn != NULL)
180
free(cpn);
181
if (spn != NULL)
182
free(spn);
183
}
184
185
return 0;
186
}
187
188
static void
189
kcm_free_ccache_data_internal(krb5_context context,
190
kcm_ccache_data *cache)
191
{
192
KCM_ASSERT_VALID(cache);
193
194
if (cache->name != NULL) {
195
free(cache->name);
196
cache->name = NULL;
197
}
198
199
if (cache->flags & KCM_FLAGS_USE_KEYTAB) {
200
krb5_kt_close(context, cache->key.keytab);
201
cache->key.keytab = NULL;
202
} else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) {
203
krb5_free_keyblock_contents(context, &cache->key.keyblock);
204
krb5_keyblock_zero(&cache->key.keyblock);
205
}
206
207
cache->flags = 0;
208
cache->mode = 0;
209
cache->uid = -1;
210
cache->gid = -1;
211
cache->session = -1;
212
213
kcm_zero_ccache_data_internal(context, cache);
214
215
cache->tkt_life = 0;
216
cache->renew_life = 0;
217
218
cache->next = NULL;
219
cache->refcnt = 0;
220
221
HEIMDAL_MUTEX_unlock(&cache->mutex);
222
HEIMDAL_MUTEX_destroy(&cache->mutex);
223
}
224
225
226
krb5_error_code
227
kcm_ccache_destroy(krb5_context context, const char *name)
228
{
229
kcm_ccache *p, ccache;
230
krb5_error_code ret;
231
232
ret = KRB5_FCC_NOFILE;
233
234
HEIMDAL_MUTEX_lock(&ccache_mutex);
235
for (p = &ccache_head; *p != NULL; p = &(*p)->next) {
236
if (((*p)->flags & KCM_FLAGS_VALID) == 0)
237
continue;
238
if (strcmp((*p)->name, name) == 0) {
239
ret = 0;
240
break;
241
}
242
}
243
if (ret)
244
goto out;
245
246
if ((*p)->refcnt != 1) {
247
ret = EAGAIN;
248
goto out;
249
}
250
251
ccache = *p;
252
*p = (*p)->next;
253
kcm_free_ccache_data_internal(context, ccache);
254
free(ccache);
255
256
out:
257
HEIMDAL_MUTEX_unlock(&ccache_mutex);
258
259
return ret;
260
}
261
262
static krb5_error_code
263
kcm_ccache_alloc(krb5_context context,
264
const char *name,
265
kcm_ccache *ccache)
266
{
267
kcm_ccache slot = NULL, p;
268
krb5_error_code ret;
269
int new_slot = 0;
270
271
*ccache = NULL;
272
273
/* First, check for duplicates */
274
HEIMDAL_MUTEX_lock(&ccache_mutex);
275
ret = 0;
276
for (p = ccache_head; p != NULL; p = p->next) {
277
if (p->flags & KCM_FLAGS_VALID) {
278
if (strcmp(p->name, name) == 0) {
279
ret = KRB5_CC_WRITE;
280
break;
281
}
282
} else if (slot == NULL)
283
slot = p;
284
}
285
286
if (ret)
287
goto out;
288
289
/*
290
* Create an enpty slot for us.
291
*/
292
if (slot == NULL) {
293
slot = (kcm_ccache_data *)malloc(sizeof(*slot));
294
if (slot == NULL) {
295
ret = KRB5_CC_NOMEM;
296
goto out;
297
}
298
slot->next = ccache_head;
299
HEIMDAL_MUTEX_init(&slot->mutex);
300
new_slot = 1;
301
}
302
303
RAND_bytes(slot->uuid, sizeof(slot->uuid));
304
305
slot->name = strdup(name);
306
if (slot->name == NULL) {
307
ret = KRB5_CC_NOMEM;
308
goto out;
309
}
310
311
slot->refcnt = 1;
312
slot->flags = KCM_FLAGS_VALID;
313
slot->mode = S_IRUSR | S_IWUSR;
314
slot->uid = -1;
315
slot->gid = -1;
316
slot->client = NULL;
317
slot->server = NULL;
318
slot->creds = NULL;
319
slot->key.keytab = NULL;
320
slot->tkt_life = 0;
321
slot->renew_life = 0;
322
323
if (new_slot)
324
ccache_head = slot;
325
326
*ccache = slot;
327
328
HEIMDAL_MUTEX_unlock(&ccache_mutex);
329
return 0;
330
331
out:
332
HEIMDAL_MUTEX_unlock(&ccache_mutex);
333
if (new_slot && slot != NULL) {
334
HEIMDAL_MUTEX_destroy(&slot->mutex);
335
free(slot);
336
}
337
return ret;
338
}
339
340
krb5_error_code
341
kcm_ccache_remove_creds_internal(krb5_context context,
342
kcm_ccache ccache)
343
{
344
struct kcm_creds *k;
345
346
k = ccache->creds;
347
while (k != NULL) {
348
struct kcm_creds *old;
349
350
krb5_free_cred_contents(context, &k->cred);
351
old = k;
352
k = k->next;
353
free(old);
354
}
355
ccache->creds = NULL;
356
357
return 0;
358
}
359
360
krb5_error_code
361
kcm_ccache_remove_creds(krb5_context context,
362
kcm_ccache ccache)
363
{
364
krb5_error_code ret;
365
366
KCM_ASSERT_VALID(ccache);
367
368
HEIMDAL_MUTEX_lock(&ccache->mutex);
369
ret = kcm_ccache_remove_creds_internal(context, ccache);
370
HEIMDAL_MUTEX_unlock(&ccache->mutex);
371
372
return ret;
373
}
374
375
krb5_error_code
376
kcm_zero_ccache_data_internal(krb5_context context,
377
kcm_ccache_data *cache)
378
{
379
if (cache->client != NULL) {
380
krb5_free_principal(context, cache->client);
381
cache->client = NULL;
382
}
383
384
if (cache->server != NULL) {
385
krb5_free_principal(context, cache->server);
386
cache->server = NULL;
387
}
388
389
kcm_ccache_remove_creds_internal(context, cache);
390
391
return 0;
392
}
393
394
krb5_error_code
395
kcm_zero_ccache_data(krb5_context context,
396
kcm_ccache cache)
397
{
398
krb5_error_code ret;
399
400
KCM_ASSERT_VALID(cache);
401
402
HEIMDAL_MUTEX_lock(&cache->mutex);
403
ret = kcm_zero_ccache_data_internal(context, cache);
404
HEIMDAL_MUTEX_unlock(&cache->mutex);
405
406
return ret;
407
}
408
409
krb5_error_code
410
kcm_retain_ccache(krb5_context context,
411
kcm_ccache ccache)
412
{
413
KCM_ASSERT_VALID(ccache);
414
415
HEIMDAL_MUTEX_lock(&ccache->mutex);
416
ccache->refcnt++;
417
HEIMDAL_MUTEX_unlock(&ccache->mutex);
418
419
return 0;
420
}
421
422
krb5_error_code
423
kcm_release_ccache(krb5_context context, kcm_ccache c)
424
{
425
krb5_error_code ret = 0;
426
427
KCM_ASSERT_VALID(c);
428
429
HEIMDAL_MUTEX_lock(&c->mutex);
430
if (c->refcnt == 1) {
431
kcm_free_ccache_data_internal(context, c);
432
free(c);
433
} else {
434
c->refcnt--;
435
HEIMDAL_MUTEX_unlock(&c->mutex);
436
}
437
438
return ret;
439
}
440
441
krb5_error_code
442
kcm_ccache_gen_new(krb5_context context,
443
pid_t pid,
444
uid_t uid,
445
gid_t gid,
446
kcm_ccache *ccache)
447
{
448
krb5_error_code ret;
449
char *name;
450
451
name = kcm_ccache_nextid(pid, uid, gid);
452
if (name == NULL) {
453
return KRB5_CC_NOMEM;
454
}
455
456
ret = kcm_ccache_new(context, name, ccache);
457
458
free(name);
459
return ret;
460
}
461
462
krb5_error_code
463
kcm_ccache_new(krb5_context context,
464
const char *name,
465
kcm_ccache *ccache)
466
{
467
krb5_error_code ret;
468
469
ret = kcm_ccache_alloc(context, name, ccache);
470
if (ret == 0) {
471
/*
472
* one reference is held by the linked list,
473
* one by the caller
474
*/
475
kcm_retain_ccache(context, *ccache);
476
}
477
478
return ret;
479
}
480
481
krb5_error_code
482
kcm_ccache_destroy_if_empty(krb5_context context,
483
kcm_ccache ccache)
484
{
485
krb5_error_code ret;
486
487
KCM_ASSERT_VALID(ccache);
488
489
if (ccache->creds == NULL) {
490
ret = kcm_ccache_destroy(context, ccache->name);
491
} else
492
ret = 0;
493
494
return ret;
495
}
496
497
krb5_error_code
498
kcm_ccache_store_cred(krb5_context context,
499
kcm_ccache ccache,
500
krb5_creds *creds,
501
int copy)
502
{
503
krb5_error_code ret;
504
krb5_creds *tmp;
505
506
KCM_ASSERT_VALID(ccache);
507
508
HEIMDAL_MUTEX_lock(&ccache->mutex);
509
ret = kcm_ccache_store_cred_internal(context, ccache, creds, copy, &tmp);
510
HEIMDAL_MUTEX_unlock(&ccache->mutex);
511
512
return ret;
513
}
514
515
struct kcm_creds *
516
kcm_ccache_find_cred_uuid(krb5_context context,
517
kcm_ccache ccache,
518
kcmuuid_t uuid)
519
{
520
struct kcm_creds *c;
521
522
for (c = ccache->creds; c != NULL; c = c->next)
523
if (memcmp(c->uuid, uuid, sizeof(c->uuid)) == 0)
524
return c;
525
526
return NULL;
527
}
528
529
530
531
krb5_error_code
532
kcm_ccache_store_cred_internal(krb5_context context,
533
kcm_ccache ccache,
534
krb5_creds *creds,
535
int copy,
536
krb5_creds **credp)
537
{
538
struct kcm_creds **c;
539
krb5_error_code ret;
540
541
for (c = &ccache->creds; *c != NULL; c = &(*c)->next)
542
;
543
544
*c = (struct kcm_creds *)calloc(1, sizeof(**c));
545
if (*c == NULL)
546
return KRB5_CC_NOMEM;
547
548
RAND_bytes((*c)->uuid, sizeof((*c)->uuid));
549
550
*credp = &(*c)->cred;
551
552
if (copy) {
553
ret = krb5_copy_creds_contents(context, creds, *credp);
554
if (ret) {
555
free(*c);
556
*c = NULL;
557
}
558
} else {
559
**credp = *creds;
560
ret = 0;
561
}
562
563
return ret;
564
}
565
566
krb5_error_code
567
kcm_ccache_remove_cred_internal(krb5_context context,
568
kcm_ccache ccache,
569
krb5_flags whichfields,
570
const krb5_creds *mcreds)
571
{
572
krb5_error_code ret;
573
struct kcm_creds **c;
574
575
ret = KRB5_CC_NOTFOUND;
576
577
for (c = &ccache->creds; *c != NULL; c = &(*c)->next) {
578
if (krb5_compare_creds(context, whichfields, mcreds, &(*c)->cred)) {
579
struct kcm_creds *cred = *c;
580
581
*c = cred->next;
582
krb5_free_cred_contents(context, &cred->cred);
583
free(cred);
584
ret = 0;
585
if (*c == NULL)
586
break;
587
}
588
}
589
590
return ret;
591
}
592
593
krb5_error_code
594
kcm_ccache_remove_cred(krb5_context context,
595
kcm_ccache ccache,
596
krb5_flags whichfields,
597
const krb5_creds *mcreds)
598
{
599
krb5_error_code ret;
600
601
KCM_ASSERT_VALID(ccache);
602
603
HEIMDAL_MUTEX_lock(&ccache->mutex);
604
ret = kcm_ccache_remove_cred_internal(context, ccache, whichfields, mcreds);
605
HEIMDAL_MUTEX_unlock(&ccache->mutex);
606
607
return ret;
608
}
609
610
krb5_error_code
611
kcm_ccache_retrieve_cred_internal(krb5_context context,
612
kcm_ccache ccache,
613
krb5_flags whichfields,
614
const krb5_creds *mcreds,
615
krb5_creds **creds)
616
{
617
krb5_boolean match;
618
struct kcm_creds *c;
619
krb5_error_code ret;
620
621
memset(creds, 0, sizeof(*creds));
622
623
ret = KRB5_CC_END;
624
625
match = FALSE;
626
for (c = ccache->creds; c != NULL; c = c->next) {
627
match = krb5_compare_creds(context, whichfields, mcreds, &c->cred);
628
if (match)
629
break;
630
}
631
632
if (match) {
633
ret = 0;
634
*creds = &c->cred;
635
}
636
637
return ret;
638
}
639
640
krb5_error_code
641
kcm_ccache_retrieve_cred(krb5_context context,
642
kcm_ccache ccache,
643
krb5_flags whichfields,
644
const krb5_creds *mcreds,
645
krb5_creds **credp)
646
{
647
krb5_error_code ret;
648
649
KCM_ASSERT_VALID(ccache);
650
651
HEIMDAL_MUTEX_lock(&ccache->mutex);
652
ret = kcm_ccache_retrieve_cred_internal(context, ccache,
653
whichfields, mcreds, credp);
654
HEIMDAL_MUTEX_unlock(&ccache->mutex);
655
656
return ret;
657
}
658
659
char *
660
kcm_ccache_first_name(kcm_client *client)
661
{
662
kcm_ccache p;
663
char *name = NULL;
664
665
HEIMDAL_MUTEX_lock(&ccache_mutex);
666
667
for (p = ccache_head; p != NULL; p = p->next) {
668
if (kcm_is_same_session(client, p->uid, p->session))
669
break;
670
}
671
if (p)
672
name = strdup(p->name);
673
HEIMDAL_MUTEX_unlock(&ccache_mutex);
674
return name;
675
}
676
677