Line data Source code
1 : /*
2 : * Copyright (c) 1997-2004 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 :
38 : typedef struct krb5_mcache {
39 : char *name;
40 : unsigned int refcnt;
41 : unsigned int anonymous:1;
42 : unsigned int dead:1;
43 : krb5_principal primary_principal;
44 : struct link {
45 : krb5_creds cred;
46 : struct link *next;
47 : } *creds;
48 : struct krb5_mcache *next;
49 : time_t mtime;
50 : krb5_deltat kdc_offset;
51 : HEIMDAL_MUTEX mutex;
52 : } krb5_mcache;
53 :
54 : static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
55 : static struct krb5_mcache *mcc_head;
56 :
57 : #define MCACHE(X) ((krb5_mcache *)(X)->data.data)
58 :
59 : #define MISDEAD(X) ((X)->dead)
60 :
61 : static krb5_error_code KRB5_CALLCONV
62 102114 : mcc_get_name_2(krb5_context context,
63 : krb5_ccache id,
64 : const char **name,
65 : const char **col,
66 : const char **sub)
67 : {
68 102114 : if (name)
69 102114 : *name = MCACHE(id)->name;
70 102114 : if (col)
71 0 : *col = NULL;
72 102114 : if (sub)
73 0 : *sub = MCACHE(id)->name;
74 102114 : return 0;
75 : }
76 :
77 : static krb5_error_code
78 284100 : mcc_alloc(krb5_context context, const char *name, krb5_mcache **out)
79 : {
80 3849 : krb5_mcache *m, *m_c;
81 284100 : size_t counter = 0;
82 284100 : int ret = 0;
83 284100 : unsigned create_anonymous = 0;
84 :
85 284100 : *out = NULL;
86 284100 : ALLOC(m, 1);
87 284100 : if(m == NULL)
88 0 : return krb5_enomem(context);
89 :
90 284100 : again:
91 284100 : if (counter > 3) {
92 0 : free(m->name);
93 0 : free(m);
94 0 : return EAGAIN; /* XXX */
95 : }
96 284100 : if(name == NULL) {
97 23113 : ret = asprintf(&m->name, "u%p-%llu", m, (unsigned long long)counter);
98 260987 : } else if (strcmp(name, "anonymous") == 0) {
99 48843 : ret = asprintf(&m->name, "anonymous-%p-%llu", m, (unsigned long long)counter);
100 48843 : create_anonymous = 1;
101 : } else {
102 212144 : m->name = strdup(name);
103 : }
104 284100 : if(ret < 0 || m->name == NULL) {
105 0 : free(m);
106 0 : return krb5_enomem(context);
107 : }
108 :
109 : /* check for dups first */
110 3849 : HEIMDAL_MUTEX_lock(&mcc_mutex);
111 1229737 : for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
112 1066291 : if (strcmp(m->name, m_c->name) == 0)
113 119188 : break;
114 284100 : if (m_c) {
115 120654 : if (name && !create_anonymous) {
116 : /* We raced with another thread to create this cache */
117 120654 : free(m->name);
118 120654 : free(m);
119 120654 : m = m_c;
120 1466 : HEIMDAL_MUTEX_lock(&(m->mutex));
121 120654 : m->refcnt++;
122 120654 : HEIMDAL_MUTEX_unlock(&(m->mutex));
123 : } else {
124 : /* How likely are we to conflict on new_unique anyways?? */
125 0 : counter++;
126 0 : free(m->name);
127 0 : m->name = NULL;
128 0 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
129 0 : goto again;
130 : }
131 1466 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
132 120654 : *out = m;
133 120654 : return 0;
134 : }
135 :
136 163446 : m->anonymous = create_anonymous;
137 163446 : m->dead = 0;
138 163446 : m->refcnt = 1;
139 163446 : m->primary_principal = NULL;
140 163446 : m->creds = NULL;
141 163446 : m->mtime = time(NULL);
142 163446 : m->kdc_offset = 0;
143 163446 : m->next = mcc_head;
144 2383 : HEIMDAL_MUTEX_init(&(m->mutex));
145 163446 : mcc_head = m;
146 2383 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
147 163446 : *out = m;
148 163446 : return 0;
149 : }
150 :
151 : static krb5_error_code KRB5_CALLCONV
152 260987 : mcc_resolve_2(krb5_context context,
153 : krb5_ccache *id,
154 : const char *res,
155 : const char *sub)
156 : {
157 3849 : krb5_error_code ret;
158 3849 : krb5_mcache *m;
159 :
160 264836 : if ((ret = mcc_alloc(context, sub && *sub ? sub : res, &m)))
161 0 : return ret;
162 :
163 260987 : (*id)->data.data = m;
164 260987 : (*id)->data.length = sizeof(*m);
165 :
166 260987 : return 0;
167 : }
168 :
169 :
170 : static krb5_error_code KRB5_CALLCONV
171 23113 : mcc_gen_new(krb5_context context, krb5_ccache *id)
172 : {
173 0 : krb5_error_code ret;
174 0 : krb5_mcache *m;
175 :
176 23113 : if ((ret = mcc_alloc(context, NULL, &m)))
177 0 : return ret;
178 :
179 23113 : (*id)->data.data = m;
180 23113 : (*id)->data.length = sizeof(*m);
181 :
182 23113 : return 0;
183 : }
184 :
185 : static void KRB5_CALLCONV
186 385651 : mcc_destroy_internal(krb5_context context,
187 : krb5_mcache *m)
188 : {
189 5646 : struct link *l;
190 :
191 385651 : if (m->primary_principal != NULL) {
192 228627 : krb5_free_principal (context, m->primary_principal);
193 228627 : m->primary_principal = NULL;
194 : }
195 385651 : m->dead = 1;
196 :
197 385651 : l = m->creds;
198 715760 : while (l != NULL) {
199 5422 : struct link *old;
200 :
201 330109 : krb5_free_cred_contents (context, &l->cred);
202 330109 : old = l;
203 330109 : l = l->next;
204 330109 : free (old);
205 : }
206 :
207 385651 : m->creds = NULL;
208 385651 : return;
209 : }
210 :
211 : static krb5_error_code KRB5_CALLCONV
212 229784 : mcc_initialize(krb5_context context,
213 : krb5_ccache id,
214 : krb5_principal primary_principal)
215 : {
216 229784 : krb5_mcache *m = MCACHE(id);
217 229784 : krb5_error_code ret = 0;
218 3264 : HEIMDAL_MUTEX_lock(&(m->mutex));
219 229784 : heim_assert(m->refcnt != 0, "resurection released mcache");
220 : /*
221 : * It's important to destroy any existing
222 : * creds here, that matches the baheviour
223 : * of all other backends and also the
224 : * MEMORY: backend in MIT.
225 : */
226 229784 : mcc_destroy_internal(context, m);
227 229784 : m->dead = 0;
228 229784 : m->kdc_offset = 0;
229 229784 : m->mtime = time(NULL);
230 229784 : ret = krb5_copy_principal (context,
231 : primary_principal,
232 : &m->primary_principal);
233 3264 : HEIMDAL_MUTEX_unlock(&(m->mutex));
234 229784 : return ret;
235 : }
236 :
237 : static int
238 282562 : mcc_close_internal(krb5_mcache *m)
239 : {
240 3842 : HEIMDAL_MUTEX_lock(&(m->mutex));
241 282562 : heim_assert(m->refcnt != 0, "closed dead cache mcache");
242 282562 : if (--m->refcnt != 0) {
243 : HEIMDAL_MUTEX_unlock(&(m->mutex));
244 102170 : return 0;
245 : }
246 178927 : if (MISDEAD(m)) {
247 155847 : free(m->name);
248 2377 : HEIMDAL_MUTEX_unlock(&(m->mutex));
249 155847 : return 1;
250 : }
251 : HEIMDAL_MUTEX_unlock(&(m->mutex));
252 23080 : return 0;
253 : }
254 :
255 : static krb5_error_code KRB5_CALLCONV
256 282562 : mcc_close(krb5_context context,
257 : krb5_ccache id)
258 : {
259 282562 : krb5_mcache *m = MCACHE(id);
260 :
261 282562 : if (mcc_close_internal(MCACHE(id))) {
262 2377 : HEIMDAL_MUTEX_destroy(&(m->mutex));
263 155847 : krb5_data_free(&id->data);
264 : }
265 282562 : return 0;
266 : }
267 :
268 : static krb5_error_code KRB5_CALLCONV
269 155867 : mcc_destroy(krb5_context context,
270 : krb5_ccache id)
271 : {
272 155867 : krb5_mcache **n, *m = MCACHE(id);
273 :
274 2382 : HEIMDAL_MUTEX_lock(&mcc_mutex);
275 2382 : HEIMDAL_MUTEX_lock(&(m->mutex));
276 155867 : if (m->refcnt == 0)
277 : {
278 0 : HEIMDAL_MUTEX_unlock(&(m->mutex));
279 0 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
280 0 : krb5_abortx(context, "mcc_destroy: refcnt already 0");
281 : }
282 :
283 155867 : if (!MISDEAD(m)) {
284 : /* if this is an active mcache, remove it from the linked
285 : list, and free all data */
286 278906 : for(n = &mcc_head; n && *n; n = &(*n)->next) {
287 278906 : if(m == *n) {
288 155867 : *n = m->next;
289 155867 : break;
290 : }
291 : }
292 155867 : mcc_destroy_internal(context, m);
293 : }
294 2382 : HEIMDAL_MUTEX_unlock(&(m->mutex));
295 2382 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
296 155867 : return 0;
297 : }
298 :
299 : static krb5_error_code KRB5_CALLCONV
300 334343 : mcc_store_cred(krb5_context context,
301 : krb5_ccache id,
302 : krb5_creds *creds)
303 : {
304 334343 : krb5_mcache *m = MCACHE(id);
305 5425 : krb5_error_code ret;
306 5425 : struct link *l;
307 :
308 5425 : HEIMDAL_MUTEX_lock(&(m->mutex));
309 334343 : if (MISDEAD(m))
310 : {
311 : HEIMDAL_MUTEX_unlock(&(m->mutex));
312 0 : return ENOENT;
313 : }
314 :
315 334343 : l = malloc (sizeof(*l));
316 334343 : if (l == NULL)
317 0 : return krb5_enomem(context);
318 334343 : l->next = m->creds;
319 334343 : m->creds = l;
320 334343 : memset (&l->cred, 0, sizeof(l->cred));
321 334343 : ret = krb5_copy_creds_contents (context, creds, &l->cred);
322 334343 : if (ret) {
323 0 : m->creds = l->next;
324 0 : free (l);
325 0 : HEIMDAL_MUTEX_unlock(&(m->mutex));
326 0 : return ret;
327 : }
328 334343 : m->mtime = time(NULL);
329 5425 : HEIMDAL_MUTEX_unlock(&(m->mutex));
330 334343 : return 0;
331 : }
332 :
333 : static krb5_error_code KRB5_CALLCONV
334 798592 : mcc_get_principal(krb5_context context,
335 : krb5_ccache id,
336 : krb5_principal *principal)
337 : {
338 798592 : krb5_mcache *m = MCACHE(id);
339 798592 : krb5_error_code ret = 0;
340 :
341 15472 : HEIMDAL_MUTEX_lock(&(m->mutex));
342 798592 : if (MISDEAD(m) || m->primary_principal == NULL) {
343 : HEIMDAL_MUTEX_unlock(&(m->mutex));
344 19527 : return ENOENT;
345 : }
346 779065 : ret = krb5_copy_principal (context,
347 763593 : m->primary_principal,
348 : principal);
349 15472 : HEIMDAL_MUTEX_unlock(&(m->mutex));
350 779065 : return ret;
351 : }
352 :
353 : static krb5_error_code KRB5_CALLCONV
354 710512 : mcc_get_first (krb5_context context,
355 : krb5_ccache id,
356 : krb5_cc_cursor *cursor)
357 : {
358 710512 : krb5_mcache *m = MCACHE(id);
359 :
360 15043 : HEIMDAL_MUTEX_lock(&(m->mutex));
361 710512 : if (MISDEAD(m)) {
362 : HEIMDAL_MUTEX_unlock(&(m->mutex));
363 0 : return ENOENT;
364 : }
365 710512 : *cursor = m->creds;
366 :
367 15043 : HEIMDAL_MUTEX_unlock(&(m->mutex));
368 710512 : return 0;
369 : }
370 :
371 : static krb5_error_code KRB5_CALLCONV
372 1900851 : mcc_get_next (krb5_context context,
373 : krb5_ccache id,
374 : krb5_cc_cursor *cursor,
375 : krb5_creds *creds)
376 : {
377 1900851 : krb5_mcache *m = MCACHE(id);
378 40465 : struct link *l;
379 :
380 40465 : HEIMDAL_MUTEX_lock(&(m->mutex));
381 1900851 : if (MISDEAD(m)) {
382 : HEIMDAL_MUTEX_unlock(&(m->mutex));
383 0 : return ENOENT;
384 : }
385 40465 : HEIMDAL_MUTEX_unlock(&(m->mutex));
386 :
387 1900851 : l = *cursor;
388 1900851 : if (l != NULL) {
389 1579710 : *cursor = l->next;
390 1579710 : return krb5_copy_creds_contents (context,
391 1579710 : &l->cred,
392 : creds);
393 : } else
394 314932 : return KRB5_CC_END;
395 : }
396 :
397 : static krb5_error_code KRB5_CALLCONV
398 710512 : mcc_end_get (krb5_context context,
399 : krb5_ccache id,
400 : krb5_cc_cursor *cursor)
401 : {
402 710512 : return 0;
403 : }
404 :
405 : static krb5_error_code KRB5_CALLCONV
406 85817 : mcc_remove_cred(krb5_context context,
407 : krb5_ccache id,
408 : krb5_flags which,
409 : krb5_creds *mcreds)
410 : {
411 85817 : krb5_mcache *m = MCACHE(id);
412 1466 : struct link **q, *p;
413 :
414 1466 : HEIMDAL_MUTEX_lock(&(m->mutex));
415 :
416 171634 : for(q = &m->creds, p = *q; p; p = *q) {
417 85817 : if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
418 0 : *q = p->next;
419 0 : krb5_free_cred_contents(context, &p->cred);
420 0 : free(p);
421 0 : m->mtime = time(NULL);
422 : } else
423 85817 : q = &p->next;
424 : }
425 1466 : HEIMDAL_MUTEX_unlock(&(m->mutex));
426 85817 : return 0;
427 : }
428 :
429 : static krb5_error_code KRB5_CALLCONV
430 0 : mcc_set_flags(krb5_context context,
431 : krb5_ccache id,
432 : krb5_flags flags)
433 : {
434 0 : return 0; /* XXX */
435 : }
436 :
437 : struct mcache_iter {
438 : krb5_mcache *cache;
439 : };
440 :
441 : static krb5_mcache *
442 52 : mcc_get_cache_find_next_internal(krb5_mcache *next)
443 : {
444 0 : HEIMDAL_MUTEX_lock(&mcc_mutex);
445 52 : for (; next != NULL && next->anonymous; next = next->next) {
446 : /* noop: iterate over all anonymous entries */
447 0 : }
448 52 : if (next != NULL) {
449 0 : HEIMDAL_MUTEX_lock(&(next->mutex));
450 0 : next->refcnt++;
451 0 : HEIMDAL_MUTEX_unlock(&(next->mutex));
452 0 : next = next->next;
453 : }
454 0 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
455 :
456 52 : return next;
457 : }
458 :
459 : static krb5_error_code KRB5_CALLCONV
460 52 : mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
461 : {
462 0 : struct mcache_iter *iter;
463 :
464 52 : iter = calloc(1, sizeof(*iter));
465 52 : if (iter == NULL)
466 0 : return krb5_enomem(context);
467 :
468 52 : iter->cache = mcc_get_cache_find_next_internal(mcc_head);
469 :
470 52 : *cursor = iter;
471 52 : return 0;
472 : }
473 :
474 : static krb5_error_code KRB5_CALLCONV
475 52 : mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
476 : {
477 52 : struct mcache_iter *iter = cursor;
478 0 : krb5_error_code ret;
479 0 : krb5_mcache *m;
480 :
481 52 : if (iter->cache == NULL)
482 52 : return KRB5_CC_END;
483 :
484 0 : m = iter->cache;
485 0 : iter->cache = mcc_get_cache_find_next_internal(m);
486 :
487 0 : ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
488 0 : if (ret)
489 0 : return ret;
490 :
491 0 : (*id)->data.data = m;
492 0 : (*id)->data.length = sizeof(*m);
493 :
494 0 : return 0;
495 : }
496 :
497 : static krb5_error_code KRB5_CALLCONV
498 52 : mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
499 : {
500 52 : struct mcache_iter *iter = cursor;
501 :
502 52 : if (iter->cache)
503 0 : mcc_close_internal(iter->cache);
504 52 : iter->cache = NULL;
505 52 : free(iter);
506 52 : return 0;
507 : }
508 :
509 : static krb5_error_code KRB5_CALLCONV
510 0 : mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
511 : {
512 0 : krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
513 0 : struct link *creds;
514 0 : krb5_principal principal;
515 0 : krb5_mcache **n;
516 :
517 0 : HEIMDAL_MUTEX_lock(&mcc_mutex);
518 :
519 : /* drop the from cache from the linked list to avoid lookups */
520 0 : for(n = &mcc_head; n && *n; n = &(*n)->next) {
521 0 : if(mfrom == *n) {
522 0 : *n = mfrom->next;
523 0 : break;
524 : }
525 : }
526 :
527 0 : HEIMDAL_MUTEX_lock(&(mfrom->mutex));
528 0 : HEIMDAL_MUTEX_lock(&(mto->mutex));
529 : /* swap creds */
530 0 : creds = mto->creds;
531 0 : mto->creds = mfrom->creds;
532 0 : mfrom->creds = creds;
533 : /* swap principal */
534 0 : principal = mto->primary_principal;
535 0 : mto->primary_principal = mfrom->primary_principal;
536 0 : mfrom->primary_principal = principal;
537 :
538 0 : mto->mtime = mfrom->mtime = time(NULL);
539 :
540 0 : HEIMDAL_MUTEX_unlock(&(mfrom->mutex));
541 0 : HEIMDAL_MUTEX_unlock(&(mto->mutex));
542 0 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
543 :
544 0 : krb5_cc_destroy(context, from);
545 0 : return 0;
546 : }
547 :
548 : static krb5_error_code KRB5_CALLCONV
549 0 : mcc_default_name(krb5_context context, char **str)
550 : {
551 0 : *str = strdup("MEMORY:");
552 0 : if (*str == NULL)
553 0 : return krb5_enomem(context);
554 0 : return 0;
555 : }
556 :
557 : static krb5_error_code KRB5_CALLCONV
558 0 : mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
559 : {
560 0 : krb5_mcache *m = MCACHE(id);
561 0 : HEIMDAL_MUTEX_lock(&(m->mutex));
562 0 : *mtime = m->mtime;
563 0 : HEIMDAL_MUTEX_unlock(&(m->mutex));
564 0 : return 0;
565 : }
566 :
567 : static krb5_error_code KRB5_CALLCONV
568 0 : mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
569 : {
570 0 : krb5_mcache *m = MCACHE(id);
571 0 : HEIMDAL_MUTEX_lock(&(m->mutex));
572 0 : m->kdc_offset = kdc_offset;
573 0 : HEIMDAL_MUTEX_unlock(&(m->mutex));
574 0 : return 0;
575 : }
576 :
577 : static krb5_error_code KRB5_CALLCONV
578 21592 : mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
579 : {
580 21592 : krb5_mcache *m = MCACHE(id);
581 623 : HEIMDAL_MUTEX_lock(&(m->mutex));
582 21592 : *kdc_offset = m->kdc_offset;
583 623 : HEIMDAL_MUTEX_unlock(&(m->mutex));
584 21592 : return 0;
585 : }
586 :
587 :
588 : /**
589 : * Variable containing the MEMORY based credential cache implemention.
590 : *
591 : * @ingroup krb5_ccache
592 : */
593 :
594 : KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
595 : KRB5_CC_OPS_VERSION_5,
596 : "MEMORY",
597 : NULL,
598 : NULL,
599 : mcc_gen_new,
600 : mcc_initialize,
601 : mcc_destroy,
602 : mcc_close,
603 : mcc_store_cred,
604 : NULL, /* mcc_retrieve */
605 : mcc_get_principal,
606 : mcc_get_first,
607 : mcc_get_next,
608 : mcc_end_get,
609 : mcc_remove_cred,
610 : mcc_set_flags,
611 : NULL,
612 : mcc_get_cache_first,
613 : mcc_get_cache_next,
614 : mcc_end_cache_get,
615 : mcc_move,
616 : mcc_default_name,
617 : NULL,
618 : mcc_lastchange,
619 : mcc_set_kdc_offset,
620 : mcc_get_kdc_offset,
621 : mcc_get_name_2,
622 : mcc_resolve_2
623 : };
|