155682Smarkm/* 2233294Sstas * Copyright (c) 1997-2004 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 755682Smarkm * 8233294Sstas * Redistribution and use in source and binary forms, with or without 9233294Sstas * modification, are permitted provided that the following conditions 10233294Sstas * are met: 1155682Smarkm * 12233294Sstas * 1. Redistributions of source code must retain the above copyright 13233294Sstas * notice, this list of conditions and the following disclaimer. 1455682Smarkm * 15233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 16233294Sstas * notice, this list of conditions and the following disclaimer in the 17233294Sstas * documentation and/or other materials provided with the distribution. 1855682Smarkm * 19233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 20233294Sstas * may be used to endorse or promote products derived from this software 21233294Sstas * without specific prior written permission. 22233294Sstas * 23233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33233294Sstas * SUCH DAMAGE. 3455682Smarkm */ 3555682Smarkm 3655682Smarkm#include "krb5_locl.h" 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; 48233294Sstas time_t mtime; 49233294Sstas krb5_deltat kdc_offset; 5055682Smarkm} krb5_mcache; 5155682Smarkm 52178825Sdfrstatic HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER; 5372445Sassarstatic struct krb5_mcache *mcc_head; 5472445Sassar 5572445Sassar#define MCACHE(X) ((krb5_mcache *)(X)->data.data) 5672445Sassar 57127808Snectar#define MISDEAD(X) ((X)->dead) 5872445Sassar 59233294Sstasstatic const char* KRB5_CALLCONV 6055682Smarkmmcc_get_name(krb5_context context, 6155682Smarkm krb5_ccache id) 6255682Smarkm{ 6372445Sassar return MCACHE(id)->name; 6455682Smarkm} 6555682Smarkm 66233294Sstasstatic krb5_mcache * KRB5_CALLCONV 6772445Sassarmcc_alloc(const char *name) 6872445Sassar{ 69178825Sdfr krb5_mcache *m, *m_c; 70233294Sstas int ret = 0; 7178527Sassar 7272445Sassar ALLOC(m, 1); 7372445Sassar if(m == NULL) 7472445Sassar return NULL; 7572445Sassar if(name == NULL) 76233294Sstas ret = asprintf(&m->name, "%p", m); 7772445Sassar else 7872445Sassar m->name = strdup(name); 79233294Sstas if(ret < 0 || m->name == NULL) { 8072445Sassar free(m); 8172445Sassar return NULL; 8272445Sassar } 83178825Sdfr /* check for dups first */ 84178825Sdfr HEIMDAL_MUTEX_lock(&mcc_mutex); 85178825Sdfr for (m_c = mcc_head; m_c != NULL; m_c = m_c->next) 86178825Sdfr if (strcmp(m->name, m_c->name) == 0) 87178825Sdfr break; 88178825Sdfr if (m_c) { 89178825Sdfr free(m->name); 90178825Sdfr free(m); 91178825Sdfr HEIMDAL_MUTEX_unlock(&mcc_mutex); 92178825Sdfr return NULL; 93178825Sdfr } 94178825Sdfr 95127808Snectar m->dead = 0; 9672445Sassar m->refcnt = 1; 9772445Sassar m->primary_principal = NULL; 9872445Sassar m->creds = NULL; 99233294Sstas m->mtime = time(NULL); 100233294Sstas m->kdc_offset = 0; 10172445Sassar m->next = mcc_head; 10272445Sassar mcc_head = m; 103178825Sdfr HEIMDAL_MUTEX_unlock(&mcc_mutex); 10472445Sassar return m; 10572445Sassar} 10672445Sassar 107233294Sstasstatic krb5_error_code KRB5_CALLCONV 10855682Smarkmmcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 10955682Smarkm{ 11072445Sassar krb5_mcache *m; 11172445Sassar 112178825Sdfr HEIMDAL_MUTEX_lock(&mcc_mutex); 11372445Sassar for (m = mcc_head; m != NULL; m = m->next) 11472445Sassar if (strcmp(m->name, res) == 0) 11572445Sassar break; 116178825Sdfr HEIMDAL_MUTEX_unlock(&mcc_mutex); 11772445Sassar 11872445Sassar if (m != NULL) { 11972445Sassar m->refcnt++; 12072445Sassar (*id)->data.data = m; 12172445Sassar (*id)->data.length = sizeof(*m); 12272445Sassar return 0; 12372445Sassar } 12472445Sassar 12572445Sassar m = mcc_alloc(res); 12678527Sassar if (m == NULL) { 127233294Sstas krb5_set_error_message(context, KRB5_CC_NOMEM, 128233294Sstas N_("malloc: out of memory", "")); 12972445Sassar return KRB5_CC_NOMEM; 13078527Sassar } 131233294Sstas 13272445Sassar (*id)->data.data = m; 13372445Sassar (*id)->data.length = sizeof(*m); 13472445Sassar 13572445Sassar return 0; 13655682Smarkm} 13755682Smarkm 13872445Sassar 139233294Sstasstatic krb5_error_code KRB5_CALLCONV 14055682Smarkmmcc_gen_new(krb5_context context, krb5_ccache *id) 14155682Smarkm{ 14255682Smarkm krb5_mcache *m; 14355682Smarkm 14472445Sassar m = mcc_alloc(NULL); 14572445Sassar 14678527Sassar if (m == NULL) { 147233294Sstas krb5_set_error_message(context, KRB5_CC_NOMEM, 148233294Sstas N_("malloc: out of memory", "")); 14955682Smarkm return KRB5_CC_NOMEM; 15078527Sassar } 15172445Sassar 15255682Smarkm (*id)->data.data = m; 15355682Smarkm (*id)->data.length = sizeof(*m); 15472445Sassar 15555682Smarkm return 0; 15655682Smarkm} 15755682Smarkm 158233294Sstasstatic krb5_error_code KRB5_CALLCONV 15955682Smarkmmcc_initialize(krb5_context context, 16055682Smarkm krb5_ccache id, 16155682Smarkm krb5_principal primary_principal) 16255682Smarkm{ 163127808Snectar krb5_mcache *m = MCACHE(id); 164127808Snectar m->dead = 0; 165233294Sstas m->mtime = time(NULL); 16672445Sassar return krb5_copy_principal (context, 16772445Sassar primary_principal, 168127808Snectar &m->primary_principal); 16955682Smarkm} 17055682Smarkm 171178825Sdfrstatic int 172178825Sdfrmcc_close_internal(krb5_mcache *m) 17355682Smarkm{ 17472445Sassar if (--m->refcnt != 0) 17572445Sassar return 0; 17655682Smarkm 17772445Sassar if (MISDEAD(m)) { 17872445Sassar free (m->name); 179178825Sdfr return 1; 18055682Smarkm } 181178825Sdfr return 0; 182178825Sdfr} 18372445Sassar 184233294Sstasstatic krb5_error_code KRB5_CALLCONV 185178825Sdfrmcc_close(krb5_context context, 186178825Sdfr krb5_ccache id) 187178825Sdfr{ 188178825Sdfr if (mcc_close_internal(MCACHE(id))) 189178825Sdfr krb5_data_free(&id->data); 19055682Smarkm return 0; 19155682Smarkm} 19255682Smarkm 193233294Sstasstatic krb5_error_code KRB5_CALLCONV 19455682Smarkmmcc_destroy(krb5_context context, 19555682Smarkm krb5_ccache id) 19655682Smarkm{ 19772445Sassar krb5_mcache **n, *m = MCACHE(id); 19872445Sassar struct link *l; 19972445Sassar 20072445Sassar if (m->refcnt == 0) 20172445Sassar krb5_abortx(context, "mcc_destroy: refcnt already 0"); 20272445Sassar 20372445Sassar if (!MISDEAD(m)) { 20472445Sassar /* if this is an active mcache, remove it from the linked 20572445Sassar list, and free all data */ 206178825Sdfr HEIMDAL_MUTEX_lock(&mcc_mutex); 20772445Sassar for(n = &mcc_head; n && *n; n = &(*n)->next) { 20872445Sassar if(m == *n) { 20972445Sassar *n = m->next; 21072445Sassar break; 21172445Sassar } 21272445Sassar } 213178825Sdfr HEIMDAL_MUTEX_unlock(&mcc_mutex); 214127808Snectar if (m->primary_principal != NULL) { 215127808Snectar krb5_free_principal (context, m->primary_principal); 216127808Snectar m->primary_principal = NULL; 217127808Snectar } 218127808Snectar m->dead = 1; 219127808Snectar 22072445Sassar l = m->creds; 22172445Sassar while (l != NULL) { 22272445Sassar struct link *old; 223233294Sstas 224178825Sdfr krb5_free_cred_contents (context, &l->cred); 22572445Sassar old = l; 22672445Sassar l = l->next; 22772445Sassar free (old); 22872445Sassar } 22972445Sassar m->creds = NULL; 23072445Sassar } 23155682Smarkm return 0; 23255682Smarkm} 23355682Smarkm 234233294Sstasstatic krb5_error_code KRB5_CALLCONV 23555682Smarkmmcc_store_cred(krb5_context context, 23655682Smarkm krb5_ccache id, 23755682Smarkm krb5_creds *creds) 23855682Smarkm{ 23972445Sassar krb5_mcache *m = MCACHE(id); 24055682Smarkm krb5_error_code ret; 24155682Smarkm struct link *l; 24255682Smarkm 24372445Sassar if (MISDEAD(m)) 24472445Sassar return ENOENT; 24572445Sassar 24655682Smarkm l = malloc (sizeof(*l)); 24778527Sassar if (l == NULL) { 248233294Sstas krb5_set_error_message(context, KRB5_CC_NOMEM, 249233294Sstas N_("malloc: out of memory", "")); 25055682Smarkm return KRB5_CC_NOMEM; 25178527Sassar } 25255682Smarkm l->next = m->creds; 25355682Smarkm m->creds = l; 25455682Smarkm memset (&l->cred, 0, sizeof(l->cred)); 25555682Smarkm ret = krb5_copy_creds_contents (context, creds, &l->cred); 25655682Smarkm if (ret) { 25755682Smarkm m->creds = l->next; 25855682Smarkm free (l); 25955682Smarkm return ret; 26055682Smarkm } 261233294Sstas m->mtime = time(NULL); 26255682Smarkm return 0; 26355682Smarkm} 26455682Smarkm 265233294Sstasstatic krb5_error_code KRB5_CALLCONV 26655682Smarkmmcc_get_principal(krb5_context context, 26755682Smarkm krb5_ccache id, 26855682Smarkm krb5_principal *principal) 26955682Smarkm{ 27072445Sassar krb5_mcache *m = MCACHE(id); 27155682Smarkm 272127808Snectar if (MISDEAD(m) || m->primary_principal == NULL) 27372445Sassar return ENOENT; 27455682Smarkm return krb5_copy_principal (context, 27555682Smarkm m->primary_principal, 27655682Smarkm principal); 27755682Smarkm} 27855682Smarkm 279233294Sstasstatic krb5_error_code KRB5_CALLCONV 28055682Smarkmmcc_get_first (krb5_context context, 28155682Smarkm krb5_ccache id, 28255682Smarkm krb5_cc_cursor *cursor) 28355682Smarkm{ 28472445Sassar krb5_mcache *m = MCACHE(id); 28572445Sassar 28672445Sassar if (MISDEAD(m)) 28772445Sassar return ENOENT; 28872445Sassar 28955682Smarkm *cursor = m->creds; 29055682Smarkm return 0; 29155682Smarkm} 29255682Smarkm 293233294Sstasstatic krb5_error_code KRB5_CALLCONV 29455682Smarkmmcc_get_next (krb5_context context, 29555682Smarkm krb5_ccache id, 29655682Smarkm krb5_cc_cursor *cursor, 29755682Smarkm krb5_creds *creds) 29855682Smarkm{ 29972445Sassar krb5_mcache *m = MCACHE(id); 30055682Smarkm struct link *l; 30155682Smarkm 30272445Sassar if (MISDEAD(m)) 30372445Sassar return ENOENT; 30472445Sassar 30555682Smarkm l = *cursor; 30655682Smarkm if (l != NULL) { 30755682Smarkm *cursor = l->next; 30855682Smarkm return krb5_copy_creds_contents (context, 30955682Smarkm &l->cred, 31055682Smarkm creds); 31155682Smarkm } else 31255682Smarkm return KRB5_CC_END; 31355682Smarkm} 31455682Smarkm 315233294Sstasstatic krb5_error_code KRB5_CALLCONV 31655682Smarkmmcc_end_get (krb5_context context, 31755682Smarkm krb5_ccache id, 31855682Smarkm krb5_cc_cursor *cursor) 31955682Smarkm{ 32055682Smarkm return 0; 32155682Smarkm} 32255682Smarkm 323233294Sstasstatic krb5_error_code KRB5_CALLCONV 32455682Smarkmmcc_remove_cred(krb5_context context, 32555682Smarkm krb5_ccache id, 32655682Smarkm krb5_flags which, 32772445Sassar krb5_creds *mcreds) 32855682Smarkm{ 32972445Sassar krb5_mcache *m = MCACHE(id); 33072445Sassar struct link **q, *p; 33172445Sassar for(q = &m->creds, p = *q; p; p = *q) { 33272445Sassar if(krb5_compare_creds(context, which, mcreds, &p->cred)) { 33372445Sassar *q = p->next; 334178825Sdfr krb5_free_cred_contents(context, &p->cred); 33572445Sassar free(p); 336233294Sstas m->mtime = time(NULL); 33772445Sassar } else 33872445Sassar q = &p->next; 33972445Sassar } 34072445Sassar return 0; 34155682Smarkm} 34255682Smarkm 343233294Sstasstatic krb5_error_code KRB5_CALLCONV 34455682Smarkmmcc_set_flags(krb5_context context, 34555682Smarkm krb5_ccache id, 34655682Smarkm krb5_flags flags) 34755682Smarkm{ 34855682Smarkm return 0; /* XXX */ 34955682Smarkm} 350233294Sstas 351178825Sdfrstruct mcache_iter { 352178825Sdfr krb5_mcache *cache; 353178825Sdfr}; 354178825Sdfr 355233294Sstasstatic krb5_error_code KRB5_CALLCONV 356178825Sdfrmcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 357178825Sdfr{ 358178825Sdfr struct mcache_iter *iter; 359178825Sdfr 360178825Sdfr iter = calloc(1, sizeof(*iter)); 361178825Sdfr if (iter == NULL) { 362233294Sstas krb5_set_error_message(context, ENOMEM, 363233294Sstas N_("malloc: out of memory", "")); 364178825Sdfr return ENOMEM; 365233294Sstas } 366178825Sdfr 367178825Sdfr HEIMDAL_MUTEX_lock(&mcc_mutex); 368178825Sdfr iter->cache = mcc_head; 369178825Sdfr if (iter->cache) 370178825Sdfr iter->cache->refcnt++; 371178825Sdfr HEIMDAL_MUTEX_unlock(&mcc_mutex); 372178825Sdfr 373178825Sdfr *cursor = iter; 374178825Sdfr return 0; 375178825Sdfr} 376178825Sdfr 377233294Sstasstatic krb5_error_code KRB5_CALLCONV 378178825Sdfrmcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 379178825Sdfr{ 380178825Sdfr struct mcache_iter *iter = cursor; 381178825Sdfr krb5_error_code ret; 382178825Sdfr krb5_mcache *m; 383178825Sdfr 384178825Sdfr if (iter->cache == NULL) 385178825Sdfr return KRB5_CC_END; 386178825Sdfr 387178825Sdfr HEIMDAL_MUTEX_lock(&mcc_mutex); 388178825Sdfr m = iter->cache; 389178825Sdfr if (m->next) 390178825Sdfr m->next->refcnt++; 391178825Sdfr iter->cache = m->next; 392178825Sdfr HEIMDAL_MUTEX_unlock(&mcc_mutex); 393178825Sdfr 394178825Sdfr ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id); 395178825Sdfr if (ret) 396178825Sdfr return ret; 397178825Sdfr 398178825Sdfr (*id)->data.data = m; 399178825Sdfr (*id)->data.length = sizeof(*m); 400178825Sdfr 401178825Sdfr return 0; 402178825Sdfr} 403178825Sdfr 404233294Sstasstatic krb5_error_code KRB5_CALLCONV 405178825Sdfrmcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 406178825Sdfr{ 407178825Sdfr struct mcache_iter *iter = cursor; 408178825Sdfr 409178825Sdfr if (iter->cache) 410178825Sdfr mcc_close_internal(iter->cache); 411178825Sdfr iter->cache = NULL; 412178825Sdfr free(iter); 413178825Sdfr return 0; 414178825Sdfr} 415178825Sdfr 416233294Sstasstatic krb5_error_code KRB5_CALLCONV 417178825Sdfrmcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 418178825Sdfr{ 419178825Sdfr krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to); 420178825Sdfr struct link *creds; 421178825Sdfr krb5_principal principal; 422178825Sdfr krb5_mcache **n; 423178825Sdfr 424178825Sdfr HEIMDAL_MUTEX_lock(&mcc_mutex); 425178825Sdfr 426178825Sdfr /* drop the from cache from the linked list to avoid lookups */ 427178825Sdfr for(n = &mcc_head; n && *n; n = &(*n)->next) { 428178825Sdfr if(mfrom == *n) { 429178825Sdfr *n = mfrom->next; 430178825Sdfr break; 431178825Sdfr } 432178825Sdfr } 433178825Sdfr 434178825Sdfr /* swap creds */ 435178825Sdfr creds = mto->creds; 436178825Sdfr mto->creds = mfrom->creds; 437178825Sdfr mfrom->creds = creds; 438178825Sdfr /* swap principal */ 439178825Sdfr principal = mto->primary_principal; 440178825Sdfr mto->primary_principal = mfrom->primary_principal; 441178825Sdfr mfrom->primary_principal = principal; 442178825Sdfr 443233294Sstas mto->mtime = mfrom->mtime = time(NULL); 444233294Sstas 445178825Sdfr HEIMDAL_MUTEX_unlock(&mcc_mutex); 446178825Sdfr mcc_destroy(context, from); 447178825Sdfr 448178825Sdfr return 0; 449178825Sdfr} 450178825Sdfr 451233294Sstasstatic krb5_error_code KRB5_CALLCONV 452178825Sdfrmcc_default_name(krb5_context context, char **str) 453178825Sdfr{ 454178825Sdfr *str = strdup("MEMORY:"); 455178825Sdfr if (*str == NULL) { 456233294Sstas krb5_set_error_message(context, ENOMEM, 457233294Sstas N_("malloc: out of memory", "")); 458178825Sdfr return ENOMEM; 459178825Sdfr } 460178825Sdfr return 0; 461178825Sdfr} 462178825Sdfr 463233294Sstasstatic krb5_error_code KRB5_CALLCONV 464233294Sstasmcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 465233294Sstas{ 466233294Sstas *mtime = MCACHE(id)->mtime; 467233294Sstas return 0; 468233294Sstas} 469178825Sdfr 470233294Sstasstatic krb5_error_code KRB5_CALLCONV 471233294Sstasmcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 472233294Sstas{ 473233294Sstas krb5_mcache *m = MCACHE(id); 474233294Sstas m->kdc_offset = kdc_offset; 475233294Sstas return 0; 476233294Sstas} 477233294Sstas 478233294Sstasstatic krb5_error_code KRB5_CALLCONV 479233294Sstasmcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 480233294Sstas{ 481233294Sstas krb5_mcache *m = MCACHE(id); 482233294Sstas *kdc_offset = m->kdc_offset; 483233294Sstas return 0; 484233294Sstas} 485233294Sstas 486233294Sstas 487178825Sdfr/** 488178825Sdfr * Variable containing the MEMORY based credential cache implemention. 489178825Sdfr * 490178825Sdfr * @ingroup krb5_ccache 491178825Sdfr */ 492178825Sdfr 493233294SstasKRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = { 494233294Sstas KRB5_CC_OPS_VERSION, 49555682Smarkm "MEMORY", 49655682Smarkm mcc_get_name, 49755682Smarkm mcc_resolve, 49855682Smarkm mcc_gen_new, 49955682Smarkm mcc_initialize, 50055682Smarkm mcc_destroy, 50155682Smarkm mcc_close, 50255682Smarkm mcc_store_cred, 50355682Smarkm NULL, /* mcc_retrieve */ 50455682Smarkm mcc_get_principal, 50555682Smarkm mcc_get_first, 50655682Smarkm mcc_get_next, 50755682Smarkm mcc_end_get, 50855682Smarkm mcc_remove_cred, 509178825Sdfr mcc_set_flags, 510178825Sdfr NULL, 511178825Sdfr mcc_get_cache_first, 512178825Sdfr mcc_get_cache_next, 513178825Sdfr mcc_end_cache_get, 514178825Sdfr mcc_move, 515233294Sstas mcc_default_name, 516233294Sstas NULL, 517233294Sstas mcc_lastchange, 518233294Sstas mcc_set_kdc_offset, 519233294Sstas mcc_get_kdc_offset 52055682Smarkm}; 521