test debug
[heimdal.git] / kcm / cache.c
blob880f7e0487dea25f06ee1c2f02da1492f9fa4861
1 /*
2 * Copyright (c) 2005, PADL Software Pty Ltd.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of PADL Software nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include "kcm_locl.h"
35 RCSID("$Id$");
37 static HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER;
38 static kcm_ccache_data *ccache_head = NULL;
39 static unsigned int ccache_nextid = 0;
41 char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid)
43 unsigned n;
44 char *name;
46 HEIMDAL_MUTEX_lock(&ccache_mutex);
47 n = ++ccache_nextid;
48 HEIMDAL_MUTEX_unlock(&ccache_mutex);
50 asprintf(&name, "%d:%u", uid, n);
52 return name;
55 static krb5_error_code
56 kcm_ccache_resolve_internal(krb5_context context,
57 const char *name,
58 kcm_ccache *ccache)
60 kcm_ccache p;
61 krb5_error_code ret;
63 *ccache = NULL;
65 ret = KRB5_FCC_NOFILE;
67 HEIMDAL_MUTEX_lock(&ccache_mutex);
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;
78 if (ret == 0) {
79 kcm_retain_ccache(context, p);
80 *ccache = p;
83 HEIMDAL_MUTEX_unlock(&ccache_mutex);
85 return ret;
88 krb5_error_code kcm_debug_ccache(krb5_context context)
90 kcm_ccache p;
92 for (p = ccache_head; p != NULL; p = p->next) {
93 char *cpn = NULL, *spn = NULL;
94 int ncreds = 0;
95 struct kcm_creds *k;
97 if ((p->flags & KCM_FLAGS_VALID) == 0) {
98 kcm_log(7, "cache %08x: empty slot");
99 continue;
102 KCM_ASSERT_VALID(p);
104 for (k = p->creds; k != NULL; k = k->next)
105 ncreds++;
107 if (p->client != NULL)
108 krb5_unparse_name(context, p->client, &cpn);
109 if (p->server != NULL)
110 krb5_unparse_name(context, p->server, &spn);
112 kcm_log(7, "cache %08x: name %s refcnt %d flags %04x mode %04o "
113 "uid %d gid %d client %s server %s ncreds %d",
114 p, p->name, p->refcnt, p->flags, p->mode, p->uid, p->gid,
115 (cpn == NULL) ? "<none>" : cpn,
116 (spn == NULL) ? "<none>" : spn,
117 ncreds);
119 if (cpn != NULL)
120 free(cpn);
121 if (spn != NULL)
122 free(spn);
125 return 0;
128 static krb5_error_code
129 kcm_ccache_destroy_internal(krb5_context context, const char *name)
131 kcm_ccache *p;
132 krb5_error_code ret;
134 ret = KRB5_FCC_NOFILE;
136 HEIMDAL_MUTEX_lock(&ccache_mutex);
137 for (p = &ccache_head; *p != NULL; p = &(*p)->next) {
138 if (((*p)->flags & KCM_FLAGS_VALID) == 0)
139 continue;
140 if (strcmp((*p)->name, name) == 0) {
141 ret = 0;
142 break;
146 if (ret)
147 goto out;
149 kcm_release_ccache(context, p);
151 out:
152 HEIMDAL_MUTEX_unlock(&ccache_mutex);
154 return ret;
157 static krb5_error_code
158 kcm_ccache_alloc(krb5_context context,
159 const char *name,
160 kcm_ccache *ccache)
162 kcm_ccache slot = NULL, p;
163 krb5_error_code ret;
164 int new_slot = 0;
166 *ccache = NULL;
168 /* First, check for duplicates */
169 HEIMDAL_MUTEX_lock(&ccache_mutex);
170 ret = 0;
171 for (p = ccache_head; p != NULL; p = p->next) {
172 if (p->flags & KCM_FLAGS_VALID) {
173 if (strcmp(p->name, name) == 0) {
174 ret = KRB5_CC_WRITE;
175 break;
177 } else if (slot == NULL)
178 slot = p;
181 if (ret)
182 goto out;
185 * Then try and find an empty slot
186 * XXX we need to recycle slots for this to actually do anything
188 if (slot == NULL) {
189 for (; p != NULL; p = p->next) {
190 if ((p->flags & KCM_FLAGS_VALID) == 0) {
191 slot = p;
192 break;
196 if (slot == NULL) {
197 slot = (kcm_ccache_data *)malloc(sizeof(*slot));
198 if (slot == NULL) {
199 ret = KRB5_CC_NOMEM;
200 goto out;
202 slot->next = ccache_head;
203 HEIMDAL_MUTEX_init(&slot->mutex);
204 new_slot = 1;
208 slot->name = strdup(name);
209 if (slot->name == NULL) {
210 ret = KRB5_CC_NOMEM;
211 goto out;
214 slot->refcnt = 1;
215 slot->flags = KCM_FLAGS_VALID;
216 slot->mode = S_IRUSR | S_IWUSR;
217 slot->uid = -1;
218 slot->gid = -1;
219 slot->client = NULL;
220 slot->server = NULL;
221 slot->creds = NULL;
222 slot->n_cursor = 0;
223 slot->cursors = NULL;
224 slot->key.keytab = NULL;
225 slot->tkt_life = 0;
226 slot->renew_life = 0;
228 if (new_slot)
229 ccache_head = slot;
231 *ccache = slot;
233 HEIMDAL_MUTEX_unlock(&ccache_mutex);
234 return 0;
236 out:
237 HEIMDAL_MUTEX_unlock(&ccache_mutex);
238 if (new_slot && slot != NULL) {
239 HEIMDAL_MUTEX_destroy(&slot->mutex);
240 free(slot);
242 return ret;
245 krb5_error_code
246 kcm_ccache_remove_creds_internal(krb5_context context,
247 kcm_ccache ccache)
249 struct kcm_creds *k;
250 struct kcm_cursor *c;
252 k = ccache->creds;
253 while (k != NULL) {
254 struct kcm_creds *old;
256 krb5_free_cred_contents(context, &k->cred);
257 old = k;
258 k = k->next;
259 free(old);
261 ccache->creds = NULL;
263 /* remove anything that would have pointed into the creds too */
265 ccache->n_cursor = 0;
267 c = ccache->cursors;
268 while (c != NULL) {
269 struct kcm_cursor *old;
271 old = c;
272 c = c->next;
273 free(old);
275 ccache->cursors = NULL;
277 return 0;
280 krb5_error_code
281 kcm_ccache_remove_creds(krb5_context context,
282 kcm_ccache ccache)
284 krb5_error_code ret;
286 KCM_ASSERT_VALID(ccache);
288 HEIMDAL_MUTEX_lock(&ccache->mutex);
289 ret = kcm_ccache_remove_creds_internal(context, ccache);
290 HEIMDAL_MUTEX_unlock(&ccache->mutex);
292 return ret;
295 krb5_error_code
296 kcm_zero_ccache_data_internal(krb5_context context,
297 kcm_ccache_data *cache)
299 if (cache->client != NULL) {
300 krb5_free_principal(context, cache->client);
301 cache->client = NULL;
304 if (cache->server != NULL) {
305 krb5_free_principal(context, cache->server);
306 cache->server = NULL;
309 kcm_ccache_remove_creds_internal(context, cache);
311 return 0;
314 krb5_error_code
315 kcm_zero_ccache_data(krb5_context context,
316 kcm_ccache cache)
318 krb5_error_code ret;
320 KCM_ASSERT_VALID(cache);
322 HEIMDAL_MUTEX_lock(&cache->mutex);
323 ret = kcm_zero_ccache_data_internal(context, cache);
324 HEIMDAL_MUTEX_unlock(&cache->mutex);
326 return ret;
329 static krb5_error_code
330 kcm_free_ccache_data_internal(krb5_context context,
331 kcm_ccache_data *cache)
333 KCM_ASSERT_VALID(cache);
335 if (cache->name != NULL) {
336 free(cache->name);
337 cache->name = NULL;
340 if (cache->flags & KCM_FLAGS_USE_KEYTAB) {
341 krb5_kt_close(context, cache->key.keytab);
342 cache->key.keytab = NULL;
343 } else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) {
344 krb5_free_keyblock_contents(context, &cache->key.keyblock);
345 krb5_keyblock_zero(&cache->key.keyblock);
348 cache->flags = 0;
349 cache->mode = 0;
350 cache->uid = -1;
351 cache->gid = -1;
353 kcm_zero_ccache_data_internal(context, cache);
355 cache->tkt_life = 0;
356 cache->renew_life = 0;
358 cache->next = NULL;
359 cache->refcnt = 0;
361 HEIMDAL_MUTEX_unlock(&cache->mutex);
362 HEIMDAL_MUTEX_destroy(&cache->mutex);
364 return 0;
367 krb5_error_code
368 kcm_retain_ccache(krb5_context context,
369 kcm_ccache ccache)
371 KCM_ASSERT_VALID(ccache);
373 HEIMDAL_MUTEX_lock(&ccache->mutex);
374 ccache->refcnt++;
375 HEIMDAL_MUTEX_unlock(&ccache->mutex);
377 return 0;
380 krb5_error_code
381 kcm_release_ccache(krb5_context context,
382 kcm_ccache *ccache)
384 kcm_ccache c = *ccache;
385 krb5_error_code ret = 0;
387 KCM_ASSERT_VALID(c);
389 HEIMDAL_MUTEX_lock(&c->mutex);
390 if (c->refcnt == 1) {
391 ret = kcm_free_ccache_data_internal(context, c);
392 if (ret == 0)
393 free(c);
394 } else {
395 c->refcnt--;
396 HEIMDAL_MUTEX_unlock(&c->mutex);
399 *ccache = NULL;
401 return ret;
404 krb5_error_code
405 kcm_ccache_gen_new(krb5_context context,
406 pid_t pid,
407 uid_t uid,
408 gid_t gid,
409 kcm_ccache *ccache)
411 krb5_error_code ret;
412 char *name;
414 name = kcm_ccache_nextid(pid, uid, gid);
415 if (name == NULL) {
416 return KRB5_CC_NOMEM;
419 ret = kcm_ccache_new(context, name, ccache);
421 free(name);
422 return ret;
425 krb5_error_code
426 kcm_ccache_new(krb5_context context,
427 const char *name,
428 kcm_ccache *ccache)
430 krb5_error_code ret;
432 ret = kcm_ccache_alloc(context, name, ccache);
433 if (ret == 0) {
435 * one reference is held by the linked list,
436 * one by the caller
438 kcm_retain_ccache(context, *ccache);
441 return ret;
444 krb5_error_code
445 kcm_ccache_resolve(krb5_context context,
446 const char *name,
447 kcm_ccache *ccache)
449 krb5_error_code ret;
451 ret = kcm_ccache_resolve_internal(context, name, ccache);
453 return ret;
456 krb5_error_code
457 kcm_ccache_destroy(krb5_context context,
458 const char *name)
460 krb5_error_code ret;
462 ret = kcm_ccache_destroy_internal(context, name);
464 return ret;
467 krb5_error_code
468 kcm_ccache_destroy_if_empty(krb5_context context,
469 kcm_ccache ccache)
471 krb5_error_code ret;
473 KCM_ASSERT_VALID(ccache);
475 if (ccache->creds == NULL) {
476 ret = kcm_ccache_destroy_internal(context, ccache->name);
477 } else
478 ret = 0;
480 return ret;
483 krb5_error_code
484 kcm_ccache_store_cred(krb5_context context,
485 kcm_ccache ccache,
486 krb5_creds *creds,
487 int copy)
489 krb5_error_code ret;
490 krb5_creds *tmp;
492 KCM_ASSERT_VALID(ccache);
494 HEIMDAL_MUTEX_lock(&ccache->mutex);
495 ret = kcm_ccache_store_cred_internal(context, ccache, creds, copy, &tmp);
496 HEIMDAL_MUTEX_unlock(&ccache->mutex);
498 return ret;
501 krb5_error_code
502 kcm_ccache_store_cred_internal(krb5_context context,
503 kcm_ccache ccache,
504 krb5_creds *creds,
505 int copy,
506 krb5_creds **credp)
508 struct kcm_creds **c;
509 krb5_error_code ret;
511 for (c = &ccache->creds; *c != NULL; c = &(*c)->next)
514 *c = (struct kcm_creds *)malloc(sizeof(struct kcm_creds));
515 if (*c == NULL) {
516 return KRB5_CC_NOMEM;
519 *credp = &(*c)->cred;
521 if (copy) {
522 ret = krb5_copy_creds_contents(context, creds, *credp);
523 if (ret) {
524 free(*c);
525 *c = NULL;
527 } else {
528 **credp = *creds;
529 ret = 0;
532 (*c)->next = NULL;
534 return ret;
537 static void
538 remove_cred(krb5_context context,
539 struct kcm_creds **c)
541 struct kcm_creds *cred;
543 cred = *c;
545 *c = cred->next;
547 krb5_free_cred_contents(context, &cred->cred);
548 free(cred);
551 krb5_error_code
552 kcm_ccache_remove_cred_internal(krb5_context context,
553 kcm_ccache ccache,
554 krb5_flags whichfields,
555 const krb5_creds *mcreds)
557 krb5_error_code ret;
558 struct kcm_creds **c;
560 ret = KRB5_CC_NOTFOUND;
562 for (c = &ccache->creds; *c != NULL; c = &(*c)->next) {
563 if (krb5_compare_creds(context, whichfields, mcreds, &(*c)->cred)) {
564 remove_cred(context, c);
565 ret = 0;
569 return ret;
572 krb5_error_code
573 kcm_ccache_remove_cred(krb5_context context,
574 kcm_ccache ccache,
575 krb5_flags whichfields,
576 const krb5_creds *mcreds)
578 krb5_error_code ret;
580 KCM_ASSERT_VALID(ccache);
582 HEIMDAL_MUTEX_lock(&ccache->mutex);
583 ret = kcm_ccache_remove_cred_internal(context, ccache, whichfields, mcreds);
584 HEIMDAL_MUTEX_unlock(&ccache->mutex);
586 return ret;
589 krb5_error_code
590 kcm_ccache_retrieve_cred_internal(krb5_context context,
591 kcm_ccache ccache,
592 krb5_flags whichfields,
593 const krb5_creds *mcreds,
594 krb5_creds **creds)
596 krb5_boolean match;
597 struct kcm_creds *c;
598 krb5_error_code ret;
600 memset(creds, 0, sizeof(*creds));
602 ret = KRB5_CC_END;
604 match = FALSE;
605 for (c = ccache->creds; c != NULL; c = c->next) {
606 match = krb5_compare_creds(context, whichfields, mcreds, &c->cred);
607 if (match)
608 break;
611 if (match) {
612 ret = 0;
613 *creds = &c->cred;
616 return ret;
619 krb5_error_code
620 kcm_ccache_retrieve_cred(krb5_context context,
621 kcm_ccache ccache,
622 krb5_flags whichfields,
623 const krb5_creds *mcreds,
624 krb5_creds **credp)
626 krb5_error_code ret;
628 KCM_ASSERT_VALID(ccache);
630 HEIMDAL_MUTEX_lock(&ccache->mutex);
631 ret = kcm_ccache_retrieve_cred_internal(context, ccache,
632 whichfields, mcreds, credp);
633 HEIMDAL_MUTEX_unlock(&ccache->mutex);
635 return ret;