etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / kcm / cache.c
blobf06e83c67a8052e26c6f3bcf6958726580163b38
1 /* $NetBSD: cache.c,v 1.2 2014/07/24 22:54:10 joerg Exp $ */
3 /*
4 * Copyright (c) 2005, PADL Software Pty Ltd.
5 * All rights reserved.
7 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of PADL Software nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #include "kcm_locl.h"
39 HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER;
40 kcm_ccache_data *ccache_head = NULL;
41 static unsigned int ccache_nextid = 0;
43 char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid)
45 unsigned n;
46 char *name;
48 HEIMDAL_MUTEX_lock(&ccache_mutex);
49 n = ++ccache_nextid;
50 HEIMDAL_MUTEX_unlock(&ccache_mutex);
52 asprintf(&name, "%ld:%u", (long)uid, n);
54 return name;
57 krb5_error_code
58 kcm_ccache_resolve(krb5_context context,
59 const char *name,
60 kcm_ccache *ccache)
62 kcm_ccache p;
63 krb5_error_code ret;
65 *ccache = NULL;
67 ret = KRB5_FCC_NOFILE;
69 HEIMDAL_MUTEX_lock(&ccache_mutex);
71 for (p = ccache_head; p != NULL; p = p->next) {
72 if ((p->flags & KCM_FLAGS_VALID) == 0)
73 continue;
74 if (strcmp(p->name, name) == 0) {
75 ret = 0;
76 break;
80 if (ret == 0) {
81 kcm_retain_ccache(context, p);
82 *ccache = p;
85 HEIMDAL_MUTEX_unlock(&ccache_mutex);
87 return ret;
90 krb5_error_code
91 kcm_ccache_resolve_by_uuid(krb5_context context,
92 kcmuuid_t uuid,
93 kcm_ccache *ccache)
95 kcm_ccache p;
96 krb5_error_code ret;
98 *ccache = NULL;
100 ret = KRB5_FCC_NOFILE;
102 HEIMDAL_MUTEX_lock(&ccache_mutex);
104 for (p = ccache_head; p != NULL; p = p->next) {
105 if ((p->flags & KCM_FLAGS_VALID) == 0)
106 continue;
107 if (memcmp(p->uuid, uuid, sizeof(*uuid)) == 0) {
108 ret = 0;
109 break;
113 if (ret == 0) {
114 kcm_retain_ccache(context, p);
115 *ccache = p;
118 HEIMDAL_MUTEX_unlock(&ccache_mutex);
120 return ret;
123 krb5_error_code
124 kcm_ccache_get_uuids(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *sp)
126 krb5_error_code ret;
127 kcm_ccache p;
129 ret = KRB5_FCC_NOFILE;
131 HEIMDAL_MUTEX_lock(&ccache_mutex);
133 for (p = ccache_head; p != NULL; p = p->next) {
134 if ((p->flags & KCM_FLAGS_VALID) == 0)
135 continue;
136 ret = kcm_access(context, client, opcode, p);
137 if (ret) {
138 ret = 0;
139 continue;
141 krb5_storage_write(sp, p->uuid, sizeof(p->uuid));
144 HEIMDAL_MUTEX_unlock(&ccache_mutex);
146 return ret;
150 krb5_error_code kcm_debug_ccache(krb5_context context)
152 kcm_ccache p;
154 for (p = ccache_head; p != NULL; p = p->next) {
155 char *cpn = NULL, *spn = NULL;
156 int ncreds = 0;
157 struct kcm_creds *k;
159 if ((p->flags & KCM_FLAGS_VALID) == 0) {
160 kcm_log(7, "cache %08x: empty slot");
161 continue;
164 KCM_ASSERT_VALID(p);
166 for (k = p->creds; k != NULL; k = k->next)
167 ncreds++;
169 if (p->client != NULL)
170 krb5_unparse_name(context, p->client, &cpn);
171 if (p->server != NULL)
172 krb5_unparse_name(context, p->server, &spn);
174 kcm_log(7, "cache %08x: name %s refcnt %d flags %04x mode %04o "
175 "uid %d gid %d client %s server %s ncreds %d",
176 p, p->name, p->refcnt, p->flags, p->mode, p->uid, p->gid,
177 (cpn == NULL) ? "<none>" : cpn,
178 (spn == NULL) ? "<none>" : spn,
179 ncreds);
181 if (cpn != NULL)
182 free(cpn);
183 if (spn != NULL)
184 free(spn);
187 return 0;
190 static void
191 kcm_free_ccache_data_internal(krb5_context context,
192 kcm_ccache_data *cache)
194 KCM_ASSERT_VALID(cache);
196 if (cache->name != NULL) {
197 free(cache->name);
198 cache->name = NULL;
201 if (cache->flags & KCM_FLAGS_USE_KEYTAB) {
202 krb5_kt_close(context, cache->key.keytab);
203 cache->key.keytab = NULL;
204 } else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) {
205 krb5_free_keyblock_contents(context, &cache->key.keyblock);
206 krb5_keyblock_zero(&cache->key.keyblock);
209 cache->flags = 0;
210 cache->mode = 0;
211 cache->uid = -1;
212 cache->gid = -1;
213 cache->session = -1;
215 kcm_zero_ccache_data_internal(context, cache);
217 cache->tkt_life = 0;
218 cache->renew_life = 0;
220 cache->next = NULL;
221 cache->refcnt = 0;
223 HEIMDAL_MUTEX_unlock(&cache->mutex);
224 HEIMDAL_MUTEX_destroy(&cache->mutex);
228 krb5_error_code
229 kcm_ccache_destroy(krb5_context context, const char *name)
231 kcm_ccache *p, ccache;
232 krb5_error_code ret;
234 ret = KRB5_FCC_NOFILE;
236 HEIMDAL_MUTEX_lock(&ccache_mutex);
237 for (p = &ccache_head; *p != NULL; p = &(*p)->next) {
238 if (((*p)->flags & KCM_FLAGS_VALID) == 0)
239 continue;
240 if (strcmp((*p)->name, name) == 0) {
241 ret = 0;
242 break;
245 if (ret)
246 goto out;
248 if ((*p)->refcnt != 1) {
249 ret = EAGAIN;
250 goto out;
253 ccache = *p;
254 *p = (*p)->next;
255 kcm_free_ccache_data_internal(context, ccache);
256 free(ccache);
258 out:
259 HEIMDAL_MUTEX_unlock(&ccache_mutex);
261 return ret;
264 static krb5_error_code
265 kcm_ccache_alloc(krb5_context context,
266 const char *name,
267 kcm_ccache *ccache)
269 kcm_ccache slot = NULL, p;
270 krb5_error_code ret;
271 int new_slot = 0;
273 *ccache = NULL;
275 /* First, check for duplicates */
276 HEIMDAL_MUTEX_lock(&ccache_mutex);
277 ret = 0;
278 for (p = ccache_head; p != NULL; p = p->next) {
279 if (p->flags & KCM_FLAGS_VALID) {
280 if (strcmp(p->name, name) == 0) {
281 ret = KRB5_CC_WRITE;
282 break;
284 } else if (slot == NULL)
285 slot = p;
288 if (ret)
289 goto out;
292 * Create an enpty slot for us.
294 if (slot == NULL) {
295 slot = (kcm_ccache_data *)malloc(sizeof(*slot));
296 if (slot == NULL) {
297 ret = KRB5_CC_NOMEM;
298 goto out;
300 slot->next = ccache_head;
301 HEIMDAL_MUTEX_init(&slot->mutex);
302 new_slot = 1;
305 RAND_bytes(slot->uuid, sizeof(slot->uuid));
307 slot->name = strdup(name);
308 if (slot->name == NULL) {
309 ret = KRB5_CC_NOMEM;
310 goto out;
313 slot->refcnt = 1;
314 slot->flags = KCM_FLAGS_VALID;
315 slot->mode = S_IRUSR | S_IWUSR;
316 slot->uid = -1;
317 slot->gid = -1;
318 slot->client = NULL;
319 slot->server = NULL;
320 slot->creds = NULL;
321 slot->key.keytab = NULL;
322 slot->tkt_life = 0;
323 slot->renew_life = 0;
325 if (new_slot)
326 ccache_head = slot;
328 *ccache = slot;
330 HEIMDAL_MUTEX_unlock(&ccache_mutex);
331 return 0;
333 out:
334 HEIMDAL_MUTEX_unlock(&ccache_mutex);
335 if (new_slot && slot != NULL) {
336 HEIMDAL_MUTEX_destroy(&slot->mutex);
337 free(slot);
339 return ret;
342 krb5_error_code
343 kcm_ccache_remove_creds_internal(krb5_context context,
344 kcm_ccache ccache)
346 struct kcm_creds *k;
348 k = ccache->creds;
349 while (k != NULL) {
350 struct kcm_creds *old;
352 krb5_free_cred_contents(context, &k->cred);
353 old = k;
354 k = k->next;
355 free(old);
357 ccache->creds = NULL;
359 return 0;
362 krb5_error_code
363 kcm_ccache_remove_creds(krb5_context context,
364 kcm_ccache ccache)
366 krb5_error_code ret;
368 KCM_ASSERT_VALID(ccache);
370 HEIMDAL_MUTEX_lock(&ccache->mutex);
371 ret = kcm_ccache_remove_creds_internal(context, ccache);
372 HEIMDAL_MUTEX_unlock(&ccache->mutex);
374 return ret;
377 krb5_error_code
378 kcm_zero_ccache_data_internal(krb5_context context,
379 kcm_ccache_data *cache)
381 if (cache->client != NULL) {
382 krb5_free_principal(context, cache->client);
383 cache->client = NULL;
386 if (cache->server != NULL) {
387 krb5_free_principal(context, cache->server);
388 cache->server = NULL;
391 kcm_ccache_remove_creds_internal(context, cache);
393 return 0;
396 krb5_error_code
397 kcm_zero_ccache_data(krb5_context context,
398 kcm_ccache cache)
400 krb5_error_code ret;
402 KCM_ASSERT_VALID(cache);
404 HEIMDAL_MUTEX_lock(&cache->mutex);
405 ret = kcm_zero_ccache_data_internal(context, cache);
406 HEIMDAL_MUTEX_unlock(&cache->mutex);
408 return ret;
411 krb5_error_code
412 kcm_retain_ccache(krb5_context context,
413 kcm_ccache ccache)
415 KCM_ASSERT_VALID(ccache);
417 HEIMDAL_MUTEX_lock(&ccache->mutex);
418 ccache->refcnt++;
419 HEIMDAL_MUTEX_unlock(&ccache->mutex);
421 return 0;
424 krb5_error_code
425 kcm_release_ccache(krb5_context context, kcm_ccache c)
427 krb5_error_code ret = 0;
429 KCM_ASSERT_VALID(c);
431 HEIMDAL_MUTEX_lock(&c->mutex);
432 if (c->refcnt == 1) {
433 kcm_free_ccache_data_internal(context, c);
434 free(c);
435 } else {
436 c->refcnt--;
437 HEIMDAL_MUTEX_unlock(&c->mutex);
440 return ret;
443 krb5_error_code
444 kcm_ccache_gen_new(krb5_context context,
445 pid_t pid,
446 uid_t uid,
447 gid_t gid,
448 kcm_ccache *ccache)
450 krb5_error_code ret;
451 char *name;
453 name = kcm_ccache_nextid(pid, uid, gid);
454 if (name == NULL) {
455 return KRB5_CC_NOMEM;
458 ret = kcm_ccache_new(context, name, ccache);
460 free(name);
461 return ret;
464 krb5_error_code
465 kcm_ccache_new(krb5_context context,
466 const char *name,
467 kcm_ccache *ccache)
469 krb5_error_code ret;
471 ret = kcm_ccache_alloc(context, name, ccache);
472 if (ret == 0) {
474 * one reference is held by the linked list,
475 * one by the caller
477 kcm_retain_ccache(context, *ccache);
480 return ret;
483 krb5_error_code
484 kcm_ccache_destroy_if_empty(krb5_context context,
485 kcm_ccache ccache)
487 krb5_error_code ret;
489 KCM_ASSERT_VALID(ccache);
491 if (ccache->creds == NULL) {
492 ret = kcm_ccache_destroy(context, ccache->name);
493 } else
494 ret = 0;
496 return ret;
499 krb5_error_code
500 kcm_ccache_store_cred(krb5_context context,
501 kcm_ccache ccache,
502 krb5_creds *creds,
503 int copy)
505 krb5_error_code ret;
506 krb5_creds *tmp;
508 KCM_ASSERT_VALID(ccache);
510 HEIMDAL_MUTEX_lock(&ccache->mutex);
511 ret = kcm_ccache_store_cred_internal(context, ccache, creds, copy, &tmp);
512 HEIMDAL_MUTEX_unlock(&ccache->mutex);
514 return ret;
517 struct kcm_creds *
518 kcm_ccache_find_cred_uuid(krb5_context context,
519 kcm_ccache ccache,
520 kcmuuid_t uuid)
522 struct kcm_creds *c;
524 for (c = ccache->creds; c != NULL; c = c->next)
525 if (memcmp(c->uuid, uuid, sizeof(c->uuid)) == 0)
526 return c;
528 return NULL;
533 krb5_error_code
534 kcm_ccache_store_cred_internal(krb5_context context,
535 kcm_ccache ccache,
536 krb5_creds *creds,
537 int copy,
538 krb5_creds **credp)
540 struct kcm_creds **c;
541 krb5_error_code ret;
543 for (c = &ccache->creds; *c != NULL; c = &(*c)->next)
546 *c = (struct kcm_creds *)calloc(1, sizeof(**c));
547 if (*c == NULL)
548 return KRB5_CC_NOMEM;
550 RAND_bytes((*c)->uuid, sizeof((*c)->uuid));
552 *credp = &(*c)->cred;
554 if (copy) {
555 ret = krb5_copy_creds_contents(context, creds, *credp);
556 if (ret) {
557 free(*c);
558 *c = NULL;
560 } else {
561 **credp = *creds;
562 ret = 0;
565 return ret;
568 krb5_error_code
569 kcm_ccache_remove_cred_internal(krb5_context context,
570 kcm_ccache ccache,
571 krb5_flags whichfields,
572 const krb5_creds *mcreds)
574 krb5_error_code ret;
575 struct kcm_creds **c;
577 ret = KRB5_CC_NOTFOUND;
579 for (c = &ccache->creds; *c != NULL; c = &(*c)->next) {
580 if (krb5_compare_creds(context, whichfields, mcreds, &(*c)->cred)) {
581 struct kcm_creds *cred = *c;
583 *c = cred->next;
584 krb5_free_cred_contents(context, &cred->cred);
585 free(cred);
586 ret = 0;
587 if (*c == NULL)
588 break;
592 return ret;
595 krb5_error_code
596 kcm_ccache_remove_cred(krb5_context context,
597 kcm_ccache ccache,
598 krb5_flags whichfields,
599 const krb5_creds *mcreds)
601 krb5_error_code ret;
603 KCM_ASSERT_VALID(ccache);
605 HEIMDAL_MUTEX_lock(&ccache->mutex);
606 ret = kcm_ccache_remove_cred_internal(context, ccache, whichfields, mcreds);
607 HEIMDAL_MUTEX_unlock(&ccache->mutex);
609 return ret;
612 krb5_error_code
613 kcm_ccache_retrieve_cred_internal(krb5_context context,
614 kcm_ccache ccache,
615 krb5_flags whichfields,
616 const krb5_creds *mcreds,
617 krb5_creds **creds)
619 krb5_boolean match;
620 struct kcm_creds *c;
621 krb5_error_code ret;
623 memset(creds, 0, sizeof(*creds));
625 ret = KRB5_CC_END;
627 match = FALSE;
628 for (c = ccache->creds; c != NULL; c = c->next) {
629 match = krb5_compare_creds(context, whichfields, mcreds, &c->cred);
630 if (match)
631 break;
634 if (match) {
635 ret = 0;
636 *creds = &c->cred;
639 return ret;
642 krb5_error_code
643 kcm_ccache_retrieve_cred(krb5_context context,
644 kcm_ccache ccache,
645 krb5_flags whichfields,
646 const krb5_creds *mcreds,
647 krb5_creds **credp)
649 krb5_error_code ret;
651 KCM_ASSERT_VALID(ccache);
653 HEIMDAL_MUTEX_lock(&ccache->mutex);
654 ret = kcm_ccache_retrieve_cred_internal(context, ccache,
655 whichfields, mcreds, credp);
656 HEIMDAL_MUTEX_unlock(&ccache->mutex);
658 return ret;
661 char *
662 kcm_ccache_first_name(kcm_client *client)
664 kcm_ccache p;
665 char *name = NULL;
667 HEIMDAL_MUTEX_lock(&ccache_mutex);
669 for (p = ccache_head; p != NULL; p = p->next) {
670 if (kcm_is_same_session(client, p->uid, p->session))
671 break;
673 if (p)
674 name = strdup(p->name);
675 HEIMDAL_MUTEX_unlock(&ccache_mutex);
676 return name;