155682Smarkm/* 2178825Sdfr * Copyright (c) 1997 - 2008 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: fcache.c 22522 2008-01-24 11:56:25Z lha $"); 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 61102644Snectarstatic const char* 6255682Smarkmfcc_get_name(krb5_context context, 6355682Smarkm krb5_ccache id) 6455682Smarkm{ 6555682Smarkm return FILENAME(id); 6655682Smarkm} 6755682Smarkm 68127808Snectarint 69127808Snectar_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive, 70127808Snectar const char *filename) 71127808Snectar{ 72127808Snectar int ret; 73127808Snectar#ifdef HAVE_FCNTL 74127808Snectar struct flock l; 75127808Snectar 76127808Snectar l.l_start = 0; 77127808Snectar l.l_len = 0; 78127808Snectar l.l_type = exclusive ? F_WRLCK : F_RDLCK; 79127808Snectar l.l_whence = SEEK_SET; 80127808Snectar ret = fcntl(fd, F_SETLKW, &l); 81127808Snectar#else 82127808Snectar ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH); 83127808Snectar#endif 84127808Snectar if(ret < 0) 85127808Snectar ret = errno; 86127808Snectar if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */ 87127808Snectar ret = EAGAIN; 88127808Snectar 89127808Snectar switch (ret) { 90127808Snectar case 0: 91127808Snectar break; 92127808Snectar case EINVAL: /* filesystem doesn't support locking, let the user have it */ 93127808Snectar ret = 0; 94127808Snectar break; 95127808Snectar case EAGAIN: 96127808Snectar krb5_set_error_string(context, "timed out locking cache file %s", 97127808Snectar filename); 98127808Snectar break; 99127808Snectar default: 100127808Snectar krb5_set_error_string(context, "error locking cache file %s: %s", 101127808Snectar filename, strerror(ret)); 102127808Snectar break; 103127808Snectar } 104127808Snectar return ret; 105127808Snectar} 106127808Snectar 107127808Snectarint 108178825Sdfr_krb5_xunlock(krb5_context context, int fd) 109127808Snectar{ 110178825Sdfr int ret; 111178825Sdfr#ifdef HAVE_FCNTL 112127808Snectar struct flock l; 113127808Snectar l.l_start = 0; 114127808Snectar l.l_len = 0; 115127808Snectar l.l_type = F_UNLCK; 116127808Snectar l.l_whence = SEEK_SET; 117178825Sdfr ret = fcntl(fd, F_SETLKW, &l); 118127808Snectar#else 119178825Sdfr ret = flock(fd, LOCK_UN); 120127808Snectar#endif 121178825Sdfr if (ret < 0) 122178825Sdfr ret = errno; 123178825Sdfr switch (ret) { 124178825Sdfr case 0: 125178825Sdfr break; 126178825Sdfr case EINVAL: /* filesystem doesn't support locking, let the user have it */ 127178825Sdfr ret = 0; 128178825Sdfr break; 129178825Sdfr default: 130178825Sdfr krb5_set_error_string(context, 131178825Sdfr "Failed to unlock file: %s", strerror(ret)); 132178825Sdfr break; 133178825Sdfr } 134178825Sdfr return ret; 135127808Snectar} 136127808Snectar 13755682Smarkmstatic krb5_error_code 138127808Snectarfcc_lock(krb5_context context, krb5_ccache id, 139127808Snectar int fd, krb5_boolean exclusive) 140127808Snectar{ 141127808Snectar return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id)); 142127808Snectar} 143127808Snectar 144127808Snectarstatic krb5_error_code 145127808Snectarfcc_unlock(krb5_context context, int fd) 146127808Snectar{ 147178825Sdfr return _krb5_xunlock(context, fd); 148127808Snectar} 149127808Snectar 150127808Snectarstatic krb5_error_code 15155682Smarkmfcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 15255682Smarkm{ 15355682Smarkm krb5_fcache *f; 15455682Smarkm f = malloc(sizeof(*f)); 15578527Sassar if(f == NULL) { 15678527Sassar krb5_set_error_string(context, "malloc: out of memory"); 15755682Smarkm return KRB5_CC_NOMEM; 15878527Sassar } 15955682Smarkm f->filename = strdup(res); 16055682Smarkm if(f->filename == NULL){ 16155682Smarkm free(f); 16278527Sassar krb5_set_error_string(context, "malloc: out of memory"); 16355682Smarkm return KRB5_CC_NOMEM; 16455682Smarkm } 16555682Smarkm f->version = 0; 16655682Smarkm (*id)->data.data = f; 16755682Smarkm (*id)->data.length = sizeof(*f); 16855682Smarkm return 0; 16955682Smarkm} 17055682Smarkm 17172445Sassar/* 17272445Sassar * Try to scrub the contents of `filename' safely. 17372445Sassar */ 17472445Sassar 17572445Sassarstatic int 17672445Sassarscrub_file (int fd) 17772445Sassar{ 17872445Sassar off_t pos; 17972445Sassar char buf[128]; 18072445Sassar 18172445Sassar pos = lseek(fd, 0, SEEK_END); 18272445Sassar if (pos < 0) 18372445Sassar return errno; 18472445Sassar if (lseek(fd, 0, SEEK_SET) < 0) 18572445Sassar return errno; 18672445Sassar memset(buf, 0, sizeof(buf)); 18772445Sassar while(pos > 0) { 18872445Sassar ssize_t tmp = write(fd, buf, min(sizeof(buf), pos)); 18972445Sassar 19072445Sassar if (tmp < 0) 19172445Sassar return errno; 19272445Sassar pos -= tmp; 19372445Sassar } 19472445Sassar fsync (fd); 19572445Sassar return 0; 19672445Sassar} 19772445Sassar 19872445Sassar/* 19972445Sassar * Erase `filename' if it exists, trying to remove the contents if 20072445Sassar * it's `safe'. We always try to remove the file, it it exists. It's 20172445Sassar * only overwritten if it's a regular file (not a symlink and not a 20272445Sassar * hardlink) 20372445Sassar */ 20472445Sassar 20555682Smarkmstatic krb5_error_code 20655682Smarkmerase_file(const char *filename) 20755682Smarkm{ 20855682Smarkm int fd; 20972445Sassar struct stat sb1, sb2; 21072445Sassar int ret; 21155682Smarkm 21272445Sassar ret = lstat (filename, &sb1); 21372445Sassar if (ret < 0) 21472445Sassar return errno; 21572445Sassar 21655682Smarkm fd = open(filename, O_RDWR | O_BINARY); 21772445Sassar if(fd < 0) { 21855682Smarkm if(errno == ENOENT) 21955682Smarkm return 0; 22055682Smarkm else 22155682Smarkm return errno; 22255682Smarkm } 22372445Sassar if (unlink(filename) < 0) { 22472445Sassar close (fd); 22572445Sassar return errno; 22672445Sassar } 22772445Sassar ret = fstat (fd, &sb2); 22872445Sassar if (ret < 0) { 22972445Sassar close (fd); 23072445Sassar return errno; 23172445Sassar } 23272445Sassar 23372445Sassar /* check if someone was playing with symlinks */ 23472445Sassar 23572445Sassar if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 23672445Sassar close (fd); 23772445Sassar return EPERM; 23872445Sassar } 23972445Sassar 24072445Sassar /* there are still hard links to this file */ 24172445Sassar 24272445Sassar if (sb2.st_nlink != 0) { 24372445Sassar close (fd); 24472445Sassar return 0; 24572445Sassar } 24672445Sassar 24772445Sassar ret = scrub_file (fd); 24872445Sassar close (fd); 24972445Sassar return ret; 25055682Smarkm} 25155682Smarkm 25255682Smarkmstatic krb5_error_code 25355682Smarkmfcc_gen_new(krb5_context context, krb5_ccache *id) 25455682Smarkm{ 25555682Smarkm krb5_fcache *f; 25655682Smarkm int fd; 25755682Smarkm char *file; 25878527Sassar 25955682Smarkm f = malloc(sizeof(*f)); 26078527Sassar if(f == NULL) { 26178527Sassar krb5_set_error_string(context, "malloc: out of memory"); 26255682Smarkm return KRB5_CC_NOMEM; 26378527Sassar } 26472445Sassar asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 26555682Smarkm if(file == NULL) { 26655682Smarkm free(f); 26778527Sassar krb5_set_error_string(context, "malloc: out of memory"); 26855682Smarkm return KRB5_CC_NOMEM; 26955682Smarkm } 27055682Smarkm fd = mkstemp(file); 27155682Smarkm if(fd < 0) { 272178825Sdfr int ret = errno; 273178825Sdfr krb5_set_error_string(context, "mkstemp %s", file); 27455682Smarkm free(f); 27555682Smarkm free(file); 276178825Sdfr return ret; 27755682Smarkm } 27855682Smarkm close(fd); 27955682Smarkm f->filename = file; 28055682Smarkm f->version = 0; 28155682Smarkm (*id)->data.data = f; 28255682Smarkm (*id)->data.length = sizeof(*f); 28355682Smarkm return 0; 28455682Smarkm} 28555682Smarkm 28655682Smarkmstatic void 28755682Smarkmstorage_set_flags(krb5_context context, krb5_storage *sp, int vno) 28855682Smarkm{ 28955682Smarkm int flags = 0; 29055682Smarkm switch(vno) { 29155682Smarkm case KRB5_FCC_FVNO_1: 29255682Smarkm flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 29355682Smarkm flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 29455682Smarkm flags |= KRB5_STORAGE_HOST_BYTEORDER; 29555682Smarkm break; 29655682Smarkm case KRB5_FCC_FVNO_2: 29755682Smarkm flags |= KRB5_STORAGE_HOST_BYTEORDER; 29855682Smarkm break; 29955682Smarkm case KRB5_FCC_FVNO_3: 30055682Smarkm flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 30155682Smarkm break; 30255682Smarkm case KRB5_FCC_FVNO_4: 30355682Smarkm break; 30455682Smarkm default: 30555682Smarkm krb5_abortx(context, 30655682Smarkm "storage_set_flags called with bad vno (%x)", vno); 30755682Smarkm } 30855682Smarkm krb5_storage_set_flags(sp, flags); 30955682Smarkm} 31055682Smarkm 31155682Smarkmstatic krb5_error_code 312127808Snectarfcc_open(krb5_context context, 313127808Snectar krb5_ccache id, 314127808Snectar int *fd_ret, 315127808Snectar int flags, 316127808Snectar mode_t mode) 317127808Snectar{ 318127808Snectar krb5_boolean exclusive = ((flags | O_WRONLY) == flags || 319127808Snectar (flags | O_RDWR) == flags); 320127808Snectar krb5_error_code ret; 321127808Snectar const char *filename = FILENAME(id); 322127808Snectar int fd; 323127808Snectar fd = open(filename, flags, mode); 324127808Snectar if(fd < 0) { 325127808Snectar ret = errno; 326127808Snectar krb5_set_error_string(context, "open(%s): %s", filename, 327127808Snectar strerror(ret)); 328127808Snectar return ret; 329127808Snectar } 330127808Snectar 331127808Snectar if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { 332127808Snectar close(fd); 333127808Snectar return ret; 334127808Snectar } 335127808Snectar *fd_ret = fd; 336127808Snectar return 0; 337127808Snectar} 338127808Snectar 339127808Snectarstatic krb5_error_code 34055682Smarkmfcc_initialize(krb5_context context, 34155682Smarkm krb5_ccache id, 34255682Smarkm krb5_principal primary_principal) 34355682Smarkm{ 34455682Smarkm krb5_fcache *f = FCACHE(id); 34572445Sassar int ret = 0; 34655682Smarkm int fd; 34755682Smarkm char *filename = f->filename; 34855682Smarkm 34972445Sassar unlink (filename); 35055682Smarkm 351127808Snectar ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); 352127808Snectar if(ret) 35378527Sassar return ret; 35455682Smarkm { 35555682Smarkm krb5_storage *sp; 35655682Smarkm sp = krb5_storage_from_fd(fd); 357102644Snectar krb5_storage_set_eof_code(sp, KRB5_CC_END); 35855682Smarkm if(context->fcache_vno != 0) 35955682Smarkm f->version = context->fcache_vno; 36055682Smarkm else 36155682Smarkm f->version = KRB5_FCC_FVNO_4; 36272445Sassar ret |= krb5_store_int8(sp, 5); 36372445Sassar ret |= krb5_store_int8(sp, f->version); 36455682Smarkm storage_set_flags(context, sp, f->version); 36572445Sassar if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 36655682Smarkm /* V4 stuff */ 36755682Smarkm if (context->kdc_sec_offset) { 36872445Sassar ret |= krb5_store_int16 (sp, 12); /* length */ 36972445Sassar ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 37072445Sassar ret |= krb5_store_int16 (sp, 8); /* length of data */ 37172445Sassar ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 37272445Sassar ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 37355682Smarkm } else { 37472445Sassar ret |= krb5_store_int16 (sp, 0); 37555682Smarkm } 37655682Smarkm } 37772445Sassar ret |= krb5_store_principal(sp, primary_principal); 378127808Snectar 37955682Smarkm krb5_storage_free(sp); 38055682Smarkm } 381127808Snectar fcc_unlock(context, fd); 382127808Snectar if (close(fd) < 0) 38378527Sassar if (ret == 0) { 38472445Sassar ret = errno; 385127808Snectar krb5_set_error_string (context, "close %s: %s", 386127808Snectar FILENAME(id), strerror(ret)); 38778527Sassar } 38872445Sassar return ret; 38955682Smarkm} 39055682Smarkm 39155682Smarkmstatic krb5_error_code 39255682Smarkmfcc_close(krb5_context context, 39355682Smarkm krb5_ccache id) 39455682Smarkm{ 39555682Smarkm free (FILENAME(id)); 39655682Smarkm krb5_data_free(&id->data); 39755682Smarkm return 0; 39855682Smarkm} 39955682Smarkm 40055682Smarkmstatic krb5_error_code 40155682Smarkmfcc_destroy(krb5_context context, 40255682Smarkm krb5_ccache id) 40355682Smarkm{ 404127808Snectar erase_file(FILENAME(id)); 40555682Smarkm return 0; 40655682Smarkm} 40755682Smarkm 40855682Smarkmstatic krb5_error_code 40955682Smarkmfcc_store_cred(krb5_context context, 41055682Smarkm krb5_ccache id, 41155682Smarkm krb5_creds *creds) 41255682Smarkm{ 41372445Sassar int ret; 41455682Smarkm int fd; 41555682Smarkm 416127808Snectar ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY, 0); 417127808Snectar if(ret) 41878527Sassar return ret; 41955682Smarkm { 42055682Smarkm krb5_storage *sp; 42155682Smarkm sp = krb5_storage_from_fd(fd); 422102644Snectar krb5_storage_set_eof_code(sp, KRB5_CC_END); 42355682Smarkm storage_set_flags(context, sp, FCACHE(id)->version); 424178825Sdfr if (!krb5_config_get_bool_default(context, NULL, TRUE, 425178825Sdfr "libdefaults", 426178825Sdfr "fcc-mit-ticketflags", 427178825Sdfr NULL)) 428178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER); 429178825Sdfr ret = krb5_store_creds(sp, creds); 43055682Smarkm krb5_storage_free(sp); 43155682Smarkm } 432127808Snectar fcc_unlock(context, fd); 43372445Sassar if (close(fd) < 0) 43478527Sassar if (ret == 0) { 43572445Sassar ret = errno; 436127808Snectar krb5_set_error_string (context, "close %s: %s", 437127808Snectar FILENAME(id), strerror(ret)); 43878527Sassar } 43972445Sassar return ret; 44055682Smarkm} 44155682Smarkm 44255682Smarkmstatic krb5_error_code 44355682Smarkminit_fcc (krb5_context context, 444127808Snectar krb5_ccache id, 44555682Smarkm krb5_storage **ret_sp, 44655682Smarkm int *ret_fd) 44755682Smarkm{ 44855682Smarkm int fd; 44955682Smarkm int8_t pvno, tag; 45055682Smarkm krb5_storage *sp; 45172445Sassar krb5_error_code ret; 45255682Smarkm 453127808Snectar ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY, 0); 454127808Snectar if(ret) 45578527Sassar return ret; 456127808Snectar 457127808Snectar sp = krb5_storage_from_fd(fd); 458127808Snectar if(sp == NULL) { 459178825Sdfr krb5_clear_error_string(context); 460127808Snectar ret = ENOMEM; 461127808Snectar goto out; 46278527Sassar } 463102644Snectar krb5_storage_set_eof_code(sp, KRB5_CC_END); 46472445Sassar ret = krb5_ret_int8(sp, &pvno); 465127808Snectar if(ret != 0) { 466178825Sdfr if(ret == KRB5_CC_END) { 467178825Sdfr krb5_set_error_string(context, "Empty credential cache file: %s", 468178825Sdfr FILENAME(id)); 469178825Sdfr ret = ENOENT; 470178825Sdfr } else 471178825Sdfr krb5_set_error_string(context, "Error reading pvno in " 472178825Sdfr "cache file: %s", FILENAME(id)); 473127808Snectar goto out; 474127808Snectar } 47555682Smarkm if(pvno != 5) { 476178825Sdfr krb5_set_error_string(context, "Bad version number in credential " 477178825Sdfr "cache file: %s", FILENAME(id)); 478127808Snectar ret = KRB5_CCACHE_BADVNO; 479127808Snectar goto out; 48055682Smarkm } 481127808Snectar ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ 482127808Snectar if(ret != 0) { 483178825Sdfr krb5_set_error_string(context, "Error reading tag in " 484178825Sdfr "cache file: %s", FILENAME(id)); 485127808Snectar ret = KRB5_CC_FORMAT; 486127808Snectar goto out; 487127808Snectar } 488127808Snectar FCACHE(id)->version = tag; 489127808Snectar storage_set_flags(context, sp, FCACHE(id)->version); 49055682Smarkm switch (tag) { 49155682Smarkm case KRB5_FCC_FVNO_4: { 49255682Smarkm int16_t length; 49355682Smarkm 494127808Snectar ret = krb5_ret_int16 (sp, &length); 495127808Snectar if(ret) { 496127808Snectar ret = KRB5_CC_FORMAT; 497178825Sdfr krb5_set_error_string(context, "Error reading tag length in " 498178825Sdfr "cache file: %s", FILENAME(id)); 499127808Snectar goto out; 500127808Snectar } 50155682Smarkm while(length > 0) { 502178825Sdfr int16_t dtag, data_len; 50355682Smarkm int i; 50455682Smarkm int8_t dummy; 50555682Smarkm 506178825Sdfr ret = krb5_ret_int16 (sp, &dtag); 507127808Snectar if(ret) { 508178825Sdfr krb5_set_error_string(context, "Error reading dtag in " 509178825Sdfr "cache file: %s", FILENAME(id)); 510127808Snectar ret = KRB5_CC_FORMAT; 511127808Snectar goto out; 512127808Snectar } 513127808Snectar ret = krb5_ret_int16 (sp, &data_len); 514127808Snectar if(ret) { 515178825Sdfr krb5_set_error_string(context, "Error reading dlength in " 516178825Sdfr "cache file: %s", FILENAME(id)); 517127808Snectar ret = KRB5_CC_FORMAT; 518127808Snectar goto out; 519127808Snectar } 520178825Sdfr switch (dtag) { 52155682Smarkm case FCC_TAG_DELTATIME : 522127808Snectar ret = krb5_ret_int32 (sp, &context->kdc_sec_offset); 523127808Snectar if(ret) { 524178825Sdfr krb5_set_error_string(context, "Error reading kdc_sec in " 525178825Sdfr "cache file: %s", FILENAME(id)); 526127808Snectar ret = KRB5_CC_FORMAT; 527127808Snectar goto out; 528127808Snectar } 529127808Snectar ret = krb5_ret_int32 (sp, &context->kdc_usec_offset); 530127808Snectar if(ret) { 531178825Sdfr krb5_set_error_string(context, "Error reading kdc_usec in " 532178825Sdfr "cache file: %s", FILENAME(id)); 533127808Snectar ret = KRB5_CC_FORMAT; 534127808Snectar goto out; 535127808Snectar } 53655682Smarkm break; 53755682Smarkm default : 538127808Snectar for (i = 0; i < data_len; ++i) { 539127808Snectar ret = krb5_ret_int8 (sp, &dummy); 540127808Snectar if(ret) { 541178825Sdfr krb5_set_error_string(context, "Error reading unknown " 542178825Sdfr "tag in cache file: %s", 543178825Sdfr FILENAME(id)); 544127808Snectar ret = KRB5_CC_FORMAT; 545127808Snectar goto out; 546127808Snectar } 547127808Snectar } 54855682Smarkm break; 54955682Smarkm } 55055682Smarkm length -= 4 + data_len; 55155682Smarkm } 55255682Smarkm break; 55355682Smarkm } 55455682Smarkm case KRB5_FCC_FVNO_3: 55555682Smarkm case KRB5_FCC_FVNO_2: 55655682Smarkm case KRB5_FCC_FVNO_1: 55755682Smarkm break; 55855682Smarkm default : 559127808Snectar ret = KRB5_CCACHE_BADVNO; 560178825Sdfr krb5_set_error_string(context, "Unknown version number (%d) in " 561178825Sdfr "credential cache file: %s", 562178825Sdfr (int)tag, FILENAME(id)); 563127808Snectar goto out; 56455682Smarkm } 56555682Smarkm *ret_sp = sp; 56655682Smarkm *ret_fd = fd; 567127808Snectar 56855682Smarkm return 0; 569127808Snectar out: 570127808Snectar if(sp != NULL) 571127808Snectar krb5_storage_free(sp); 572127808Snectar fcc_unlock(context, fd); 573127808Snectar close(fd); 574127808Snectar return ret; 57555682Smarkm} 57655682Smarkm 57755682Smarkmstatic krb5_error_code 57855682Smarkmfcc_get_principal(krb5_context context, 57955682Smarkm krb5_ccache id, 58055682Smarkm krb5_principal *principal) 58155682Smarkm{ 58255682Smarkm krb5_error_code ret; 58355682Smarkm int fd; 58455682Smarkm krb5_storage *sp; 58555682Smarkm 586127808Snectar ret = init_fcc (context, id, &sp, &fd); 58755682Smarkm if (ret) 58855682Smarkm return ret; 58972445Sassar ret = krb5_ret_principal(sp, principal); 590178825Sdfr if (ret) 591178825Sdfr krb5_clear_error_string(context); 59255682Smarkm krb5_storage_free(sp); 593127808Snectar fcc_unlock(context, fd); 59455682Smarkm close(fd); 59572445Sassar return ret; 59655682Smarkm} 59755682Smarkm 59855682Smarkmstatic krb5_error_code 599127808Snectarfcc_end_get (krb5_context context, 600127808Snectar krb5_ccache id, 601127808Snectar krb5_cc_cursor *cursor); 602127808Snectar 603127808Snectarstatic krb5_error_code 60455682Smarkmfcc_get_first (krb5_context context, 60555682Smarkm krb5_ccache id, 60655682Smarkm krb5_cc_cursor *cursor) 60755682Smarkm{ 60855682Smarkm krb5_error_code ret; 60955682Smarkm krb5_principal principal; 61055682Smarkm 61155682Smarkm *cursor = malloc(sizeof(struct fcc_cursor)); 612178825Sdfr if (*cursor == NULL) { 613178825Sdfr krb5_set_error_string (context, "malloc: out of memory"); 614178825Sdfr return ENOMEM; 615178825Sdfr } 616178825Sdfr memset(*cursor, 0, sizeof(struct fcc_cursor)); 61755682Smarkm 618127808Snectar ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp, 61955682Smarkm &FCC_CURSOR(*cursor)->fd); 620127808Snectar if (ret) { 621127808Snectar free(*cursor); 622178825Sdfr *cursor = NULL; 62355682Smarkm return ret; 624127808Snectar } 625127808Snectar ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 626127808Snectar if(ret) { 627178825Sdfr krb5_clear_error_string(context); 628127808Snectar fcc_end_get(context, id, cursor); 629127808Snectar return ret; 630127808Snectar } 63155682Smarkm krb5_free_principal (context, principal); 632127808Snectar fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 63355682Smarkm return 0; 63455682Smarkm} 63555682Smarkm 63655682Smarkmstatic krb5_error_code 63755682Smarkmfcc_get_next (krb5_context context, 63855682Smarkm krb5_ccache id, 63955682Smarkm krb5_cc_cursor *cursor, 64055682Smarkm krb5_creds *creds) 64155682Smarkm{ 642127808Snectar krb5_error_code ret; 643127808Snectar if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0) 644127808Snectar return ret; 645127808Snectar 646127808Snectar ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); 647178825Sdfr if (ret) 648178825Sdfr krb5_clear_error_string(context); 649127808Snectar 650127808Snectar fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 651127808Snectar return ret; 65255682Smarkm} 65355682Smarkm 65455682Smarkmstatic krb5_error_code 65555682Smarkmfcc_end_get (krb5_context context, 65655682Smarkm krb5_ccache id, 65755682Smarkm krb5_cc_cursor *cursor) 65855682Smarkm{ 65955682Smarkm krb5_storage_free(FCC_CURSOR(*cursor)->sp); 66055682Smarkm close (FCC_CURSOR(*cursor)->fd); 66155682Smarkm free(*cursor); 662127808Snectar *cursor = NULL; 66355682Smarkm return 0; 66455682Smarkm} 66555682Smarkm 66655682Smarkmstatic krb5_error_code 66755682Smarkmfcc_remove_cred(krb5_context context, 66855682Smarkm krb5_ccache id, 66955682Smarkm krb5_flags which, 67055682Smarkm krb5_creds *cred) 67155682Smarkm{ 672178825Sdfr krb5_error_code ret; 673178825Sdfr krb5_ccache copy; 674178825Sdfr 675178825Sdfr ret = krb5_cc_gen_new(context, &krb5_mcc_ops, ©); 676178825Sdfr if (ret) 677178825Sdfr return ret; 678178825Sdfr 679178825Sdfr ret = krb5_cc_copy_cache(context, id, copy); 680178825Sdfr if (ret) { 681178825Sdfr krb5_cc_destroy(context, copy); 682178825Sdfr return ret; 683178825Sdfr } 684178825Sdfr 685178825Sdfr ret = krb5_cc_remove_cred(context, copy, which, cred); 686178825Sdfr if (ret) { 687178825Sdfr krb5_cc_destroy(context, copy); 688178825Sdfr return ret; 689178825Sdfr } 690178825Sdfr 691178825Sdfr fcc_destroy(context, id); 692178825Sdfr 693178825Sdfr ret = krb5_cc_copy_cache(context, copy, id); 694178825Sdfr krb5_cc_destroy(context, copy); 695178825Sdfr 696178825Sdfr return ret; 69755682Smarkm} 69855682Smarkm 69955682Smarkmstatic krb5_error_code 70055682Smarkmfcc_set_flags(krb5_context context, 70155682Smarkm krb5_ccache id, 70255682Smarkm krb5_flags flags) 70355682Smarkm{ 70455682Smarkm return 0; /* XXX */ 70555682Smarkm} 70655682Smarkm 70755682Smarkmstatic krb5_error_code 70855682Smarkmfcc_get_version(krb5_context context, 70955682Smarkm krb5_ccache id) 71055682Smarkm{ 71155682Smarkm return FCACHE(id)->version; 71255682Smarkm} 71355682Smarkm 714178825Sdfrstruct fcache_iter { 715178825Sdfr int first; 716178825Sdfr}; 717178825Sdfr 718178825Sdfrstatic krb5_error_code 719178825Sdfrfcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 720178825Sdfr{ 721178825Sdfr struct fcache_iter *iter; 722178825Sdfr 723178825Sdfr iter = calloc(1, sizeof(*iter)); 724178825Sdfr if (iter == NULL) { 725178825Sdfr krb5_set_error_string(context, "malloc - out of memory"); 726178825Sdfr return ENOMEM; 727178825Sdfr } 728178825Sdfr iter->first = 1; 729178825Sdfr *cursor = iter; 730178825Sdfr return 0; 731178825Sdfr} 732178825Sdfr 733178825Sdfrstatic krb5_error_code 734178825Sdfrfcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 735178825Sdfr{ 736178825Sdfr struct fcache_iter *iter = cursor; 737178825Sdfr krb5_error_code ret; 738178825Sdfr const char *fn; 739178825Sdfr char *expandedfn = NULL; 740178825Sdfr 741178825Sdfr if (!iter->first) { 742178825Sdfr krb5_clear_error_string(context); 743178825Sdfr return KRB5_CC_END; 744178825Sdfr } 745178825Sdfr iter->first = 0; 746178825Sdfr 747178825Sdfr fn = krb5_cc_default_name(context); 748178825Sdfr if (strncasecmp(fn, "FILE:", 5) != 0) { 749178825Sdfr ret = _krb5_expand_default_cc_name(context, 750178825Sdfr KRB5_DEFAULT_CCNAME_FILE, 751178825Sdfr &expandedfn); 752178825Sdfr if (ret) 753178825Sdfr return ret; 754178825Sdfr } 755178825Sdfr ret = krb5_cc_resolve(context, fn, id); 756178825Sdfr if (expandedfn) 757178825Sdfr free(expandedfn); 758178825Sdfr 759178825Sdfr return ret; 760178825Sdfr} 761178825Sdfr 762178825Sdfrstatic krb5_error_code 763178825Sdfrfcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 764178825Sdfr{ 765178825Sdfr struct fcache_iter *iter = cursor; 766178825Sdfr free(iter); 767178825Sdfr return 0; 768178825Sdfr} 769178825Sdfr 770178825Sdfrstatic krb5_error_code 771178825Sdfrfcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 772178825Sdfr{ 773178825Sdfr krb5_error_code ret = 0; 774178825Sdfr 775178825Sdfr ret = rename(FILENAME(from), FILENAME(to)); 776178825Sdfr if (ret && errno != EXDEV) { 777178825Sdfr ret = errno; 778178825Sdfr krb5_set_error_string(context, 779178825Sdfr "Rename of file from %s to %s failed: %s", 780178825Sdfr FILENAME(from), FILENAME(to), 781178825Sdfr strerror(ret)); 782178825Sdfr return ret; 783178825Sdfr } else if (ret && errno == EXDEV) { 784178825Sdfr /* make a copy and delete the orignal */ 785178825Sdfr krb5_ssize_t sz1, sz2; 786178825Sdfr int fd1, fd2; 787178825Sdfr char buf[BUFSIZ]; 788178825Sdfr 789178825Sdfr ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY, 0); 790178825Sdfr if(ret) 791178825Sdfr return ret; 792178825Sdfr 793178825Sdfr unlink(FILENAME(to)); 794178825Sdfr 795178825Sdfr ret = fcc_open(context, to, &fd2, 796178825Sdfr O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600); 797178825Sdfr if(ret) 798178825Sdfr goto out1; 799178825Sdfr 800178825Sdfr while((sz1 = read(fd1, buf, sizeof(buf))) > 0) { 801178825Sdfr sz2 = write(fd2, buf, sz1); 802178825Sdfr if (sz1 != sz2) { 803178825Sdfr ret = EIO; 804178825Sdfr krb5_set_error_string(context, 805178825Sdfr "Failed to write data from one file " 806178825Sdfr "credential cache to the other"); 807178825Sdfr goto out2; 808178825Sdfr } 809178825Sdfr } 810178825Sdfr if (sz1 < 0) { 811178825Sdfr ret = EIO; 812178825Sdfr krb5_set_error_string(context, 813178825Sdfr "Failed to read data from one file " 814178825Sdfr "credential cache to the other"); 815178825Sdfr goto out2; 816178825Sdfr } 817178825Sdfr erase_file(FILENAME(from)); 818178825Sdfr 819178825Sdfr out2: 820178825Sdfr fcc_unlock(context, fd2); 821178825Sdfr close(fd2); 822178825Sdfr 823178825Sdfr out1: 824178825Sdfr fcc_unlock(context, fd1); 825178825Sdfr close(fd1); 826178825Sdfr 827178825Sdfr if (ret) { 828178825Sdfr erase_file(FILENAME(to)); 829178825Sdfr return ret; 830178825Sdfr } 831178825Sdfr } 832178825Sdfr 833178825Sdfr /* make sure ->version is uptodate */ 834178825Sdfr { 835178825Sdfr krb5_storage *sp; 836178825Sdfr int fd; 837178825Sdfr ret = init_fcc (context, to, &sp, &fd); 838178825Sdfr krb5_storage_free(sp); 839178825Sdfr fcc_unlock(context, fd); 840178825Sdfr close(fd); 841178825Sdfr } 842178825Sdfr return ret; 843178825Sdfr} 844178825Sdfr 845178825Sdfrstatic krb5_error_code 846178825Sdfrfcc_default_name(krb5_context context, char **str) 847178825Sdfr{ 848178825Sdfr return _krb5_expand_default_cc_name(context, 849178825Sdfr KRB5_DEFAULT_CCNAME_FILE, 850178825Sdfr str); 851178825Sdfr} 852178825Sdfr 853178825Sdfr/** 854178825Sdfr * Variable containing the FILE based credential cache implemention. 855178825Sdfr * 856178825Sdfr * @ingroup krb5_ccache 857178825Sdfr */ 858178825Sdfr 85955682Smarkmconst krb5_cc_ops krb5_fcc_ops = { 86055682Smarkm "FILE", 86155682Smarkm fcc_get_name, 86255682Smarkm fcc_resolve, 86355682Smarkm fcc_gen_new, 86455682Smarkm fcc_initialize, 86555682Smarkm fcc_destroy, 86655682Smarkm fcc_close, 86755682Smarkm fcc_store_cred, 86855682Smarkm NULL, /* fcc_retrieve */ 86955682Smarkm fcc_get_principal, 87055682Smarkm fcc_get_first, 87155682Smarkm fcc_get_next, 87255682Smarkm fcc_end_get, 87355682Smarkm fcc_remove_cred, 87455682Smarkm fcc_set_flags, 875178825Sdfr fcc_get_version, 876178825Sdfr fcc_get_cache_first, 877178825Sdfr fcc_get_cache_next, 878178825Sdfr fcc_end_cache_get, 879178825Sdfr fcc_move, 880178825Sdfr fcc_default_name 88155682Smarkm}; 882