155682Smarkm/*
2127808Snectar * Copyright (c) 1997-2004 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm
36178825SdfrRCSID("$Id: mcache.c 22107 2007-12-03 17:22:51Z lha $");
3755682Smarkm
3855682Smarkmtypedef struct krb5_mcache {
3972445Sassar    char *name;
4072445Sassar    unsigned int refcnt;
41127808Snectar    int dead;
4255682Smarkm    krb5_principal primary_principal;
4355682Smarkm    struct link {
4455682Smarkm	krb5_creds cred;
4555682Smarkm	struct link *next;
4655682Smarkm    } *creds;
4772445Sassar    struct krb5_mcache *next;
4855682Smarkm} krb5_mcache;
4955682Smarkm
50178825Sdfrstatic HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
5172445Sassarstatic struct krb5_mcache *mcc_head;
5272445Sassar
5372445Sassar#define	MCACHE(X)	((krb5_mcache *)(X)->data.data)
5472445Sassar
55127808Snectar#define MISDEAD(X)	((X)->dead)
5672445Sassar
57102644Snectarstatic const char*
5855682Smarkmmcc_get_name(krb5_context context,
5955682Smarkm	     krb5_ccache id)
6055682Smarkm{
6172445Sassar    return MCACHE(id)->name;
6255682Smarkm}
6355682Smarkm
6472445Sassarstatic krb5_mcache *
6572445Sassarmcc_alloc(const char *name)
6672445Sassar{
67178825Sdfr    krb5_mcache *m, *m_c;
6878527Sassar
6972445Sassar    ALLOC(m, 1);
7072445Sassar    if(m == NULL)
7172445Sassar	return NULL;
7272445Sassar    if(name == NULL)
7372445Sassar	asprintf(&m->name, "%p", m);
7472445Sassar    else
7572445Sassar	m->name = strdup(name);
7672445Sassar    if(m->name == NULL) {
7772445Sassar	free(m);
7872445Sassar	return NULL;
7972445Sassar    }
80178825Sdfr    /* check for dups first */
81178825Sdfr    HEIMDAL_MUTEX_lock(&mcc_mutex);
82178825Sdfr    for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
83178825Sdfr	if (strcmp(m->name, m_c->name) == 0)
84178825Sdfr	    break;
85178825Sdfr    if (m_c) {
86178825Sdfr	free(m->name);
87178825Sdfr	free(m);
88178825Sdfr	HEIMDAL_MUTEX_unlock(&mcc_mutex);
89178825Sdfr	return NULL;
90178825Sdfr    }
91178825Sdfr
92127808Snectar    m->dead = 0;
9372445Sassar    m->refcnt = 1;
9472445Sassar    m->primary_principal = NULL;
9572445Sassar    m->creds = NULL;
9672445Sassar    m->next = mcc_head;
9772445Sassar    mcc_head = m;
98178825Sdfr    HEIMDAL_MUTEX_unlock(&mcc_mutex);
9972445Sassar    return m;
10072445Sassar}
10172445Sassar
10255682Smarkmstatic krb5_error_code
10355682Smarkmmcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
10455682Smarkm{
10572445Sassar    krb5_mcache *m;
10672445Sassar
107178825Sdfr    HEIMDAL_MUTEX_lock(&mcc_mutex);
10872445Sassar    for (m = mcc_head; m != NULL; m = m->next)
10972445Sassar	if (strcmp(m->name, res) == 0)
11072445Sassar	    break;
111178825Sdfr    HEIMDAL_MUTEX_unlock(&mcc_mutex);
11272445Sassar
11372445Sassar    if (m != NULL) {
11472445Sassar	m->refcnt++;
11572445Sassar	(*id)->data.data = m;
11672445Sassar	(*id)->data.length = sizeof(*m);
11772445Sassar	return 0;
11872445Sassar    }
11972445Sassar
12072445Sassar    m = mcc_alloc(res);
12178527Sassar    if (m == NULL) {
12278527Sassar	krb5_set_error_string (context, "malloc: out of memory");
12372445Sassar	return KRB5_CC_NOMEM;
12478527Sassar    }
12572445Sassar
12672445Sassar    (*id)->data.data = m;
12772445Sassar    (*id)->data.length = sizeof(*m);
12872445Sassar
12972445Sassar    return 0;
13055682Smarkm}
13155682Smarkm
13272445Sassar
13355682Smarkmstatic krb5_error_code
13455682Smarkmmcc_gen_new(krb5_context context, krb5_ccache *id)
13555682Smarkm{
13655682Smarkm    krb5_mcache *m;
13755682Smarkm
13872445Sassar    m = mcc_alloc(NULL);
13972445Sassar
14078527Sassar    if (m == NULL) {
14178527Sassar	krb5_set_error_string (context, "malloc: out of memory");
14255682Smarkm	return KRB5_CC_NOMEM;
14378527Sassar    }
14472445Sassar
14555682Smarkm    (*id)->data.data = m;
14655682Smarkm    (*id)->data.length = sizeof(*m);
14772445Sassar
14855682Smarkm    return 0;
14955682Smarkm}
15055682Smarkm
15155682Smarkmstatic krb5_error_code
15255682Smarkmmcc_initialize(krb5_context context,
15355682Smarkm	       krb5_ccache id,
15455682Smarkm	       krb5_principal primary_principal)
15555682Smarkm{
156127808Snectar    krb5_mcache *m = MCACHE(id);
157127808Snectar    m->dead = 0;
15872445Sassar    return krb5_copy_principal (context,
15972445Sassar				primary_principal,
160127808Snectar				&m->primary_principal);
16155682Smarkm}
16255682Smarkm
163178825Sdfrstatic int
164178825Sdfrmcc_close_internal(krb5_mcache *m)
16555682Smarkm{
16672445Sassar    if (--m->refcnt != 0)
16772445Sassar	return 0;
16855682Smarkm
16972445Sassar    if (MISDEAD(m)) {
17072445Sassar	free (m->name);
171178825Sdfr	return 1;
17255682Smarkm    }
173178825Sdfr    return 0;
174178825Sdfr}
17572445Sassar
176178825Sdfrstatic krb5_error_code
177178825Sdfrmcc_close(krb5_context context,
178178825Sdfr	  krb5_ccache id)
179178825Sdfr{
180178825Sdfr    if (mcc_close_internal(MCACHE(id)))
181178825Sdfr	krb5_data_free(&id->data);
18255682Smarkm    return 0;
18355682Smarkm}
18455682Smarkm
18555682Smarkmstatic krb5_error_code
18655682Smarkmmcc_destroy(krb5_context context,
18755682Smarkm	    krb5_ccache id)
18855682Smarkm{
18972445Sassar    krb5_mcache **n, *m = MCACHE(id);
19072445Sassar    struct link *l;
19172445Sassar
19272445Sassar    if (m->refcnt == 0)
19372445Sassar	krb5_abortx(context, "mcc_destroy: refcnt already 0");
19472445Sassar
19572445Sassar    if (!MISDEAD(m)) {
19672445Sassar	/* if this is an active mcache, remove it from the linked
19772445Sassar           list, and free all data */
198178825Sdfr	HEIMDAL_MUTEX_lock(&mcc_mutex);
19972445Sassar	for(n = &mcc_head; n && *n; n = &(*n)->next) {
20072445Sassar	    if(m == *n) {
20172445Sassar		*n = m->next;
20272445Sassar		break;
20372445Sassar	    }
20472445Sassar	}
205178825Sdfr	HEIMDAL_MUTEX_unlock(&mcc_mutex);
206127808Snectar	if (m->primary_principal != NULL) {
207127808Snectar	    krb5_free_principal (context, m->primary_principal);
208127808Snectar	    m->primary_principal = NULL;
209127808Snectar	}
210127808Snectar	m->dead = 1;
211127808Snectar
21272445Sassar	l = m->creds;
21372445Sassar	while (l != NULL) {
21472445Sassar	    struct link *old;
21572445Sassar
216178825Sdfr	    krb5_free_cred_contents (context, &l->cred);
21772445Sassar	    old = l;
21872445Sassar	    l = l->next;
21972445Sassar	    free (old);
22072445Sassar	}
22172445Sassar	m->creds = NULL;
22272445Sassar    }
22355682Smarkm    return 0;
22455682Smarkm}
22555682Smarkm
22655682Smarkmstatic krb5_error_code
22755682Smarkmmcc_store_cred(krb5_context context,
22855682Smarkm	       krb5_ccache id,
22955682Smarkm	       krb5_creds *creds)
23055682Smarkm{
23172445Sassar    krb5_mcache *m = MCACHE(id);
23255682Smarkm    krb5_error_code ret;
23355682Smarkm    struct link *l;
23455682Smarkm
23572445Sassar    if (MISDEAD(m))
23672445Sassar	return ENOENT;
23772445Sassar
23855682Smarkm    l = malloc (sizeof(*l));
23978527Sassar    if (l == NULL) {
24078527Sassar	krb5_set_error_string (context, "malloc: out of memory");
24155682Smarkm	return KRB5_CC_NOMEM;
24278527Sassar    }
24355682Smarkm    l->next = m->creds;
24455682Smarkm    m->creds = l;
24555682Smarkm    memset (&l->cred, 0, sizeof(l->cred));
24655682Smarkm    ret = krb5_copy_creds_contents (context, creds, &l->cred);
24755682Smarkm    if (ret) {
24855682Smarkm	m->creds = l->next;
24955682Smarkm	free (l);
25055682Smarkm	return ret;
25155682Smarkm    }
25255682Smarkm    return 0;
25355682Smarkm}
25455682Smarkm
25555682Smarkmstatic krb5_error_code
25655682Smarkmmcc_get_principal(krb5_context context,
25755682Smarkm		  krb5_ccache id,
25855682Smarkm		  krb5_principal *principal)
25955682Smarkm{
26072445Sassar    krb5_mcache *m = MCACHE(id);
26155682Smarkm
262127808Snectar    if (MISDEAD(m) || m->primary_principal == NULL)
26372445Sassar	return ENOENT;
26455682Smarkm    return krb5_copy_principal (context,
26555682Smarkm				m->primary_principal,
26655682Smarkm				principal);
26755682Smarkm}
26855682Smarkm
26955682Smarkmstatic krb5_error_code
27055682Smarkmmcc_get_first (krb5_context context,
27155682Smarkm	       krb5_ccache id,
27255682Smarkm	       krb5_cc_cursor *cursor)
27355682Smarkm{
27472445Sassar    krb5_mcache *m = MCACHE(id);
27572445Sassar
27672445Sassar    if (MISDEAD(m))
27772445Sassar	return ENOENT;
27872445Sassar
27955682Smarkm    *cursor = m->creds;
28055682Smarkm    return 0;
28155682Smarkm}
28255682Smarkm
28355682Smarkmstatic krb5_error_code
28455682Smarkmmcc_get_next (krb5_context context,
28555682Smarkm	      krb5_ccache id,
28655682Smarkm	      krb5_cc_cursor *cursor,
28755682Smarkm	      krb5_creds *creds)
28855682Smarkm{
28972445Sassar    krb5_mcache *m = MCACHE(id);
29055682Smarkm    struct link *l;
29155682Smarkm
29272445Sassar    if (MISDEAD(m))
29372445Sassar	return ENOENT;
29472445Sassar
29555682Smarkm    l = *cursor;
29655682Smarkm    if (l != NULL) {
29755682Smarkm	*cursor = l->next;
29855682Smarkm	return krb5_copy_creds_contents (context,
29955682Smarkm					 &l->cred,
30055682Smarkm					 creds);
30155682Smarkm    } else
30255682Smarkm	return KRB5_CC_END;
30355682Smarkm}
30455682Smarkm
30555682Smarkmstatic krb5_error_code
30655682Smarkmmcc_end_get (krb5_context context,
30755682Smarkm	     krb5_ccache id,
30855682Smarkm	     krb5_cc_cursor *cursor)
30955682Smarkm{
31055682Smarkm    return 0;
31155682Smarkm}
31255682Smarkm
31355682Smarkmstatic krb5_error_code
31455682Smarkmmcc_remove_cred(krb5_context context,
31555682Smarkm		 krb5_ccache id,
31655682Smarkm		 krb5_flags which,
31772445Sassar		 krb5_creds *mcreds)
31855682Smarkm{
31972445Sassar    krb5_mcache *m = MCACHE(id);
32072445Sassar    struct link **q, *p;
32172445Sassar    for(q = &m->creds, p = *q; p; p = *q) {
32272445Sassar	if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
32372445Sassar	    *q = p->next;
324178825Sdfr	    krb5_free_cred_contents(context, &p->cred);
32572445Sassar	    free(p);
32672445Sassar	} else
32772445Sassar	    q = &p->next;
32872445Sassar    }
32972445Sassar    return 0;
33055682Smarkm}
33155682Smarkm
33255682Smarkmstatic krb5_error_code
33355682Smarkmmcc_set_flags(krb5_context context,
33455682Smarkm	      krb5_ccache id,
33555682Smarkm	      krb5_flags flags)
33655682Smarkm{
33755682Smarkm    return 0; /* XXX */
33855682Smarkm}
33955682Smarkm
340178825Sdfrstruct mcache_iter {
341178825Sdfr    krb5_mcache *cache;
342178825Sdfr};
343178825Sdfr
344178825Sdfrstatic krb5_error_code
345178825Sdfrmcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
346178825Sdfr{
347178825Sdfr    struct mcache_iter *iter;
348178825Sdfr
349178825Sdfr    iter = calloc(1, sizeof(*iter));
350178825Sdfr    if (iter == NULL) {
351178825Sdfr	krb5_set_error_string(context, "malloc - out of memory");
352178825Sdfr	return ENOMEM;
353178825Sdfr    }
354178825Sdfr
355178825Sdfr    HEIMDAL_MUTEX_lock(&mcc_mutex);
356178825Sdfr    iter->cache = mcc_head;
357178825Sdfr    if (iter->cache)
358178825Sdfr	iter->cache->refcnt++;
359178825Sdfr    HEIMDAL_MUTEX_unlock(&mcc_mutex);
360178825Sdfr
361178825Sdfr    *cursor = iter;
362178825Sdfr    return 0;
363178825Sdfr}
364178825Sdfr
365178825Sdfrstatic krb5_error_code
366178825Sdfrmcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
367178825Sdfr{
368178825Sdfr    struct mcache_iter *iter = cursor;
369178825Sdfr    krb5_error_code ret;
370178825Sdfr    krb5_mcache *m;
371178825Sdfr
372178825Sdfr    if (iter->cache == NULL)
373178825Sdfr	return KRB5_CC_END;
374178825Sdfr
375178825Sdfr    HEIMDAL_MUTEX_lock(&mcc_mutex);
376178825Sdfr    m = iter->cache;
377178825Sdfr    if (m->next)
378178825Sdfr	m->next->refcnt++;
379178825Sdfr    iter->cache = m->next;
380178825Sdfr    HEIMDAL_MUTEX_unlock(&mcc_mutex);
381178825Sdfr
382178825Sdfr    ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
383178825Sdfr    if (ret)
384178825Sdfr	return ret;
385178825Sdfr
386178825Sdfr    (*id)->data.data = m;
387178825Sdfr    (*id)->data.length = sizeof(*m);
388178825Sdfr
389178825Sdfr    return 0;
390178825Sdfr}
391178825Sdfr
392178825Sdfrstatic krb5_error_code
393178825Sdfrmcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
394178825Sdfr{
395178825Sdfr    struct mcache_iter *iter = cursor;
396178825Sdfr
397178825Sdfr    if (iter->cache)
398178825Sdfr	mcc_close_internal(iter->cache);
399178825Sdfr    iter->cache = NULL;
400178825Sdfr    free(iter);
401178825Sdfr    return 0;
402178825Sdfr}
403178825Sdfr
404178825Sdfrstatic krb5_error_code
405178825Sdfrmcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
406178825Sdfr{
407178825Sdfr    krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
408178825Sdfr    struct link *creds;
409178825Sdfr    krb5_principal principal;
410178825Sdfr    krb5_mcache **n;
411178825Sdfr
412178825Sdfr    HEIMDAL_MUTEX_lock(&mcc_mutex);
413178825Sdfr
414178825Sdfr    /* drop the from cache from the linked list to avoid lookups */
415178825Sdfr    for(n = &mcc_head; n && *n; n = &(*n)->next) {
416178825Sdfr	if(mfrom == *n) {
417178825Sdfr	    *n = mfrom->next;
418178825Sdfr	    break;
419178825Sdfr	}
420178825Sdfr    }
421178825Sdfr
422178825Sdfr    /* swap creds */
423178825Sdfr    creds = mto->creds;
424178825Sdfr    mto->creds = mfrom->creds;
425178825Sdfr    mfrom->creds = creds;
426178825Sdfr    /* swap principal */
427178825Sdfr    principal = mto->primary_principal;
428178825Sdfr    mto->primary_principal = mfrom->primary_principal;
429178825Sdfr    mfrom->primary_principal = principal;
430178825Sdfr
431178825Sdfr    HEIMDAL_MUTEX_unlock(&mcc_mutex);
432178825Sdfr    mcc_destroy(context, from);
433178825Sdfr
434178825Sdfr    return 0;
435178825Sdfr}
436178825Sdfr
437178825Sdfrstatic krb5_error_code
438178825Sdfrmcc_default_name(krb5_context context, char **str)
439178825Sdfr{
440178825Sdfr    *str = strdup("MEMORY:");
441178825Sdfr    if (*str == NULL) {
442178825Sdfr	krb5_set_error_string(context, "out of memory");
443178825Sdfr	return ENOMEM;
444178825Sdfr    }
445178825Sdfr    return 0;
446178825Sdfr}
447178825Sdfr
448178825Sdfr
449178825Sdfr/**
450178825Sdfr * Variable containing the MEMORY based credential cache implemention.
451178825Sdfr *
452178825Sdfr * @ingroup krb5_ccache
453178825Sdfr */
454178825Sdfr
45555682Smarkmconst krb5_cc_ops krb5_mcc_ops = {
45655682Smarkm    "MEMORY",
45755682Smarkm    mcc_get_name,
45855682Smarkm    mcc_resolve,
45955682Smarkm    mcc_gen_new,
46055682Smarkm    mcc_initialize,
46155682Smarkm    mcc_destroy,
46255682Smarkm    mcc_close,
46355682Smarkm    mcc_store_cred,
46455682Smarkm    NULL, /* mcc_retrieve */
46555682Smarkm    mcc_get_principal,
46655682Smarkm    mcc_get_first,
46755682Smarkm    mcc_get_next,
46855682Smarkm    mcc_end_get,
46955682Smarkm    mcc_remove_cred,
470178825Sdfr    mcc_set_flags,
471178825Sdfr    NULL,
472178825Sdfr    mcc_get_cache_first,
473178825Sdfr    mcc_get_cache_next,
474178825Sdfr    mcc_end_cache_get,
475178825Sdfr    mcc_move,
476178825Sdfr    mcc_default_name
47755682Smarkm};
478