libtommath: Fix possible integer overflow CVE-2023-36328
[heimdal.git] / lib / krb5 / mcache.c
blob80b510757d573328cd051643f5102314d0a88916
1 /*
2 * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #include "krb5_locl.h"
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;
54 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
55 static struct krb5_mcache *mcc_head;
57 #define MCACHE(X) ((krb5_mcache *)(X)->data.data)
59 #define MISDEAD(X) ((X)->dead)
61 static krb5_error_code KRB5_CALLCONV
62 mcc_get_name_2(krb5_context context,
63 krb5_ccache id,
64 const char **name,
65 const char **col,
66 const char **sub)
68 if (name)
69 *name = MCACHE(id)->name;
70 if (col)
71 *col = NULL;
72 if (sub)
73 *sub = MCACHE(id)->name;
74 return 0;
77 static krb5_error_code
78 mcc_alloc(krb5_context context, const char *name, krb5_mcache **out)
80 krb5_mcache *m, *m_c;
81 size_t counter = 0;
82 int ret = 0;
84 *out = NULL;
85 ALLOC(m, 1);
86 if(m == NULL)
87 return krb5_enomem(context);
89 again:
90 if (counter > 3) {
91 free(m->name);
92 free(m);
93 return EAGAIN; /* XXX */
95 if(name == NULL)
96 ret = asprintf(&m->name, "u%p-%llu", m, (unsigned long long)counter);
97 else
98 m->name = strdup(name);
99 if(ret < 0 || m->name == NULL) {
100 free(m);
101 return krb5_enomem(context);
103 if (strcmp(m->name, "anonymous") == 0) {
104 HEIMDAL_MUTEX_init(&(m->mutex));
105 m->anonymous = 1;
106 m->dead = 0;
107 m->refcnt = 1;
108 m->primary_principal = NULL;
109 m->creds = NULL;
110 m->mtime = time(NULL);
111 m->kdc_offset = 0;
112 m->next = NULL;
113 *out = m;
114 return 0;
117 /* check for dups first */
118 HEIMDAL_MUTEX_lock(&mcc_mutex);
119 for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
120 if (strcmp(m->name, m_c->name) == 0)
121 break;
122 if (m_c) {
123 if (name) {
124 /* We raced with another thread to create this cache */
125 free(m->name);
126 free(m);
127 m = m_c;
128 HEIMDAL_MUTEX_lock(&(m->mutex));
129 m->refcnt++;
130 HEIMDAL_MUTEX_unlock(&(m->mutex));
131 } else {
132 /* How likely are we to conflict on new_unique anyways?? */
133 counter++;
134 free(m->name);
135 m->name = NULL;
136 HEIMDAL_MUTEX_unlock(&mcc_mutex);
137 goto again;
139 HEIMDAL_MUTEX_unlock(&mcc_mutex);
140 *out = m;
141 return 0;
144 m->anonymous = 0;
145 m->dead = 0;
146 m->refcnt = 1;
147 m->primary_principal = NULL;
148 m->creds = NULL;
149 m->mtime = time(NULL);
150 m->kdc_offset = 0;
151 m->next = mcc_head;
152 HEIMDAL_MUTEX_init(&(m->mutex));
153 mcc_head = m;
154 HEIMDAL_MUTEX_unlock(&mcc_mutex);
155 *out = m;
156 return 0;
159 static krb5_error_code KRB5_CALLCONV
160 mcc_resolve_2(krb5_context context,
161 krb5_ccache *id,
162 const char *res,
163 const char *sub)
165 krb5_error_code ret;
166 krb5_mcache *m;
168 if ((ret = mcc_alloc(context, sub && *sub ? sub : res, &m)))
169 return ret;
171 (*id)->data.data = m;
172 (*id)->data.length = sizeof(*m);
174 return 0;
178 static krb5_error_code KRB5_CALLCONV
179 mcc_gen_new(krb5_context context, krb5_ccache *id)
181 krb5_error_code ret;
182 krb5_mcache *m;
184 if ((ret = mcc_alloc(context, NULL, &m)))
185 return ret;
187 (*id)->data.data = m;
188 (*id)->data.length = sizeof(*m);
190 return 0;
193 static void KRB5_CALLCONV
194 mcc_destroy_internal(krb5_context context,
195 krb5_mcache *m)
197 struct link *l;
199 if (m->primary_principal != NULL) {
200 krb5_free_principal (context, m->primary_principal);
201 m->primary_principal = NULL;
203 m->dead = 1;
205 l = m->creds;
206 while (l != NULL) {
207 struct link *old;
209 krb5_free_cred_contents (context, &l->cred);
210 old = l;
211 l = l->next;
212 free (old);
215 m->creds = NULL;
216 return;
219 static krb5_error_code KRB5_CALLCONV
220 mcc_initialize(krb5_context context,
221 krb5_ccache id,
222 krb5_principal primary_principal)
224 krb5_mcache *m = MCACHE(id);
225 krb5_error_code ret = 0;
226 HEIMDAL_MUTEX_lock(&(m->mutex));
227 heim_assert(m->refcnt != 0, "resurection released mcache");
229 * It's important to destroy any existing
230 * creds here, that matches the baheviour
231 * of all other backends and also the
232 * MEMORY: backend in MIT.
234 mcc_destroy_internal(context, m);
235 m->dead = 0;
236 m->kdc_offset = context->kdc_sec_offset;
237 m->mtime = time(NULL);
238 ret = krb5_copy_principal (context,
239 primary_principal,
240 &m->primary_principal);
241 HEIMDAL_MUTEX_unlock(&(m->mutex));
242 return ret;
245 static int
246 mcc_close_internal(krb5_mcache *m)
248 HEIMDAL_MUTEX_lock(&(m->mutex));
249 heim_assert(m->refcnt != 0, "closed dead cache mcache");
250 if (--m->refcnt != 0) {
251 HEIMDAL_MUTEX_unlock(&(m->mutex));
252 return 0;
254 if (MISDEAD(m)) {
255 free(m->name);
256 HEIMDAL_MUTEX_unlock(&(m->mutex));
257 return 1;
259 HEIMDAL_MUTEX_unlock(&(m->mutex));
260 return 0;
263 static krb5_error_code KRB5_CALLCONV
264 mcc_close(krb5_context context,
265 krb5_ccache id)
267 krb5_mcache *m = MCACHE(id);
269 if (mcc_close_internal(MCACHE(id))) {
270 HEIMDAL_MUTEX_destroy(&(m->mutex));
271 krb5_data_free(&id->data);
273 return 0;
276 static krb5_error_code KRB5_CALLCONV
277 mcc_destroy(krb5_context context,
278 krb5_ccache id)
280 krb5_mcache **n, *m = MCACHE(id);
282 if (m->anonymous) {
283 HEIMDAL_MUTEX_lock(&(m->mutex));
284 if (m->refcnt == 0) {
285 HEIMDAL_MUTEX_unlock(&(m->mutex));
286 krb5_abortx(context, "mcc_destroy: refcnt already 0");
288 if (!MISDEAD(m))
289 mcc_destroy_internal(context, m);
290 HEIMDAL_MUTEX_unlock(&(m->mutex));
291 return 0;
294 HEIMDAL_MUTEX_lock(&mcc_mutex);
295 HEIMDAL_MUTEX_lock(&(m->mutex));
296 if (m->refcnt == 0)
298 HEIMDAL_MUTEX_unlock(&(m->mutex));
299 HEIMDAL_MUTEX_unlock(&mcc_mutex);
300 krb5_abortx(context, "mcc_destroy: refcnt already 0");
303 if (!MISDEAD(m)) {
304 /* if this is an active mcache, remove it from the linked
305 list, and free all data */
306 for(n = &mcc_head; n && *n; n = &(*n)->next) {
307 if(m == *n) {
308 *n = m->next;
309 break;
312 mcc_destroy_internal(context, m);
314 HEIMDAL_MUTEX_unlock(&(m->mutex));
315 HEIMDAL_MUTEX_unlock(&mcc_mutex);
316 return 0;
319 static krb5_error_code KRB5_CALLCONV
320 mcc_store_cred(krb5_context context,
321 krb5_ccache id,
322 krb5_creds *creds)
324 krb5_mcache *m = MCACHE(id);
325 krb5_error_code ret;
326 struct link *l;
328 HEIMDAL_MUTEX_lock(&(m->mutex));
329 if (MISDEAD(m))
331 HEIMDAL_MUTEX_unlock(&(m->mutex));
332 return ENOENT;
335 l = malloc (sizeof(*l));
336 if (l == NULL)
337 return krb5_enomem(context);
338 l->next = m->creds;
339 m->creds = l;
340 memset (&l->cred, 0, sizeof(l->cred));
341 ret = krb5_copy_creds_contents (context, creds, &l->cred);
342 if (ret) {
343 m->creds = l->next;
344 free (l);
345 HEIMDAL_MUTEX_unlock(&(m->mutex));
346 return ret;
348 m->mtime = time(NULL);
349 HEIMDAL_MUTEX_unlock(&(m->mutex));
350 return 0;
353 static krb5_error_code KRB5_CALLCONV
354 mcc_get_principal(krb5_context context,
355 krb5_ccache id,
356 krb5_principal *principal)
358 krb5_mcache *m = MCACHE(id);
359 krb5_error_code ret = 0;
361 HEIMDAL_MUTEX_lock(&(m->mutex));
362 if (MISDEAD(m) || m->primary_principal == NULL) {
363 HEIMDAL_MUTEX_unlock(&(m->mutex));
364 return ENOENT;
366 ret = krb5_copy_principal (context,
367 m->primary_principal,
368 principal);
369 HEIMDAL_MUTEX_unlock(&(m->mutex));
370 return ret;
373 static krb5_error_code KRB5_CALLCONV
374 mcc_get_first (krb5_context context,
375 krb5_ccache id,
376 krb5_cc_cursor *cursor)
378 krb5_mcache *m = MCACHE(id);
380 HEIMDAL_MUTEX_lock(&(m->mutex));
381 if (MISDEAD(m)) {
382 HEIMDAL_MUTEX_unlock(&(m->mutex));
383 return ENOENT;
385 *cursor = m->creds;
387 HEIMDAL_MUTEX_unlock(&(m->mutex));
388 return 0;
391 static krb5_error_code KRB5_CALLCONV
392 mcc_get_next (krb5_context context,
393 krb5_ccache id,
394 krb5_cc_cursor *cursor,
395 krb5_creds *creds)
397 krb5_mcache *m = MCACHE(id);
398 struct link *l;
400 HEIMDAL_MUTEX_lock(&(m->mutex));
401 if (MISDEAD(m)) {
402 HEIMDAL_MUTEX_unlock(&(m->mutex));
403 return ENOENT;
405 HEIMDAL_MUTEX_unlock(&(m->mutex));
407 l = *cursor;
408 if (l != NULL) {
409 *cursor = l->next;
410 return krb5_copy_creds_contents (context,
411 &l->cred,
412 creds);
413 } else
414 return KRB5_CC_END;
417 static krb5_error_code KRB5_CALLCONV
418 mcc_end_get (krb5_context context,
419 krb5_ccache id,
420 krb5_cc_cursor *cursor)
422 return 0;
425 static krb5_error_code KRB5_CALLCONV
426 mcc_remove_cred(krb5_context context,
427 krb5_ccache id,
428 krb5_flags which,
429 krb5_creds *mcreds)
431 krb5_mcache *m = MCACHE(id);
432 struct link **q, *p;
434 HEIMDAL_MUTEX_lock(&(m->mutex));
436 for(q = &m->creds, p = *q; p; p = *q) {
437 if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
438 *q = p->next;
439 krb5_free_cred_contents(context, &p->cred);
440 free(p);
441 m->mtime = time(NULL);
442 } else
443 q = &p->next;
445 HEIMDAL_MUTEX_unlock(&(m->mutex));
446 return 0;
449 static krb5_error_code KRB5_CALLCONV
450 mcc_set_flags(krb5_context context,
451 krb5_ccache id,
452 krb5_flags flags)
454 return 0; /* XXX */
457 struct mcache_iter {
458 krb5_mcache *cache;
461 static krb5_error_code KRB5_CALLCONV
462 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
464 struct mcache_iter *iter;
466 iter = calloc(1, sizeof(*iter));
467 if (iter == NULL)
468 return krb5_enomem(context);
470 HEIMDAL_MUTEX_lock(&mcc_mutex);
471 iter->cache = mcc_head;
472 if (iter->cache) {
473 HEIMDAL_MUTEX_lock(&(iter->cache->mutex));
474 iter->cache->refcnt++;
475 HEIMDAL_MUTEX_unlock(&(iter->cache->mutex));
477 HEIMDAL_MUTEX_unlock(&mcc_mutex);
479 *cursor = iter;
480 return 0;
483 static krb5_error_code KRB5_CALLCONV
484 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
486 struct mcache_iter *iter = cursor;
487 krb5_error_code ret;
488 krb5_mcache *m;
490 if (iter->cache == NULL)
491 return KRB5_CC_END;
493 HEIMDAL_MUTEX_lock(&mcc_mutex);
494 m = iter->cache;
495 if (m->next)
497 HEIMDAL_MUTEX_lock(&(m->next->mutex));
498 m->next->refcnt++;
499 HEIMDAL_MUTEX_unlock(&(m->next->mutex));
502 iter->cache = m->next;
503 HEIMDAL_MUTEX_unlock(&mcc_mutex);
505 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
506 if (ret)
507 return ret;
509 (*id)->data.data = m;
510 (*id)->data.length = sizeof(*m);
512 return 0;
515 static krb5_error_code KRB5_CALLCONV
516 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
518 struct mcache_iter *iter = cursor;
520 if (iter->cache)
521 mcc_close_internal(iter->cache);
522 iter->cache = NULL;
523 free(iter);
524 return 0;
527 static krb5_error_code KRB5_CALLCONV
528 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
530 krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
531 struct link *creds;
532 krb5_principal principal;
533 krb5_mcache **n;
535 HEIMDAL_MUTEX_lock(&mcc_mutex);
537 /* drop the from cache from the linked list to avoid lookups */
538 for(n = &mcc_head; n && *n; n = &(*n)->next) {
539 if(mfrom == *n) {
540 *n = mfrom->next;
541 break;
545 HEIMDAL_MUTEX_lock(&(mfrom->mutex));
546 HEIMDAL_MUTEX_lock(&(mto->mutex));
547 /* swap creds */
548 creds = mto->creds;
549 mto->creds = mfrom->creds;
550 mfrom->creds = creds;
551 /* swap principal */
552 principal = mto->primary_principal;
553 mto->primary_principal = mfrom->primary_principal;
554 mfrom->primary_principal = principal;
556 mto->mtime = mfrom->mtime = time(NULL);
558 HEIMDAL_MUTEX_unlock(&(mfrom->mutex));
559 HEIMDAL_MUTEX_unlock(&(mto->mutex));
560 HEIMDAL_MUTEX_unlock(&mcc_mutex);
562 krb5_cc_destroy(context, from);
563 return 0;
566 static krb5_error_code KRB5_CALLCONV
567 mcc_default_name(krb5_context context, char **str)
569 *str = strdup("MEMORY:");
570 if (*str == NULL)
571 return krb5_enomem(context);
572 return 0;
575 static krb5_error_code KRB5_CALLCONV
576 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
578 krb5_mcache *m = MCACHE(id);
579 HEIMDAL_MUTEX_lock(&(m->mutex));
580 *mtime = m->mtime;
581 HEIMDAL_MUTEX_unlock(&(m->mutex));
582 return 0;
585 static krb5_error_code KRB5_CALLCONV
586 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
588 krb5_mcache *m = MCACHE(id);
589 HEIMDAL_MUTEX_lock(&(m->mutex));
590 m->kdc_offset = kdc_offset;
591 HEIMDAL_MUTEX_unlock(&(m->mutex));
592 return 0;
595 static krb5_error_code KRB5_CALLCONV
596 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
598 krb5_mcache *m = MCACHE(id);
599 HEIMDAL_MUTEX_lock(&(m->mutex));
600 *kdc_offset = m->kdc_offset;
601 HEIMDAL_MUTEX_unlock(&(m->mutex));
602 return 0;
607 * Variable containing the MEMORY based credential cache implementation.
609 * @ingroup krb5_ccache
612 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
613 KRB5_CC_OPS_VERSION_5,
614 "MEMORY",
615 NULL,
616 NULL,
617 mcc_gen_new,
618 mcc_initialize,
619 mcc_destroy,
620 mcc_close,
621 mcc_store_cred,
622 NULL, /* mcc_retrieve */
623 mcc_get_principal,
624 mcc_get_first,
625 mcc_get_next,
626 mcc_end_get,
627 mcc_remove_cred,
628 mcc_set_flags,
629 NULL,
630 mcc_get_cache_first,
631 mcc_get_cache_next,
632 mcc_end_cache_get,
633 mcc_move,
634 mcc_default_name,
635 NULL,
636 mcc_lastchange,
637 mcc_set_kdc_offset,
638 mcc_get_kdc_offset,
639 mcc_get_name_2,
640 mcc_resolve_2