155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2008 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_fcache{ 3955682Smarkm char *filename; 4055682Smarkm int version; 4155682Smarkm}krb5_fcache; 4255682Smarkm 4355682Smarkmstruct fcc_cursor { 4455682Smarkm int fd; 4555682Smarkm krb5_storage *sp; 4655682Smarkm}; 4755682Smarkm 4855682Smarkm#define KRB5_FCC_FVNO_1 1 4955682Smarkm#define KRB5_FCC_FVNO_2 2 5055682Smarkm#define KRB5_FCC_FVNO_3 3 5155682Smarkm#define KRB5_FCC_FVNO_4 4 5255682Smarkm 5355682Smarkm#define FCC_TAG_DELTATIME 1 5455682Smarkm 5555682Smarkm#define FCACHE(X) ((krb5_fcache*)(X)->data.data) 5655682Smarkm 5755682Smarkm#define FILENAME(X) (FCACHE(X)->filename) 5855682Smarkm 5955682Smarkm#define FCC_CURSOR(C) ((struct fcc_cursor*)(C)) 6055682Smarkm 61233294Sstasstatic const char* KRB5_CALLCONV 6255682Smarkmfcc_get_name(krb5_context context, 6355682Smarkm krb5_ccache id) 6455682Smarkm{ 65233294Sstas if (FCACHE(id) == NULL) 66233294Sstas return NULL; 67233294Sstas 6855682Smarkm return FILENAME(id); 6955682Smarkm} 7055682Smarkm 71127808Snectarint 72127808Snectar_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive, 73127808Snectar const char *filename) 74127808Snectar{ 75127808Snectar int ret; 76127808Snectar#ifdef HAVE_FCNTL 77127808Snectar struct flock l; 78127808Snectar 79127808Snectar l.l_start = 0; 80127808Snectar l.l_len = 0; 81127808Snectar l.l_type = exclusive ? F_WRLCK : F_RDLCK; 82127808Snectar l.l_whence = SEEK_SET; 83127808Snectar ret = fcntl(fd, F_SETLKW, &l); 84127808Snectar#else 85127808Snectar ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH); 86127808Snectar#endif 87127808Snectar if(ret < 0) 88127808Snectar ret = errno; 89127808Snectar if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */ 90127808Snectar ret = EAGAIN; 91127808Snectar 92127808Snectar switch (ret) { 93127808Snectar case 0: 94127808Snectar break; 95127808Snectar case EINVAL: /* filesystem doesn't support locking, let the user have it */ 96233294Sstas ret = 0; 97127808Snectar break; 98127808Snectar case EAGAIN: 99233294Sstas krb5_set_error_message(context, ret, 100233294Sstas N_("timed out locking cache file %s", "file"), 101233294Sstas filename); 102127808Snectar break; 103233294Sstas default: { 104233294Sstas char buf[128]; 105233294Sstas rk_strerror_r(ret, buf, sizeof(buf)); 106233294Sstas krb5_set_error_message(context, ret, 107233294Sstas N_("error locking cache file %s: %s", 108233294Sstas "file, error"), filename, buf); 109127808Snectar break; 110127808Snectar } 111233294Sstas } 112127808Snectar return ret; 113127808Snectar} 114127808Snectar 115127808Snectarint 116178825Sdfr_krb5_xunlock(krb5_context context, int fd) 117127808Snectar{ 118178825Sdfr int ret; 119178825Sdfr#ifdef HAVE_FCNTL 120127808Snectar struct flock l; 121127808Snectar l.l_start = 0; 122127808Snectar l.l_len = 0; 123127808Snectar l.l_type = F_UNLCK; 124127808Snectar l.l_whence = SEEK_SET; 125178825Sdfr ret = fcntl(fd, F_SETLKW, &l); 126127808Snectar#else 127178825Sdfr ret = flock(fd, LOCK_UN); 128127808Snectar#endif 129178825Sdfr if (ret < 0) 130178825Sdfr ret = errno; 131178825Sdfr switch (ret) { 132178825Sdfr case 0: 133178825Sdfr break; 134178825Sdfr case EINVAL: /* filesystem doesn't support locking, let the user have it */ 135233294Sstas ret = 0; 136178825Sdfr break; 137233294Sstas default: { 138233294Sstas char buf[128]; 139233294Sstas rk_strerror_r(ret, buf, sizeof(buf)); 140233294Sstas krb5_set_error_message(context, ret, 141233294Sstas N_("Failed to unlock file: %s", ""), buf); 142178825Sdfr break; 143178825Sdfr } 144233294Sstas } 145178825Sdfr return ret; 146127808Snectar} 147127808Snectar 14855682Smarkmstatic krb5_error_code 149233294Sstaswrite_storage(krb5_context context, krb5_storage *sp, int fd) 150233294Sstas{ 151233294Sstas krb5_error_code ret; 152233294Sstas krb5_data data; 153233294Sstas ssize_t sret; 154233294Sstas 155233294Sstas ret = krb5_storage_to_data(sp, &data); 156233294Sstas if (ret) { 157233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 158233294Sstas return ret; 159233294Sstas } 160233294Sstas sret = write(fd, data.data, data.length); 161233294Sstas ret = (sret != (ssize_t)data.length); 162233294Sstas krb5_data_free(&data); 163233294Sstas if (ret) { 164233294Sstas ret = errno; 165233294Sstas krb5_set_error_message(context, ret, 166233294Sstas N_("Failed to write FILE credential data", "")); 167233294Sstas return ret; 168233294Sstas } 169233294Sstas return 0; 170233294Sstas} 171233294Sstas 172233294Sstas 173233294Sstasstatic krb5_error_code KRB5_CALLCONV 174127808Snectarfcc_lock(krb5_context context, krb5_ccache id, 175127808Snectar int fd, krb5_boolean exclusive) 176127808Snectar{ 177127808Snectar return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id)); 178127808Snectar} 179127808Snectar 180233294Sstasstatic krb5_error_code KRB5_CALLCONV 181127808Snectarfcc_unlock(krb5_context context, int fd) 182127808Snectar{ 183178825Sdfr return _krb5_xunlock(context, fd); 184127808Snectar} 185127808Snectar 186233294Sstasstatic krb5_error_code KRB5_CALLCONV 18755682Smarkmfcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 18855682Smarkm{ 18955682Smarkm krb5_fcache *f; 19055682Smarkm f = malloc(sizeof(*f)); 19178527Sassar if(f == NULL) { 192233294Sstas krb5_set_error_message(context, KRB5_CC_NOMEM, 193233294Sstas N_("malloc: out of memory", "")); 19455682Smarkm return KRB5_CC_NOMEM; 19578527Sassar } 19655682Smarkm f->filename = strdup(res); 19755682Smarkm if(f->filename == NULL){ 19855682Smarkm free(f); 199233294Sstas krb5_set_error_message(context, KRB5_CC_NOMEM, 200233294Sstas N_("malloc: out of memory", "")); 20155682Smarkm return KRB5_CC_NOMEM; 20255682Smarkm } 20355682Smarkm f->version = 0; 20455682Smarkm (*id)->data.data = f; 20555682Smarkm (*id)->data.length = sizeof(*f); 20655682Smarkm return 0; 20755682Smarkm} 20855682Smarkm 20972445Sassar/* 21072445Sassar * Try to scrub the contents of `filename' safely. 21172445Sassar */ 21272445Sassar 21372445Sassarstatic int 21472445Sassarscrub_file (int fd) 21572445Sassar{ 21672445Sassar off_t pos; 21772445Sassar char buf[128]; 21872445Sassar 21972445Sassar pos = lseek(fd, 0, SEEK_END); 22072445Sassar if (pos < 0) 22172445Sassar return errno; 22272445Sassar if (lseek(fd, 0, SEEK_SET) < 0) 22372445Sassar return errno; 22472445Sassar memset(buf, 0, sizeof(buf)); 22572445Sassar while(pos > 0) { 226233294Sstas ssize_t tmp = write(fd, buf, min((off_t)sizeof(buf), pos)); 22772445Sassar 22872445Sassar if (tmp < 0) 22972445Sassar return errno; 23072445Sassar pos -= tmp; 23172445Sassar } 232233294Sstas#ifdef _MSC_VER 233233294Sstas _commit (fd); 234233294Sstas#else 23572445Sassar fsync (fd); 236233294Sstas#endif 23772445Sassar return 0; 23872445Sassar} 23972445Sassar 24072445Sassar/* 24172445Sassar * Erase `filename' if it exists, trying to remove the contents if 24272445Sassar * it's `safe'. We always try to remove the file, it it exists. It's 24372445Sassar * only overwritten if it's a regular file (not a symlink and not a 24472445Sassar * hardlink) 24572445Sassar */ 24672445Sassar 247233294Sstaskrb5_error_code 248233294Sstas_krb5_erase_file(krb5_context context, const char *filename) 24955682Smarkm{ 25055682Smarkm int fd; 25172445Sassar struct stat sb1, sb2; 25272445Sassar int ret; 25355682Smarkm 25472445Sassar ret = lstat (filename, &sb1); 25572445Sassar if (ret < 0) 25672445Sassar return errno; 25772445Sassar 25855682Smarkm fd = open(filename, O_RDWR | O_BINARY); 25972445Sassar if(fd < 0) { 26055682Smarkm if(errno == ENOENT) 26155682Smarkm return 0; 26255682Smarkm else 26355682Smarkm return errno; 26455682Smarkm } 265233294Sstas rk_cloexec(fd); 266233294Sstas ret = _krb5_xlock(context, fd, 1, filename); 267233294Sstas if (ret) { 268233294Sstas close(fd); 269233294Sstas return ret; 270233294Sstas } 27172445Sassar if (unlink(filename) < 0) { 272233294Sstas _krb5_xunlock(context, fd); 27372445Sassar close (fd); 27472445Sassar return errno; 27572445Sassar } 27672445Sassar ret = fstat (fd, &sb2); 27772445Sassar if (ret < 0) { 278233294Sstas _krb5_xunlock(context, fd); 27972445Sassar close (fd); 28072445Sassar return errno; 28172445Sassar } 28272445Sassar 28372445Sassar /* check if someone was playing with symlinks */ 28472445Sassar 28572445Sassar if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 286233294Sstas _krb5_xunlock(context, fd); 28772445Sassar close (fd); 28872445Sassar return EPERM; 28972445Sassar } 29072445Sassar 29172445Sassar /* there are still hard links to this file */ 29272445Sassar 29372445Sassar if (sb2.st_nlink != 0) { 294233294Sstas _krb5_xunlock(context, fd); 29572445Sassar close (fd); 29672445Sassar return 0; 29772445Sassar } 29872445Sassar 29972445Sassar ret = scrub_file (fd); 300233294Sstas if (ret) { 301233294Sstas _krb5_xunlock(context, fd); 302233294Sstas close(fd); 303233294Sstas return ret; 304233294Sstas } 305233294Sstas ret = _krb5_xunlock(context, fd); 30672445Sassar close (fd); 30772445Sassar return ret; 30855682Smarkm} 30955682Smarkm 310233294Sstasstatic krb5_error_code KRB5_CALLCONV 31155682Smarkmfcc_gen_new(krb5_context context, krb5_ccache *id) 31255682Smarkm{ 313233294Sstas char *file = NULL, *exp_file = NULL; 314233294Sstas krb5_error_code ret; 31555682Smarkm krb5_fcache *f; 31655682Smarkm int fd; 31778527Sassar 31855682Smarkm f = malloc(sizeof(*f)); 31978527Sassar if(f == NULL) { 320233294Sstas krb5_set_error_message(context, KRB5_CC_NOMEM, 321233294Sstas N_("malloc: out of memory", "")); 32255682Smarkm return KRB5_CC_NOMEM; 32378527Sassar } 324233294Sstas ret = asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 325233294Sstas if(ret < 0 || file == NULL) { 32655682Smarkm free(f); 327233294Sstas krb5_set_error_message(context, KRB5_CC_NOMEM, 328233294Sstas N_("malloc: out of memory", "")); 32955682Smarkm return KRB5_CC_NOMEM; 33055682Smarkm } 331233294Sstas ret = _krb5_expand_path_tokens(context, file, &exp_file); 332233294Sstas free(file); 333233294Sstas if (ret) 334233294Sstas return ret; 335233294Sstas 336233294Sstas file = exp_file; 337233294Sstas 338233294Sstas fd = mkstemp(exp_file); 33955682Smarkm if(fd < 0) { 340233294Sstas int xret = errno; 341233294Sstas krb5_set_error_message(context, xret, N_("mkstemp %s failed", ""), exp_file); 34255682Smarkm free(f); 343233294Sstas free(exp_file); 344233294Sstas return xret; 34555682Smarkm } 34655682Smarkm close(fd); 347233294Sstas f->filename = exp_file; 34855682Smarkm f->version = 0; 34955682Smarkm (*id)->data.data = f; 35055682Smarkm (*id)->data.length = sizeof(*f); 35155682Smarkm return 0; 35255682Smarkm} 35355682Smarkm 35455682Smarkmstatic void 35555682Smarkmstorage_set_flags(krb5_context context, krb5_storage *sp, int vno) 35655682Smarkm{ 35755682Smarkm int flags = 0; 35855682Smarkm switch(vno) { 35955682Smarkm case KRB5_FCC_FVNO_1: 36055682Smarkm flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 36155682Smarkm flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 36255682Smarkm flags |= KRB5_STORAGE_HOST_BYTEORDER; 36355682Smarkm break; 36455682Smarkm case KRB5_FCC_FVNO_2: 36555682Smarkm flags |= KRB5_STORAGE_HOST_BYTEORDER; 36655682Smarkm break; 36755682Smarkm case KRB5_FCC_FVNO_3: 36855682Smarkm flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 36955682Smarkm break; 37055682Smarkm case KRB5_FCC_FVNO_4: 37155682Smarkm break; 37255682Smarkm default: 373233294Sstas krb5_abortx(context, 37455682Smarkm "storage_set_flags called with bad vno (%x)", vno); 37555682Smarkm } 37655682Smarkm krb5_storage_set_flags(sp, flags); 37755682Smarkm} 37855682Smarkm 379233294Sstasstatic krb5_error_code KRB5_CALLCONV 380127808Snectarfcc_open(krb5_context context, 381127808Snectar krb5_ccache id, 382127808Snectar int *fd_ret, 383127808Snectar int flags, 384127808Snectar mode_t mode) 385127808Snectar{ 386127808Snectar krb5_boolean exclusive = ((flags | O_WRONLY) == flags || 387127808Snectar (flags | O_RDWR) == flags); 388127808Snectar krb5_error_code ret; 389233294Sstas const char *filename; 390127808Snectar int fd; 391233294Sstas 392233294Sstas if (FCACHE(id) == NULL) 393233294Sstas return krb5_einval(context, 2); 394233294Sstas 395233294Sstas filename = FILENAME(id); 396233294Sstas 397127808Snectar fd = open(filename, flags, mode); 398127808Snectar if(fd < 0) { 399233294Sstas char buf[128]; 400127808Snectar ret = errno; 401233294Sstas rk_strerror_r(ret, buf, sizeof(buf)); 402233294Sstas krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"), 403233294Sstas filename, buf); 404127808Snectar return ret; 405127808Snectar } 406233294Sstas rk_cloexec(fd); 407233294Sstas 408127808Snectar if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { 409127808Snectar close(fd); 410127808Snectar return ret; 411127808Snectar } 412127808Snectar *fd_ret = fd; 413127808Snectar return 0; 414127808Snectar} 415127808Snectar 416233294Sstasstatic krb5_error_code KRB5_CALLCONV 41755682Smarkmfcc_initialize(krb5_context context, 41855682Smarkm krb5_ccache id, 41955682Smarkm krb5_principal primary_principal) 42055682Smarkm{ 42155682Smarkm krb5_fcache *f = FCACHE(id); 42272445Sassar int ret = 0; 42355682Smarkm int fd; 42455682Smarkm 425233294Sstas if (f == NULL) 426233294Sstas return krb5_einval(context, 2); 427233294Sstas 428233294Sstas unlink (f->filename); 429233294Sstas 430233294Sstas ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 431127808Snectar if(ret) 43278527Sassar return ret; 43355682Smarkm { 434233294Sstas krb5_storage *sp; 435233294Sstas sp = krb5_storage_emem(); 436102644Snectar krb5_storage_set_eof_code(sp, KRB5_CC_END); 43755682Smarkm if(context->fcache_vno != 0) 43855682Smarkm f->version = context->fcache_vno; 43955682Smarkm else 44055682Smarkm f->version = KRB5_FCC_FVNO_4; 44172445Sassar ret |= krb5_store_int8(sp, 5); 44272445Sassar ret |= krb5_store_int8(sp, f->version); 44355682Smarkm storage_set_flags(context, sp, f->version); 44472445Sassar if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 44555682Smarkm /* V4 stuff */ 44655682Smarkm if (context->kdc_sec_offset) { 44772445Sassar ret |= krb5_store_int16 (sp, 12); /* length */ 44872445Sassar ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 44972445Sassar ret |= krb5_store_int16 (sp, 8); /* length of data */ 45072445Sassar ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 45172445Sassar ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 45255682Smarkm } else { 45372445Sassar ret |= krb5_store_int16 (sp, 0); 45455682Smarkm } 45555682Smarkm } 45672445Sassar ret |= krb5_store_principal(sp, primary_principal); 457233294Sstas 458233294Sstas ret |= write_storage(context, sp, fd); 459233294Sstas 46055682Smarkm krb5_storage_free(sp); 46155682Smarkm } 462127808Snectar fcc_unlock(context, fd); 463127808Snectar if (close(fd) < 0) 46478527Sassar if (ret == 0) { 465233294Sstas char buf[128]; 46672445Sassar ret = errno; 467233294Sstas rk_strerror_r(ret, buf, sizeof(buf)); 468233294Sstas krb5_set_error_message (context, ret, N_("close %s: %s", ""), 469233294Sstas FILENAME(id), buf); 47078527Sassar } 47172445Sassar return ret; 47255682Smarkm} 47355682Smarkm 474233294Sstasstatic krb5_error_code KRB5_CALLCONV 47555682Smarkmfcc_close(krb5_context context, 47655682Smarkm krb5_ccache id) 47755682Smarkm{ 478233294Sstas if (FCACHE(id) == NULL) 479233294Sstas return krb5_einval(context, 2); 480233294Sstas 48155682Smarkm free (FILENAME(id)); 48255682Smarkm krb5_data_free(&id->data); 48355682Smarkm return 0; 48455682Smarkm} 48555682Smarkm 486233294Sstasstatic krb5_error_code KRB5_CALLCONV 48755682Smarkmfcc_destroy(krb5_context context, 48855682Smarkm krb5_ccache id) 48955682Smarkm{ 490233294Sstas if (FCACHE(id) == NULL) 491233294Sstas return krb5_einval(context, 2); 492233294Sstas 493233294Sstas _krb5_erase_file(context, FILENAME(id)); 49455682Smarkm return 0; 49555682Smarkm} 49655682Smarkm 497233294Sstasstatic krb5_error_code KRB5_CALLCONV 49855682Smarkmfcc_store_cred(krb5_context context, 49955682Smarkm krb5_ccache id, 50055682Smarkm krb5_creds *creds) 50155682Smarkm{ 50272445Sassar int ret; 50355682Smarkm int fd; 50455682Smarkm 505233294Sstas ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0); 506127808Snectar if(ret) 50778527Sassar return ret; 50855682Smarkm { 50955682Smarkm krb5_storage *sp; 510233294Sstas 511233294Sstas sp = krb5_storage_emem(); 512102644Snectar krb5_storage_set_eof_code(sp, KRB5_CC_END); 51355682Smarkm storage_set_flags(context, sp, FCACHE(id)->version); 514178825Sdfr if (!krb5_config_get_bool_default(context, NULL, TRUE, 515178825Sdfr "libdefaults", 516178825Sdfr "fcc-mit-ticketflags", 517178825Sdfr NULL)) 518178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER); 519178825Sdfr ret = krb5_store_creds(sp, creds); 520233294Sstas if (ret == 0) 521233294Sstas ret = write_storage(context, sp, fd); 52255682Smarkm krb5_storage_free(sp); 52355682Smarkm } 524127808Snectar fcc_unlock(context, fd); 525233294Sstas if (close(fd) < 0) { 52678527Sassar if (ret == 0) { 527233294Sstas char buf[128]; 528233294Sstas rk_strerror_r(ret, buf, sizeof(buf)); 52972445Sassar ret = errno; 530233294Sstas krb5_set_error_message (context, ret, N_("close %s: %s", ""), 531233294Sstas FILENAME(id), buf); 53278527Sassar } 533233294Sstas } 53472445Sassar return ret; 53555682Smarkm} 53655682Smarkm 53755682Smarkmstatic krb5_error_code 53855682Smarkminit_fcc (krb5_context context, 539127808Snectar krb5_ccache id, 54055682Smarkm krb5_storage **ret_sp, 541233294Sstas int *ret_fd, 542233294Sstas krb5_deltat *kdc_offset) 54355682Smarkm{ 54455682Smarkm int fd; 54555682Smarkm int8_t pvno, tag; 54655682Smarkm krb5_storage *sp; 54772445Sassar krb5_error_code ret; 54855682Smarkm 549233294Sstas if (kdc_offset) 550233294Sstas *kdc_offset = 0; 551233294Sstas 552233294Sstas ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 553127808Snectar if(ret) 55478527Sassar return ret; 555233294Sstas 556127808Snectar sp = krb5_storage_from_fd(fd); 557127808Snectar if(sp == NULL) { 558233294Sstas krb5_clear_error_message(context); 559127808Snectar ret = ENOMEM; 560127808Snectar goto out; 56178527Sassar } 562102644Snectar krb5_storage_set_eof_code(sp, KRB5_CC_END); 56372445Sassar ret = krb5_ret_int8(sp, &pvno); 564127808Snectar if(ret != 0) { 565178825Sdfr if(ret == KRB5_CC_END) { 566178825Sdfr ret = ENOENT; 567233294Sstas krb5_set_error_message(context, ret, 568233294Sstas N_("Empty credential cache file: %s", ""), 569233294Sstas FILENAME(id)); 570178825Sdfr } else 571233294Sstas krb5_set_error_message(context, ret, N_("Error reading pvno " 572233294Sstas "in cache file: %s", ""), 573233294Sstas FILENAME(id)); 574127808Snectar goto out; 575127808Snectar } 57655682Smarkm if(pvno != 5) { 577127808Snectar ret = KRB5_CCACHE_BADVNO; 578233294Sstas krb5_set_error_message(context, ret, N_("Bad version number in credential " 579233294Sstas "cache file: %s", ""), 580233294Sstas FILENAME(id)); 581127808Snectar goto out; 58255682Smarkm } 583127808Snectar ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ 584127808Snectar if(ret != 0) { 585233294Sstas ret = KRB5_CC_FORMAT; 586233294Sstas krb5_set_error_message(context, ret, "Error reading tag in " 587178825Sdfr "cache file: %s", FILENAME(id)); 588127808Snectar goto out; 589127808Snectar } 590127808Snectar FCACHE(id)->version = tag; 591127808Snectar storage_set_flags(context, sp, FCACHE(id)->version); 59255682Smarkm switch (tag) { 59355682Smarkm case KRB5_FCC_FVNO_4: { 59455682Smarkm int16_t length; 59555682Smarkm 596127808Snectar ret = krb5_ret_int16 (sp, &length); 597127808Snectar if(ret) { 598127808Snectar ret = KRB5_CC_FORMAT; 599233294Sstas krb5_set_error_message(context, ret, 600233294Sstas N_("Error reading tag length in " 601233294Sstas "cache file: %s", ""), FILENAME(id)); 602127808Snectar goto out; 603127808Snectar } 60455682Smarkm while(length > 0) { 605178825Sdfr int16_t dtag, data_len; 60655682Smarkm int i; 60755682Smarkm int8_t dummy; 60855682Smarkm 609178825Sdfr ret = krb5_ret_int16 (sp, &dtag); 610127808Snectar if(ret) { 611127808Snectar ret = KRB5_CC_FORMAT; 612233294Sstas krb5_set_error_message(context, ret, N_("Error reading dtag in " 613233294Sstas "cache file: %s", ""), 614233294Sstas FILENAME(id)); 615127808Snectar goto out; 616127808Snectar } 617127808Snectar ret = krb5_ret_int16 (sp, &data_len); 618127808Snectar if(ret) { 619127808Snectar ret = KRB5_CC_FORMAT; 620233294Sstas krb5_set_error_message(context, ret, 621233294Sstas N_("Error reading dlength " 622233294Sstas "in cache file: %s",""), 623233294Sstas FILENAME(id)); 624127808Snectar goto out; 625127808Snectar } 626178825Sdfr switch (dtag) { 627233294Sstas case FCC_TAG_DELTATIME : { 628233294Sstas int32_t offset; 629233294Sstas 630233294Sstas ret = krb5_ret_int32 (sp, &offset); 631233294Sstas ret |= krb5_ret_int32 (sp, &context->kdc_usec_offset); 632127808Snectar if(ret) { 633127808Snectar ret = KRB5_CC_FORMAT; 634233294Sstas krb5_set_error_message(context, ret, 635233294Sstas N_("Error reading kdc_sec in " 636233294Sstas "cache file: %s", ""), 637233294Sstas FILENAME(id)); 638127808Snectar goto out; 639127808Snectar } 640233294Sstas context->kdc_sec_offset = offset; 641233294Sstas if (kdc_offset) 642233294Sstas *kdc_offset = offset; 64355682Smarkm break; 644233294Sstas } 64555682Smarkm default : 646127808Snectar for (i = 0; i < data_len; ++i) { 647127808Snectar ret = krb5_ret_int8 (sp, &dummy); 648127808Snectar if(ret) { 649127808Snectar ret = KRB5_CC_FORMAT; 650233294Sstas krb5_set_error_message(context, ret, 651233294Sstas N_("Error reading unknown " 652233294Sstas "tag in cache file: %s", ""), 653233294Sstas FILENAME(id)); 654127808Snectar goto out; 655127808Snectar } 656127808Snectar } 65755682Smarkm break; 65855682Smarkm } 65955682Smarkm length -= 4 + data_len; 66055682Smarkm } 66155682Smarkm break; 66255682Smarkm } 66355682Smarkm case KRB5_FCC_FVNO_3: 66455682Smarkm case KRB5_FCC_FVNO_2: 66555682Smarkm case KRB5_FCC_FVNO_1: 66655682Smarkm break; 66755682Smarkm default : 668127808Snectar ret = KRB5_CCACHE_BADVNO; 669233294Sstas krb5_set_error_message(context, ret, 670233294Sstas N_("Unknown version number (%d) in " 671233294Sstas "credential cache file: %s", ""), 672233294Sstas (int)tag, FILENAME(id)); 673127808Snectar goto out; 67455682Smarkm } 67555682Smarkm *ret_sp = sp; 67655682Smarkm *ret_fd = fd; 677233294Sstas 67855682Smarkm return 0; 679127808Snectar out: 680127808Snectar if(sp != NULL) 681127808Snectar krb5_storage_free(sp); 682127808Snectar fcc_unlock(context, fd); 683127808Snectar close(fd); 684127808Snectar return ret; 68555682Smarkm} 68655682Smarkm 687233294Sstasstatic krb5_error_code KRB5_CALLCONV 68855682Smarkmfcc_get_principal(krb5_context context, 68955682Smarkm krb5_ccache id, 69055682Smarkm krb5_principal *principal) 69155682Smarkm{ 69255682Smarkm krb5_error_code ret; 69355682Smarkm int fd; 69455682Smarkm krb5_storage *sp; 69555682Smarkm 696233294Sstas ret = init_fcc (context, id, &sp, &fd, NULL); 69755682Smarkm if (ret) 69855682Smarkm return ret; 69972445Sassar ret = krb5_ret_principal(sp, principal); 700178825Sdfr if (ret) 701233294Sstas krb5_clear_error_message(context); 70255682Smarkm krb5_storage_free(sp); 703127808Snectar fcc_unlock(context, fd); 70455682Smarkm close(fd); 70572445Sassar return ret; 70655682Smarkm} 70755682Smarkm 708233294Sstasstatic krb5_error_code KRB5_CALLCONV 709127808Snectarfcc_end_get (krb5_context context, 710127808Snectar krb5_ccache id, 711127808Snectar krb5_cc_cursor *cursor); 712127808Snectar 713233294Sstasstatic krb5_error_code KRB5_CALLCONV 71455682Smarkmfcc_get_first (krb5_context context, 71555682Smarkm krb5_ccache id, 71655682Smarkm krb5_cc_cursor *cursor) 71755682Smarkm{ 71855682Smarkm krb5_error_code ret; 71955682Smarkm krb5_principal principal; 72055682Smarkm 721233294Sstas if (FCACHE(id) == NULL) 722233294Sstas return krb5_einval(context, 2); 723233294Sstas 72455682Smarkm *cursor = malloc(sizeof(struct fcc_cursor)); 725178825Sdfr if (*cursor == NULL) { 726233294Sstas krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 727178825Sdfr return ENOMEM; 728178825Sdfr } 729178825Sdfr memset(*cursor, 0, sizeof(struct fcc_cursor)); 73055682Smarkm 731233294Sstas ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp, 732233294Sstas &FCC_CURSOR(*cursor)->fd, NULL); 733127808Snectar if (ret) { 734127808Snectar free(*cursor); 735178825Sdfr *cursor = NULL; 73655682Smarkm return ret; 737127808Snectar } 738127808Snectar ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 739127808Snectar if(ret) { 740233294Sstas krb5_clear_error_message(context); 741127808Snectar fcc_end_get(context, id, cursor); 742127808Snectar return ret; 743127808Snectar } 74455682Smarkm krb5_free_principal (context, principal); 745127808Snectar fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 74655682Smarkm return 0; 74755682Smarkm} 74855682Smarkm 749233294Sstasstatic krb5_error_code KRB5_CALLCONV 75055682Smarkmfcc_get_next (krb5_context context, 75155682Smarkm krb5_ccache id, 75255682Smarkm krb5_cc_cursor *cursor, 75355682Smarkm krb5_creds *creds) 75455682Smarkm{ 755127808Snectar krb5_error_code ret; 756233294Sstas 757233294Sstas if (FCACHE(id) == NULL) 758233294Sstas return krb5_einval(context, 2); 759233294Sstas 760233294Sstas if (FCC_CURSOR(*cursor) == NULL) 761233294Sstas return krb5_einval(context, 3); 762233294Sstas 763127808Snectar if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0) 764127808Snectar return ret; 765127808Snectar 766127808Snectar ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); 767178825Sdfr if (ret) 768233294Sstas krb5_clear_error_message(context); 769127808Snectar 770127808Snectar fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 771127808Snectar return ret; 77255682Smarkm} 77355682Smarkm 774233294Sstasstatic krb5_error_code KRB5_CALLCONV 77555682Smarkmfcc_end_get (krb5_context context, 77655682Smarkm krb5_ccache id, 77755682Smarkm krb5_cc_cursor *cursor) 77855682Smarkm{ 779233294Sstas 780233294Sstas if (FCACHE(id) == NULL) 781233294Sstas return krb5_einval(context, 2); 782233294Sstas 783233294Sstas if (FCC_CURSOR(*cursor) == NULL) 784233294Sstas return krb5_einval(context, 3); 785233294Sstas 78655682Smarkm krb5_storage_free(FCC_CURSOR(*cursor)->sp); 78755682Smarkm close (FCC_CURSOR(*cursor)->fd); 78855682Smarkm free(*cursor); 789127808Snectar *cursor = NULL; 79055682Smarkm return 0; 79155682Smarkm} 79255682Smarkm 793233294Sstasstatic krb5_error_code KRB5_CALLCONV 79455682Smarkmfcc_remove_cred(krb5_context context, 79555682Smarkm krb5_ccache id, 79655682Smarkm krb5_flags which, 79755682Smarkm krb5_creds *cred) 79855682Smarkm{ 799178825Sdfr krb5_error_code ret; 800233294Sstas krb5_ccache copy, newfile; 801233294Sstas char *newname = NULL; 802233294Sstas int fd; 803178825Sdfr 804233294Sstas if (FCACHE(id) == NULL) 805233294Sstas return krb5_einval(context, 2); 806233294Sstas 807233294Sstas ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, ©); 808178825Sdfr if (ret) 809178825Sdfr return ret; 810178825Sdfr 811178825Sdfr ret = krb5_cc_copy_cache(context, id, copy); 812178825Sdfr if (ret) { 813178825Sdfr krb5_cc_destroy(context, copy); 814178825Sdfr return ret; 815178825Sdfr } 816178825Sdfr 817178825Sdfr ret = krb5_cc_remove_cred(context, copy, which, cred); 818178825Sdfr if (ret) { 819178825Sdfr krb5_cc_destroy(context, copy); 820178825Sdfr return ret; 821178825Sdfr } 822178825Sdfr 823233294Sstas ret = asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id)); 824233294Sstas if (ret < 0 || newname == NULL) { 825233294Sstas krb5_cc_destroy(context, copy); 826233294Sstas return ENOMEM; 827233294Sstas } 828178825Sdfr 829233294Sstas fd = mkstemp(&newname[5]); 830233294Sstas if (fd < 0) { 831233294Sstas ret = errno; 832233294Sstas krb5_cc_destroy(context, copy); 833233294Sstas return ret; 834233294Sstas } 835233294Sstas close(fd); 836233294Sstas 837233294Sstas ret = krb5_cc_resolve(context, newname, &newfile); 838233294Sstas if (ret) { 839233294Sstas unlink(&newname[5]); 840233294Sstas free(newname); 841233294Sstas krb5_cc_destroy(context, copy); 842233294Sstas return ret; 843233294Sstas } 844233294Sstas 845233294Sstas ret = krb5_cc_copy_cache(context, copy, newfile); 846178825Sdfr krb5_cc_destroy(context, copy); 847233294Sstas if (ret) { 848233294Sstas free(newname); 849233294Sstas krb5_cc_destroy(context, newfile); 850233294Sstas return ret; 851233294Sstas } 852178825Sdfr 853233294Sstas ret = rk_rename(&newname[5], FILENAME(id)); 854233294Sstas if (ret) 855233294Sstas ret = errno; 856233294Sstas free(newname); 857233294Sstas krb5_cc_close(context, newfile); 858233294Sstas 859178825Sdfr return ret; 86055682Smarkm} 86155682Smarkm 862233294Sstasstatic krb5_error_code KRB5_CALLCONV 86355682Smarkmfcc_set_flags(krb5_context context, 86455682Smarkm krb5_ccache id, 86555682Smarkm krb5_flags flags) 86655682Smarkm{ 867233294Sstas if (FCACHE(id) == NULL) 868233294Sstas return krb5_einval(context, 2); 869233294Sstas 87055682Smarkm return 0; /* XXX */ 87155682Smarkm} 87255682Smarkm 873233294Sstasstatic int KRB5_CALLCONV 87455682Smarkmfcc_get_version(krb5_context context, 87555682Smarkm krb5_ccache id) 87655682Smarkm{ 877233294Sstas if (FCACHE(id) == NULL) 878233294Sstas return -1; 879233294Sstas 88055682Smarkm return FCACHE(id)->version; 88155682Smarkm} 882233294Sstas 883178825Sdfrstruct fcache_iter { 884178825Sdfr int first; 885178825Sdfr}; 886178825Sdfr 887233294Sstasstatic krb5_error_code KRB5_CALLCONV 888178825Sdfrfcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 889178825Sdfr{ 890178825Sdfr struct fcache_iter *iter; 891178825Sdfr 892178825Sdfr iter = calloc(1, sizeof(*iter)); 893178825Sdfr if (iter == NULL) { 894233294Sstas krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 895178825Sdfr return ENOMEM; 896233294Sstas } 897178825Sdfr iter->first = 1; 898178825Sdfr *cursor = iter; 899178825Sdfr return 0; 900178825Sdfr} 901178825Sdfr 902233294Sstasstatic krb5_error_code KRB5_CALLCONV 903178825Sdfrfcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 904178825Sdfr{ 905178825Sdfr struct fcache_iter *iter = cursor; 906178825Sdfr krb5_error_code ret; 907178825Sdfr const char *fn; 908178825Sdfr char *expandedfn = NULL; 909178825Sdfr 910233294Sstas if (iter == NULL) 911233294Sstas return krb5_einval(context, 2); 912233294Sstas 913178825Sdfr if (!iter->first) { 914233294Sstas krb5_clear_error_message(context); 915178825Sdfr return KRB5_CC_END; 916178825Sdfr } 917178825Sdfr iter->first = 0; 918178825Sdfr 919178825Sdfr fn = krb5_cc_default_name(context); 920233294Sstas if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) { 921233294Sstas ret = _krb5_expand_default_cc_name(context, 922178825Sdfr KRB5_DEFAULT_CCNAME_FILE, 923178825Sdfr &expandedfn); 924178825Sdfr if (ret) 925178825Sdfr return ret; 926233294Sstas fn = expandedfn; 927178825Sdfr } 928233294Sstas /* check if file exists, don't return a non existant "next" */ 929233294Sstas if (strncasecmp(fn, "FILE:", 5) == 0) { 930233294Sstas struct stat sb; 931233294Sstas ret = stat(fn + 5, &sb); 932233294Sstas if (ret) { 933233294Sstas ret = KRB5_CC_END; 934233294Sstas goto out; 935233294Sstas } 936233294Sstas } 937178825Sdfr ret = krb5_cc_resolve(context, fn, id); 938233294Sstas out: 939178825Sdfr if (expandedfn) 940178825Sdfr free(expandedfn); 941233294Sstas 942178825Sdfr return ret; 943178825Sdfr} 944178825Sdfr 945233294Sstasstatic krb5_error_code KRB5_CALLCONV 946178825Sdfrfcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 947178825Sdfr{ 948178825Sdfr struct fcache_iter *iter = cursor; 949233294Sstas 950233294Sstas if (iter == NULL) 951233294Sstas return krb5_einval(context, 2); 952233294Sstas 953178825Sdfr free(iter); 954178825Sdfr return 0; 955178825Sdfr} 956178825Sdfr 957233294Sstasstatic krb5_error_code KRB5_CALLCONV 958178825Sdfrfcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 959178825Sdfr{ 960178825Sdfr krb5_error_code ret = 0; 961178825Sdfr 962233294Sstas ret = rk_rename(FILENAME(from), FILENAME(to)); 963233294Sstas 964178825Sdfr if (ret && errno != EXDEV) { 965233294Sstas char buf[128]; 966178825Sdfr ret = errno; 967233294Sstas rk_strerror_r(ret, buf, sizeof(buf)); 968233294Sstas krb5_set_error_message(context, ret, 969233294Sstas N_("Rename of file from %s " 970233294Sstas "to %s failed: %s", ""), 971233294Sstas FILENAME(from), FILENAME(to), buf); 972178825Sdfr return ret; 973178825Sdfr } else if (ret && errno == EXDEV) { 974178825Sdfr /* make a copy and delete the orignal */ 975178825Sdfr krb5_ssize_t sz1, sz2; 976178825Sdfr int fd1, fd2; 977178825Sdfr char buf[BUFSIZ]; 978178825Sdfr 979233294Sstas ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 980178825Sdfr if(ret) 981178825Sdfr return ret; 982178825Sdfr 983178825Sdfr unlink(FILENAME(to)); 984178825Sdfr 985233294Sstas ret = fcc_open(context, to, &fd2, 986233294Sstas O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 987178825Sdfr if(ret) 988178825Sdfr goto out1; 989178825Sdfr 990178825Sdfr while((sz1 = read(fd1, buf, sizeof(buf))) > 0) { 991178825Sdfr sz2 = write(fd2, buf, sz1); 992178825Sdfr if (sz1 != sz2) { 993178825Sdfr ret = EIO; 994233294Sstas krb5_set_error_message(context, ret, 995233294Sstas N_("Failed to write data from one file " 996233294Sstas "credential cache to the other", "")); 997178825Sdfr goto out2; 998178825Sdfr } 999178825Sdfr } 1000178825Sdfr if (sz1 < 0) { 1001178825Sdfr ret = EIO; 1002233294Sstas krb5_set_error_message(context, ret, 1003233294Sstas N_("Failed to read data from one file " 1004233294Sstas "credential cache to the other", "")); 1005178825Sdfr goto out2; 1006178825Sdfr } 1007178825Sdfr out2: 1008178825Sdfr fcc_unlock(context, fd2); 1009178825Sdfr close(fd2); 1010178825Sdfr 1011178825Sdfr out1: 1012178825Sdfr fcc_unlock(context, fd1); 1013178825Sdfr close(fd1); 1014178825Sdfr 1015233294Sstas _krb5_erase_file(context, FILENAME(from)); 1016233294Sstas 1017178825Sdfr if (ret) { 1018233294Sstas _krb5_erase_file(context, FILENAME(to)); 1019178825Sdfr return ret; 1020178825Sdfr } 1021178825Sdfr } 1022178825Sdfr 1023178825Sdfr /* make sure ->version is uptodate */ 1024178825Sdfr { 1025178825Sdfr krb5_storage *sp; 1026178825Sdfr int fd; 1027233294Sstas if ((ret = init_fcc (context, to, &sp, &fd, NULL)) == 0) { 1028233294Sstas if (sp) 1029233294Sstas krb5_storage_free(sp); 1030233294Sstas fcc_unlock(context, fd); 1031233294Sstas close(fd); 1032233294Sstas } 1033233294Sstas } 1034233294Sstas 1035233294Sstas fcc_close(context, from); 1036233294Sstas 1037178825Sdfr return ret; 1038178825Sdfr} 1039178825Sdfr 1040233294Sstasstatic krb5_error_code KRB5_CALLCONV 1041233294Sstasfcc_get_default_name(krb5_context context, char **str) 1042178825Sdfr{ 1043233294Sstas return _krb5_expand_default_cc_name(context, 1044178825Sdfr KRB5_DEFAULT_CCNAME_FILE, 1045178825Sdfr str); 1046178825Sdfr} 1047178825Sdfr 1048233294Sstasstatic krb5_error_code KRB5_CALLCONV 1049233294Sstasfcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1050233294Sstas{ 1051233294Sstas krb5_error_code ret; 1052233294Sstas struct stat sb; 1053233294Sstas int fd; 1054233294Sstas 1055233294Sstas ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 1056233294Sstas if(ret) 1057233294Sstas return ret; 1058233294Sstas ret = fstat(fd, &sb); 1059233294Sstas close(fd); 1060233294Sstas if (ret) { 1061233294Sstas ret = errno; 1062233294Sstas krb5_set_error_message(context, ret, N_("Failed to stat cache file", "")); 1063233294Sstas return ret; 1064233294Sstas } 1065233294Sstas *mtime = sb.st_mtime; 1066233294Sstas return 0; 1067233294Sstas} 1068233294Sstas 1069233294Sstasstatic krb5_error_code KRB5_CALLCONV 1070233294Sstasfcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1071233294Sstas{ 1072233294Sstas return 0; 1073233294Sstas} 1074233294Sstas 1075233294Sstasstatic krb5_error_code KRB5_CALLCONV 1076233294Sstasfcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1077233294Sstas{ 1078233294Sstas krb5_error_code ret; 1079233294Sstas krb5_storage *sp = NULL; 1080233294Sstas int fd; 1081233294Sstas ret = init_fcc(context, id, &sp, &fd, kdc_offset); 1082233294Sstas if (sp) 1083233294Sstas krb5_storage_free(sp); 1084233294Sstas fcc_unlock(context, fd); 1085233294Sstas close(fd); 1086233294Sstas 1087233294Sstas return ret; 1088233294Sstas} 1089233294Sstas 1090233294Sstas 1091178825Sdfr/** 1092178825Sdfr * Variable containing the FILE based credential cache implemention. 1093178825Sdfr * 1094178825Sdfr * @ingroup krb5_ccache 1095178825Sdfr */ 1096178825Sdfr 1097233294SstasKRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = { 1098233294Sstas KRB5_CC_OPS_VERSION, 109955682Smarkm "FILE", 110055682Smarkm fcc_get_name, 110155682Smarkm fcc_resolve, 110255682Smarkm fcc_gen_new, 110355682Smarkm fcc_initialize, 110455682Smarkm fcc_destroy, 110555682Smarkm fcc_close, 110655682Smarkm fcc_store_cred, 110755682Smarkm NULL, /* fcc_retrieve */ 110855682Smarkm fcc_get_principal, 110955682Smarkm fcc_get_first, 111055682Smarkm fcc_get_next, 111155682Smarkm fcc_end_get, 111255682Smarkm fcc_remove_cred, 111355682Smarkm fcc_set_flags, 1114178825Sdfr fcc_get_version, 1115178825Sdfr fcc_get_cache_first, 1116178825Sdfr fcc_get_cache_next, 1117178825Sdfr fcc_end_cache_get, 1118178825Sdfr fcc_move, 1119233294Sstas fcc_get_default_name, 1120233294Sstas NULL, 1121233294Sstas fcc_lastchange, 1122233294Sstas fcc_set_kdc_offset, 1123233294Sstas fcc_get_kdc_offset 112455682Smarkm}; 1125