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