1b528cefcSMark Murray /* 2*ae771770SStanislav Sedov * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan 3b528cefcSMark Murray * (Royal Institute of Technology, Stockholm, Sweden). 4b528cefcSMark Murray * All rights reserved. 5b528cefcSMark Murray * 6*ae771770SStanislav Sedov * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7*ae771770SStanislav Sedov * 8b528cefcSMark Murray * Redistribution and use in source and binary forms, with or without 9b528cefcSMark Murray * modification, are permitted provided that the following conditions 10b528cefcSMark Murray * are met: 11b528cefcSMark Murray * 12b528cefcSMark Murray * 1. Redistributions of source code must retain the above copyright 13b528cefcSMark Murray * notice, this list of conditions and the following disclaimer. 14b528cefcSMark Murray * 15b528cefcSMark Murray * 2. Redistributions in binary form must reproduce the above copyright 16b528cefcSMark Murray * notice, this list of conditions and the following disclaimer in the 17b528cefcSMark Murray * documentation and/or other materials provided with the distribution. 18b528cefcSMark Murray * 19b528cefcSMark Murray * 3. Neither the name of the Institute nor the names of its contributors 20b528cefcSMark Murray * may be used to endorse or promote products derived from this software 21b528cefcSMark Murray * without specific prior written permission. 22b528cefcSMark Murray * 23b528cefcSMark Murray * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24b528cefcSMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25b528cefcSMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26b528cefcSMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27b528cefcSMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28b528cefcSMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29b528cefcSMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30b528cefcSMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31b528cefcSMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32b528cefcSMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33b528cefcSMark Murray * SUCH DAMAGE. 34b528cefcSMark Murray */ 35b528cefcSMark Murray 36b528cefcSMark Murray #include "krb5_locl.h" 37b528cefcSMark Murray 38b528cefcSMark Murray typedef struct krb5_fcache{ 39b528cefcSMark Murray char *filename; 40b528cefcSMark Murray int version; 41b528cefcSMark Murray }krb5_fcache; 42b528cefcSMark Murray 43b528cefcSMark Murray struct fcc_cursor { 44b528cefcSMark Murray int fd; 45b528cefcSMark Murray krb5_storage *sp; 46b528cefcSMark Murray }; 47b528cefcSMark Murray 48b528cefcSMark Murray #define KRB5_FCC_FVNO_1 1 49b528cefcSMark Murray #define KRB5_FCC_FVNO_2 2 50b528cefcSMark Murray #define KRB5_FCC_FVNO_3 3 51b528cefcSMark Murray #define KRB5_FCC_FVNO_4 4 52b528cefcSMark Murray 53b528cefcSMark Murray #define FCC_TAG_DELTATIME 1 54b528cefcSMark Murray 55b528cefcSMark Murray #define FCACHE(X) ((krb5_fcache*)(X)->data.data) 56b528cefcSMark Murray 57b528cefcSMark Murray #define FILENAME(X) (FCACHE(X)->filename) 58b528cefcSMark Murray 59b528cefcSMark Murray #define FCC_CURSOR(C) ((struct fcc_cursor*)(C)) 60b528cefcSMark Murray 61*ae771770SStanislav Sedov static const char* KRB5_CALLCONV 62b528cefcSMark Murray fcc_get_name(krb5_context context, 63b528cefcSMark Murray krb5_ccache id) 64b528cefcSMark Murray { 65*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 66*ae771770SStanislav Sedov return NULL; 67*ae771770SStanislav Sedov 68b528cefcSMark Murray return FILENAME(id); 69b528cefcSMark Murray } 70b528cefcSMark Murray 711c43270aSJacques Vidrine int 721c43270aSJacques Vidrine _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive, 731c43270aSJacques Vidrine const char *filename) 741c43270aSJacques Vidrine { 751c43270aSJacques Vidrine int ret; 761c43270aSJacques Vidrine #ifdef HAVE_FCNTL 771c43270aSJacques Vidrine struct flock l; 781c43270aSJacques Vidrine 791c43270aSJacques Vidrine l.l_start = 0; 801c43270aSJacques Vidrine l.l_len = 0; 811c43270aSJacques Vidrine l.l_type = exclusive ? F_WRLCK : F_RDLCK; 821c43270aSJacques Vidrine l.l_whence = SEEK_SET; 831c43270aSJacques Vidrine ret = fcntl(fd, F_SETLKW, &l); 841c43270aSJacques Vidrine #else 851c43270aSJacques Vidrine ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH); 861c43270aSJacques Vidrine #endif 871c43270aSJacques Vidrine if(ret < 0) 881c43270aSJacques Vidrine ret = errno; 891c43270aSJacques Vidrine if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */ 901c43270aSJacques Vidrine ret = EAGAIN; 911c43270aSJacques Vidrine 921c43270aSJacques Vidrine switch (ret) { 931c43270aSJacques Vidrine case 0: 941c43270aSJacques Vidrine break; 951c43270aSJacques Vidrine case EINVAL: /* filesystem doesn't support locking, let the user have it */ 961c43270aSJacques Vidrine ret = 0; 971c43270aSJacques Vidrine break; 981c43270aSJacques Vidrine case EAGAIN: 99*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 100*ae771770SStanislav Sedov N_("timed out locking cache file %s", "file"), 1011c43270aSJacques Vidrine filename); 1021c43270aSJacques Vidrine break; 103*ae771770SStanislav Sedov default: { 104*ae771770SStanislav Sedov char buf[128]; 105*ae771770SStanislav Sedov rk_strerror_r(ret, buf, sizeof(buf)); 106*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 107*ae771770SStanislav Sedov N_("error locking cache file %s: %s", 108*ae771770SStanislav Sedov "file, error"), filename, buf); 1091c43270aSJacques Vidrine break; 1101c43270aSJacques Vidrine } 111*ae771770SStanislav Sedov } 1121c43270aSJacques Vidrine return ret; 1131c43270aSJacques Vidrine } 1141c43270aSJacques Vidrine 1151c43270aSJacques Vidrine int 116c19800e8SDoug Rabson _krb5_xunlock(krb5_context context, int fd) 1171c43270aSJacques Vidrine { 118c19800e8SDoug Rabson int ret; 119c19800e8SDoug Rabson #ifdef HAVE_FCNTL 1201c43270aSJacques Vidrine struct flock l; 1211c43270aSJacques Vidrine l.l_start = 0; 1221c43270aSJacques Vidrine l.l_len = 0; 1231c43270aSJacques Vidrine l.l_type = F_UNLCK; 1241c43270aSJacques Vidrine l.l_whence = SEEK_SET; 125c19800e8SDoug Rabson ret = fcntl(fd, F_SETLKW, &l); 1261c43270aSJacques Vidrine #else 127c19800e8SDoug Rabson ret = flock(fd, LOCK_UN); 1281c43270aSJacques Vidrine #endif 129c19800e8SDoug Rabson if (ret < 0) 130c19800e8SDoug Rabson ret = errno; 131c19800e8SDoug Rabson switch (ret) { 132c19800e8SDoug Rabson case 0: 133c19800e8SDoug Rabson break; 134c19800e8SDoug Rabson case EINVAL: /* filesystem doesn't support locking, let the user have it */ 135c19800e8SDoug Rabson ret = 0; 136c19800e8SDoug Rabson break; 137*ae771770SStanislav Sedov default: { 138*ae771770SStanislav Sedov char buf[128]; 139*ae771770SStanislav Sedov rk_strerror_r(ret, buf, sizeof(buf)); 140*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 141*ae771770SStanislav Sedov N_("Failed to unlock file: %s", ""), buf); 142c19800e8SDoug Rabson break; 143c19800e8SDoug Rabson } 144*ae771770SStanislav Sedov } 145c19800e8SDoug Rabson return ret; 1461c43270aSJacques Vidrine } 1471c43270aSJacques Vidrine 1481c43270aSJacques Vidrine static krb5_error_code 149*ae771770SStanislav Sedov write_storage(krb5_context context, krb5_storage *sp, int fd) 150*ae771770SStanislav Sedov { 151*ae771770SStanislav Sedov krb5_error_code ret; 152*ae771770SStanislav Sedov krb5_data data; 153*ae771770SStanislav Sedov ssize_t sret; 154*ae771770SStanislav Sedov 155*ae771770SStanislav Sedov ret = krb5_storage_to_data(sp, &data); 156*ae771770SStanislav Sedov if (ret) { 157*ae771770SStanislav Sedov krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 158*ae771770SStanislav Sedov return ret; 159*ae771770SStanislav Sedov } 160*ae771770SStanislav Sedov sret = write(fd, data.data, data.length); 161*ae771770SStanislav Sedov ret = (sret != (ssize_t)data.length); 162*ae771770SStanislav Sedov krb5_data_free(&data); 163*ae771770SStanislav Sedov if (ret) { 164*ae771770SStanislav Sedov ret = errno; 165*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 166*ae771770SStanislav Sedov N_("Failed to write FILE credential data", "")); 167*ae771770SStanislav Sedov return ret; 168*ae771770SStanislav Sedov } 169*ae771770SStanislav Sedov return 0; 170*ae771770SStanislav Sedov } 171*ae771770SStanislav Sedov 172*ae771770SStanislav Sedov 173*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 1741c43270aSJacques Vidrine fcc_lock(krb5_context context, krb5_ccache id, 1751c43270aSJacques Vidrine int fd, krb5_boolean exclusive) 1761c43270aSJacques Vidrine { 1771c43270aSJacques Vidrine return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id)); 1781c43270aSJacques Vidrine } 1791c43270aSJacques Vidrine 180*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 1811c43270aSJacques Vidrine fcc_unlock(krb5_context context, int fd) 1821c43270aSJacques Vidrine { 183c19800e8SDoug Rabson return _krb5_xunlock(context, fd); 1841c43270aSJacques Vidrine } 1851c43270aSJacques Vidrine 186*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 187b528cefcSMark Murray fcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 188b528cefcSMark Murray { 189b528cefcSMark Murray krb5_fcache *f; 190b528cefcSMark Murray f = malloc(sizeof(*f)); 191adb0ddaeSAssar Westerlund if(f == NULL) { 192*ae771770SStanislav Sedov krb5_set_error_message(context, KRB5_CC_NOMEM, 193*ae771770SStanislav Sedov N_("malloc: out of memory", "")); 194b528cefcSMark Murray return KRB5_CC_NOMEM; 195adb0ddaeSAssar Westerlund } 196b528cefcSMark Murray f->filename = strdup(res); 197b528cefcSMark Murray if(f->filename == NULL){ 198b528cefcSMark Murray free(f); 199*ae771770SStanislav Sedov krb5_set_error_message(context, KRB5_CC_NOMEM, 200*ae771770SStanislav Sedov N_("malloc: out of memory", "")); 201b528cefcSMark Murray return KRB5_CC_NOMEM; 202b528cefcSMark Murray } 203b528cefcSMark Murray f->version = 0; 204b528cefcSMark Murray (*id)->data.data = f; 205b528cefcSMark Murray (*id)->data.length = sizeof(*f); 206b528cefcSMark Murray return 0; 207b528cefcSMark Murray } 208b528cefcSMark Murray 2095e9cd1aeSAssar Westerlund /* 2105e9cd1aeSAssar Westerlund * Try to scrub the contents of `filename' safely. 2115e9cd1aeSAssar Westerlund */ 2125e9cd1aeSAssar Westerlund 2135e9cd1aeSAssar Westerlund static int 2145e9cd1aeSAssar Westerlund scrub_file (int fd) 2155e9cd1aeSAssar Westerlund { 2165e9cd1aeSAssar Westerlund off_t pos; 2175e9cd1aeSAssar Westerlund char buf[128]; 2185e9cd1aeSAssar Westerlund 2195e9cd1aeSAssar Westerlund pos = lseek(fd, 0, SEEK_END); 2205e9cd1aeSAssar Westerlund if (pos < 0) 2215e9cd1aeSAssar Westerlund return errno; 2225e9cd1aeSAssar Westerlund if (lseek(fd, 0, SEEK_SET) < 0) 2235e9cd1aeSAssar Westerlund return errno; 2245e9cd1aeSAssar Westerlund memset(buf, 0, sizeof(buf)); 2255e9cd1aeSAssar Westerlund while(pos > 0) { 226*ae771770SStanislav Sedov ssize_t tmp = write(fd, buf, min((off_t)sizeof(buf), pos)); 2275e9cd1aeSAssar Westerlund 2285e9cd1aeSAssar Westerlund if (tmp < 0) 2295e9cd1aeSAssar Westerlund return errno; 2305e9cd1aeSAssar Westerlund pos -= tmp; 2315e9cd1aeSAssar Westerlund } 232*ae771770SStanislav Sedov #ifdef _MSC_VER 233*ae771770SStanislav Sedov _commit (fd); 234*ae771770SStanislav Sedov #else 2355e9cd1aeSAssar Westerlund fsync (fd); 236*ae771770SStanislav Sedov #endif 2375e9cd1aeSAssar Westerlund return 0; 2385e9cd1aeSAssar Westerlund } 2395e9cd1aeSAssar Westerlund 2405e9cd1aeSAssar Westerlund /* 2415e9cd1aeSAssar Westerlund * Erase `filename' if it exists, trying to remove the contents if 2425e9cd1aeSAssar Westerlund * it's `safe'. We always try to remove the file, it it exists. It's 2435e9cd1aeSAssar Westerlund * only overwritten if it's a regular file (not a symlink and not a 2445e9cd1aeSAssar Westerlund * hardlink) 2455e9cd1aeSAssar Westerlund */ 2465e9cd1aeSAssar Westerlund 247*ae771770SStanislav Sedov krb5_error_code 248*ae771770SStanislav Sedov _krb5_erase_file(krb5_context context, const char *filename) 249b528cefcSMark Murray { 250b528cefcSMark Murray int fd; 2515e9cd1aeSAssar Westerlund struct stat sb1, sb2; 2525e9cd1aeSAssar Westerlund int ret; 2535e9cd1aeSAssar Westerlund 2545e9cd1aeSAssar Westerlund ret = lstat (filename, &sb1); 2555e9cd1aeSAssar Westerlund if (ret < 0) 2565e9cd1aeSAssar Westerlund return errno; 257b528cefcSMark Murray 258b528cefcSMark Murray fd = open(filename, O_RDWR | O_BINARY); 259b528cefcSMark Murray if(fd < 0) { 260b528cefcSMark Murray if(errno == ENOENT) 261b528cefcSMark Murray return 0; 262b528cefcSMark Murray else 263b528cefcSMark Murray return errno; 264b528cefcSMark Murray } 265*ae771770SStanislav Sedov rk_cloexec(fd); 266*ae771770SStanislav Sedov ret = _krb5_xlock(context, fd, 1, filename); 267*ae771770SStanislav Sedov if (ret) { 268*ae771770SStanislav Sedov close(fd); 269*ae771770SStanislav Sedov return ret; 270*ae771770SStanislav Sedov } 2715e9cd1aeSAssar Westerlund if (unlink(filename) < 0) { 272*ae771770SStanislav Sedov _krb5_xunlock(context, fd); 273b528cefcSMark Murray close (fd); 2745e9cd1aeSAssar Westerlund return errno; 2755e9cd1aeSAssar Westerlund } 2765e9cd1aeSAssar Westerlund ret = fstat (fd, &sb2); 2775e9cd1aeSAssar Westerlund if (ret < 0) { 278*ae771770SStanislav Sedov _krb5_xunlock(context, fd); 2795e9cd1aeSAssar Westerlund close (fd); 2805e9cd1aeSAssar Westerlund return errno; 2815e9cd1aeSAssar Westerlund } 2825e9cd1aeSAssar Westerlund 2835e9cd1aeSAssar Westerlund /* check if someone was playing with symlinks */ 2845e9cd1aeSAssar Westerlund 2855e9cd1aeSAssar Westerlund if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 286*ae771770SStanislav Sedov _krb5_xunlock(context, fd); 2875e9cd1aeSAssar Westerlund close (fd); 2885e9cd1aeSAssar Westerlund return EPERM; 2895e9cd1aeSAssar Westerlund } 2905e9cd1aeSAssar Westerlund 2915e9cd1aeSAssar Westerlund /* there are still hard links to this file */ 2925e9cd1aeSAssar Westerlund 2935e9cd1aeSAssar Westerlund if (sb2.st_nlink != 0) { 294*ae771770SStanislav Sedov _krb5_xunlock(context, fd); 2955e9cd1aeSAssar Westerlund close (fd); 296b528cefcSMark Murray return 0; 297b528cefcSMark Murray } 298b528cefcSMark Murray 2995e9cd1aeSAssar Westerlund ret = scrub_file (fd); 300*ae771770SStanislav Sedov if (ret) { 301*ae771770SStanislav Sedov _krb5_xunlock(context, fd); 302*ae771770SStanislav Sedov close(fd); 303*ae771770SStanislav Sedov return ret; 304*ae771770SStanislav Sedov } 305*ae771770SStanislav Sedov ret = _krb5_xunlock(context, fd); 3065e9cd1aeSAssar Westerlund close (fd); 3075e9cd1aeSAssar Westerlund return ret; 3085e9cd1aeSAssar Westerlund } 3095e9cd1aeSAssar Westerlund 310*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 311b528cefcSMark Murray fcc_gen_new(krb5_context context, krb5_ccache *id) 312b528cefcSMark Murray { 313*ae771770SStanislav Sedov char *file = NULL, *exp_file = NULL; 314*ae771770SStanislav Sedov krb5_error_code ret; 315b528cefcSMark Murray krb5_fcache *f; 316b528cefcSMark Murray int fd; 317adb0ddaeSAssar Westerlund 318b528cefcSMark Murray f = malloc(sizeof(*f)); 319adb0ddaeSAssar Westerlund if(f == NULL) { 320*ae771770SStanislav Sedov krb5_set_error_message(context, KRB5_CC_NOMEM, 321*ae771770SStanislav Sedov N_("malloc: out of memory", "")); 322b528cefcSMark Murray return KRB5_CC_NOMEM; 323adb0ddaeSAssar Westerlund } 324*ae771770SStanislav Sedov ret = asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 325*ae771770SStanislav Sedov if(ret < 0 || file == NULL) { 326b528cefcSMark Murray free(f); 327*ae771770SStanislav Sedov krb5_set_error_message(context, KRB5_CC_NOMEM, 328*ae771770SStanislav Sedov N_("malloc: out of memory", "")); 329b528cefcSMark Murray return KRB5_CC_NOMEM; 330b528cefcSMark Murray } 331*ae771770SStanislav Sedov ret = _krb5_expand_path_tokens(context, file, &exp_file); 332b528cefcSMark Murray free(file); 333*ae771770SStanislav Sedov if (ret) 334c19800e8SDoug Rabson return ret; 335*ae771770SStanislav Sedov 336*ae771770SStanislav Sedov file = exp_file; 337*ae771770SStanislav Sedov 338*ae771770SStanislav Sedov fd = mkstemp(exp_file); 339*ae771770SStanislav Sedov if(fd < 0) { 340*ae771770SStanislav Sedov int xret = errno; 341*ae771770SStanislav Sedov krb5_set_error_message(context, xret, N_("mkstemp %s failed", ""), exp_file); 342*ae771770SStanislav Sedov free(f); 343*ae771770SStanislav Sedov free(exp_file); 344*ae771770SStanislav Sedov return xret; 345b528cefcSMark Murray } 346b528cefcSMark Murray close(fd); 347*ae771770SStanislav Sedov f->filename = exp_file; 348b528cefcSMark Murray f->version = 0; 349b528cefcSMark Murray (*id)->data.data = f; 350b528cefcSMark Murray (*id)->data.length = sizeof(*f); 351b528cefcSMark Murray return 0; 352b528cefcSMark Murray } 353b528cefcSMark Murray 354b528cefcSMark Murray static void 355b528cefcSMark Murray storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 356b528cefcSMark Murray { 357b528cefcSMark Murray int flags = 0; 358b528cefcSMark Murray switch(vno) { 359b528cefcSMark Murray case KRB5_FCC_FVNO_1: 360b528cefcSMark Murray flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 361b528cefcSMark Murray flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 362b528cefcSMark Murray flags |= KRB5_STORAGE_HOST_BYTEORDER; 363b528cefcSMark Murray break; 364b528cefcSMark Murray case KRB5_FCC_FVNO_2: 365b528cefcSMark Murray flags |= KRB5_STORAGE_HOST_BYTEORDER; 366b528cefcSMark Murray break; 367b528cefcSMark Murray case KRB5_FCC_FVNO_3: 368b528cefcSMark Murray flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 369b528cefcSMark Murray break; 370b528cefcSMark Murray case KRB5_FCC_FVNO_4: 371b528cefcSMark Murray break; 372b528cefcSMark Murray default: 373b528cefcSMark Murray krb5_abortx(context, 374b528cefcSMark Murray "storage_set_flags called with bad vno (%x)", vno); 375b528cefcSMark Murray } 376b528cefcSMark Murray krb5_storage_set_flags(sp, flags); 377b528cefcSMark Murray } 378b528cefcSMark Murray 379*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 3801c43270aSJacques Vidrine fcc_open(krb5_context context, 3811c43270aSJacques Vidrine krb5_ccache id, 3821c43270aSJacques Vidrine int *fd_ret, 3831c43270aSJacques Vidrine int flags, 3841c43270aSJacques Vidrine mode_t mode) 3851c43270aSJacques Vidrine { 3861c43270aSJacques Vidrine krb5_boolean exclusive = ((flags | O_WRONLY) == flags || 3871c43270aSJacques Vidrine (flags | O_RDWR) == flags); 3881c43270aSJacques Vidrine krb5_error_code ret; 389*ae771770SStanislav Sedov const char *filename; 3901c43270aSJacques Vidrine int fd; 391*ae771770SStanislav Sedov 392*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 393*ae771770SStanislav Sedov return krb5_einval(context, 2); 394*ae771770SStanislav Sedov 395*ae771770SStanislav Sedov filename = FILENAME(id); 396*ae771770SStanislav Sedov 3971c43270aSJacques Vidrine fd = open(filename, flags, mode); 3981c43270aSJacques Vidrine if(fd < 0) { 399*ae771770SStanislav Sedov char buf[128]; 4001c43270aSJacques Vidrine ret = errno; 401*ae771770SStanislav Sedov rk_strerror_r(ret, buf, sizeof(buf)); 402*ae771770SStanislav Sedov krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"), 403*ae771770SStanislav Sedov filename, buf); 4041c43270aSJacques Vidrine return ret; 4051c43270aSJacques Vidrine } 406*ae771770SStanislav Sedov rk_cloexec(fd); 4071c43270aSJacques Vidrine 4081c43270aSJacques Vidrine if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { 4091c43270aSJacques Vidrine close(fd); 4101c43270aSJacques Vidrine return ret; 4111c43270aSJacques Vidrine } 4121c43270aSJacques Vidrine *fd_ret = fd; 4131c43270aSJacques Vidrine return 0; 4141c43270aSJacques Vidrine } 4151c43270aSJacques Vidrine 416*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 417b528cefcSMark Murray fcc_initialize(krb5_context context, 418b528cefcSMark Murray krb5_ccache id, 419b528cefcSMark Murray krb5_principal primary_principal) 420b528cefcSMark Murray { 421b528cefcSMark Murray krb5_fcache *f = FCACHE(id); 4225e9cd1aeSAssar Westerlund int ret = 0; 423b528cefcSMark Murray int fd; 424b528cefcSMark Murray 425*ae771770SStanislav Sedov if (f == NULL) 426*ae771770SStanislav Sedov return krb5_einval(context, 2); 427b528cefcSMark Murray 428*ae771770SStanislav Sedov unlink (f->filename); 429*ae771770SStanislav Sedov 430*ae771770SStanislav Sedov ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 4311c43270aSJacques Vidrine if(ret) 432adb0ddaeSAssar Westerlund return ret; 433b528cefcSMark Murray { 434b528cefcSMark Murray krb5_storage *sp; 435*ae771770SStanislav Sedov sp = krb5_storage_emem(); 4368373020dSJacques Vidrine krb5_storage_set_eof_code(sp, KRB5_CC_END); 437b528cefcSMark Murray if(context->fcache_vno != 0) 438b528cefcSMark Murray f->version = context->fcache_vno; 439b528cefcSMark Murray else 440b528cefcSMark Murray f->version = KRB5_FCC_FVNO_4; 4415e9cd1aeSAssar Westerlund ret |= krb5_store_int8(sp, 5); 4425e9cd1aeSAssar Westerlund ret |= krb5_store_int8(sp, f->version); 443b528cefcSMark Murray storage_set_flags(context, sp, f->version); 4445e9cd1aeSAssar Westerlund if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 445b528cefcSMark Murray /* V4 stuff */ 446b528cefcSMark Murray if (context->kdc_sec_offset) { 4475e9cd1aeSAssar Westerlund ret |= krb5_store_int16 (sp, 12); /* length */ 4485e9cd1aeSAssar Westerlund ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 4495e9cd1aeSAssar Westerlund ret |= krb5_store_int16 (sp, 8); /* length of data */ 4505e9cd1aeSAssar Westerlund ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 4515e9cd1aeSAssar Westerlund ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 452b528cefcSMark Murray } else { 4535e9cd1aeSAssar Westerlund ret |= krb5_store_int16 (sp, 0); 454b528cefcSMark Murray } 455b528cefcSMark Murray } 4565e9cd1aeSAssar Westerlund ret |= krb5_store_principal(sp, primary_principal); 4571c43270aSJacques Vidrine 458*ae771770SStanislav Sedov ret |= write_storage(context, sp, fd); 459*ae771770SStanislav Sedov 460b528cefcSMark Murray krb5_storage_free(sp); 461b528cefcSMark Murray } 4621c43270aSJacques Vidrine fcc_unlock(context, fd); 4635e9cd1aeSAssar Westerlund if (close(fd) < 0) 464adb0ddaeSAssar Westerlund if (ret == 0) { 465*ae771770SStanislav Sedov char buf[128]; 4665e9cd1aeSAssar Westerlund ret = errno; 467*ae771770SStanislav Sedov rk_strerror_r(ret, buf, sizeof(buf)); 468*ae771770SStanislav Sedov krb5_set_error_message (context, ret, N_("close %s: %s", ""), 469*ae771770SStanislav Sedov FILENAME(id), buf); 470adb0ddaeSAssar Westerlund } 4715e9cd1aeSAssar Westerlund return ret; 472b528cefcSMark Murray } 473b528cefcSMark Murray 474*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 475b528cefcSMark Murray fcc_close(krb5_context context, 476b528cefcSMark Murray krb5_ccache id) 477b528cefcSMark Murray { 478*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 479*ae771770SStanislav Sedov return krb5_einval(context, 2); 480*ae771770SStanislav Sedov 481b528cefcSMark Murray free (FILENAME(id)); 482b528cefcSMark Murray krb5_data_free(&id->data); 483b528cefcSMark Murray return 0; 484b528cefcSMark Murray } 485b528cefcSMark Murray 486*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 487b528cefcSMark Murray fcc_destroy(krb5_context context, 488b528cefcSMark Murray krb5_ccache id) 489b528cefcSMark Murray { 490*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 491*ae771770SStanislav Sedov return krb5_einval(context, 2); 492*ae771770SStanislav Sedov 493*ae771770SStanislav Sedov _krb5_erase_file(context, FILENAME(id)); 494b528cefcSMark Murray return 0; 495b528cefcSMark Murray } 496b528cefcSMark Murray 497*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 498b528cefcSMark Murray fcc_store_cred(krb5_context context, 499b528cefcSMark Murray krb5_ccache id, 500b528cefcSMark Murray krb5_creds *creds) 501b528cefcSMark Murray { 5025e9cd1aeSAssar Westerlund int ret; 503b528cefcSMark Murray int fd; 504b528cefcSMark Murray 505*ae771770SStanislav Sedov ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0); 5061c43270aSJacques Vidrine if(ret) 507adb0ddaeSAssar Westerlund return ret; 508b528cefcSMark Murray { 509b528cefcSMark Murray krb5_storage *sp; 510*ae771770SStanislav Sedov 511*ae771770SStanislav Sedov sp = krb5_storage_emem(); 5128373020dSJacques Vidrine krb5_storage_set_eof_code(sp, KRB5_CC_END); 513b528cefcSMark Murray storage_set_flags(context, sp, FCACHE(id)->version); 514c19800e8SDoug Rabson if (!krb5_config_get_bool_default(context, NULL, TRUE, 5151c43270aSJacques Vidrine "libdefaults", 5161c43270aSJacques Vidrine "fcc-mit-ticketflags", 5171c43270aSJacques Vidrine NULL)) 518c19800e8SDoug Rabson krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER); 519c19800e8SDoug Rabson ret = krb5_store_creds(sp, creds); 520*ae771770SStanislav Sedov if (ret == 0) 521*ae771770SStanislav Sedov ret = write_storage(context, sp, fd); 522b528cefcSMark Murray krb5_storage_free(sp); 523b528cefcSMark Murray } 5241c43270aSJacques Vidrine fcc_unlock(context, fd); 525*ae771770SStanislav Sedov if (close(fd) < 0) { 526adb0ddaeSAssar Westerlund if (ret == 0) { 527*ae771770SStanislav Sedov char buf[128]; 528*ae771770SStanislav Sedov rk_strerror_r(ret, buf, sizeof(buf)); 5295e9cd1aeSAssar Westerlund ret = errno; 530*ae771770SStanislav Sedov krb5_set_error_message (context, ret, N_("close %s: %s", ""), 531*ae771770SStanislav Sedov FILENAME(id), buf); 532*ae771770SStanislav Sedov } 533adb0ddaeSAssar Westerlund } 5345e9cd1aeSAssar Westerlund return ret; 535b528cefcSMark Murray } 536b528cefcSMark Murray 537b528cefcSMark Murray static krb5_error_code 538b528cefcSMark Murray init_fcc (krb5_context context, 5391c43270aSJacques Vidrine krb5_ccache id, 540b528cefcSMark Murray krb5_storage **ret_sp, 541*ae771770SStanislav Sedov int *ret_fd, 542*ae771770SStanislav Sedov krb5_deltat *kdc_offset) 543b528cefcSMark Murray { 544b528cefcSMark Murray int fd; 545b528cefcSMark Murray int8_t pvno, tag; 546b528cefcSMark Murray krb5_storage *sp; 5475e9cd1aeSAssar Westerlund krb5_error_code ret; 548b528cefcSMark Murray 549*ae771770SStanislav Sedov if (kdc_offset) 550*ae771770SStanislav Sedov *kdc_offset = 0; 551*ae771770SStanislav Sedov 552*ae771770SStanislav Sedov ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 5535e9cd1aeSAssar Westerlund if(ret) 5545e9cd1aeSAssar Westerlund return ret; 5551c43270aSJacques Vidrine 5561c43270aSJacques Vidrine sp = krb5_storage_from_fd(fd); 5571c43270aSJacques Vidrine if(sp == NULL) { 558*ae771770SStanislav Sedov krb5_clear_error_message(context); 5591c43270aSJacques Vidrine ret = ENOMEM; 5601c43270aSJacques Vidrine goto out; 561b528cefcSMark Murray } 5621c43270aSJacques Vidrine krb5_storage_set_eof_code(sp, KRB5_CC_END); 5631c43270aSJacques Vidrine ret = krb5_ret_int8(sp, &pvno); 5641c43270aSJacques Vidrine if(ret != 0) { 565c19800e8SDoug Rabson if(ret == KRB5_CC_END) { 566c19800e8SDoug Rabson ret = ENOENT; 567*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 568*ae771770SStanislav Sedov N_("Empty credential cache file: %s", ""), 569*ae771770SStanislav Sedov FILENAME(id)); 570c19800e8SDoug Rabson } else 571*ae771770SStanislav Sedov krb5_set_error_message(context, ret, N_("Error reading pvno " 572*ae771770SStanislav Sedov "in cache file: %s", ""), 573*ae771770SStanislav Sedov FILENAME(id)); 5741c43270aSJacques Vidrine goto out; 5751c43270aSJacques Vidrine } 5761c43270aSJacques Vidrine if(pvno != 5) { 5771c43270aSJacques Vidrine ret = KRB5_CCACHE_BADVNO; 578*ae771770SStanislav Sedov krb5_set_error_message(context, ret, N_("Bad version number in credential " 579*ae771770SStanislav Sedov "cache file: %s", ""), 580*ae771770SStanislav Sedov FILENAME(id)); 5811c43270aSJacques Vidrine goto out; 5821c43270aSJacques Vidrine } 5831c43270aSJacques Vidrine ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ 5841c43270aSJacques Vidrine if(ret != 0) { 5851c43270aSJacques Vidrine ret = KRB5_CC_FORMAT; 586*ae771770SStanislav Sedov krb5_set_error_message(context, ret, "Error reading tag in " 587*ae771770SStanislav Sedov "cache file: %s", FILENAME(id)); 5881c43270aSJacques Vidrine goto out; 5891c43270aSJacques Vidrine } 5901c43270aSJacques Vidrine FCACHE(id)->version = tag; 5911c43270aSJacques Vidrine storage_set_flags(context, sp, FCACHE(id)->version); 592b528cefcSMark Murray switch (tag) { 593b528cefcSMark Murray case KRB5_FCC_FVNO_4: { 594b528cefcSMark Murray int16_t length; 595b528cefcSMark Murray 5961c43270aSJacques Vidrine ret = krb5_ret_int16 (sp, &length); 5971c43270aSJacques Vidrine if(ret) { 5981c43270aSJacques Vidrine ret = KRB5_CC_FORMAT; 599*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 600*ae771770SStanislav Sedov N_("Error reading tag length in " 601*ae771770SStanislav Sedov "cache file: %s", ""), FILENAME(id)); 6021c43270aSJacques Vidrine goto out; 6031c43270aSJacques Vidrine } 604b528cefcSMark Murray while(length > 0) { 605c19800e8SDoug Rabson int16_t dtag, data_len; 606b528cefcSMark Murray int i; 607b528cefcSMark Murray int8_t dummy; 608b528cefcSMark Murray 609c19800e8SDoug Rabson ret = krb5_ret_int16 (sp, &dtag); 6101c43270aSJacques Vidrine if(ret) { 6111c43270aSJacques Vidrine ret = KRB5_CC_FORMAT; 612*ae771770SStanislav Sedov krb5_set_error_message(context, ret, N_("Error reading dtag in " 613*ae771770SStanislav Sedov "cache file: %s", ""), 614*ae771770SStanislav Sedov FILENAME(id)); 6151c43270aSJacques Vidrine goto out; 6161c43270aSJacques Vidrine } 6171c43270aSJacques Vidrine ret = krb5_ret_int16 (sp, &data_len); 6181c43270aSJacques Vidrine if(ret) { 6191c43270aSJacques Vidrine ret = KRB5_CC_FORMAT; 620*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 621*ae771770SStanislav Sedov N_("Error reading dlength " 622*ae771770SStanislav Sedov "in cache file: %s",""), 623*ae771770SStanislav Sedov FILENAME(id)); 6241c43270aSJacques Vidrine goto out; 6251c43270aSJacques Vidrine } 626c19800e8SDoug Rabson switch (dtag) { 627*ae771770SStanislav Sedov case FCC_TAG_DELTATIME : { 628*ae771770SStanislav Sedov int32_t offset; 629*ae771770SStanislav Sedov 630*ae771770SStanislav Sedov ret = krb5_ret_int32 (sp, &offset); 631*ae771770SStanislav Sedov ret |= krb5_ret_int32 (sp, &context->kdc_usec_offset); 6321c43270aSJacques Vidrine if(ret) { 6331c43270aSJacques Vidrine ret = KRB5_CC_FORMAT; 634*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 635*ae771770SStanislav Sedov N_("Error reading kdc_sec in " 636*ae771770SStanislav Sedov "cache file: %s", ""), 637*ae771770SStanislav Sedov FILENAME(id)); 6381c43270aSJacques Vidrine goto out; 6391c43270aSJacques Vidrine } 640*ae771770SStanislav Sedov context->kdc_sec_offset = offset; 641*ae771770SStanislav Sedov if (kdc_offset) 642*ae771770SStanislav Sedov *kdc_offset = offset; 643b528cefcSMark Murray break; 644*ae771770SStanislav Sedov } 645b528cefcSMark Murray default : 6461c43270aSJacques Vidrine for (i = 0; i < data_len; ++i) { 6471c43270aSJacques Vidrine ret = krb5_ret_int8 (sp, &dummy); 6481c43270aSJacques Vidrine if(ret) { 6491c43270aSJacques Vidrine ret = KRB5_CC_FORMAT; 650*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 651*ae771770SStanislav Sedov N_("Error reading unknown " 652*ae771770SStanislav Sedov "tag in cache file: %s", ""), 653*ae771770SStanislav Sedov FILENAME(id)); 6541c43270aSJacques Vidrine goto out; 6551c43270aSJacques Vidrine } 6561c43270aSJacques Vidrine } 657b528cefcSMark Murray break; 658b528cefcSMark Murray } 659b528cefcSMark Murray length -= 4 + data_len; 660b528cefcSMark Murray } 661b528cefcSMark Murray break; 662b528cefcSMark Murray } 663b528cefcSMark Murray case KRB5_FCC_FVNO_3: 664b528cefcSMark Murray case KRB5_FCC_FVNO_2: 665b528cefcSMark Murray case KRB5_FCC_FVNO_1: 666b528cefcSMark Murray break; 667b528cefcSMark Murray default : 6681c43270aSJacques Vidrine ret = KRB5_CCACHE_BADVNO; 669*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 670*ae771770SStanislav Sedov N_("Unknown version number (%d) in " 671*ae771770SStanislav Sedov "credential cache file: %s", ""), 672c19800e8SDoug Rabson (int)tag, FILENAME(id)); 6731c43270aSJacques Vidrine goto out; 674b528cefcSMark Murray } 675b528cefcSMark Murray *ret_sp = sp; 676b528cefcSMark Murray *ret_fd = fd; 6771c43270aSJacques Vidrine 678b528cefcSMark Murray return 0; 6791c43270aSJacques Vidrine out: 6801c43270aSJacques Vidrine if(sp != NULL) 6811c43270aSJacques Vidrine krb5_storage_free(sp); 6821c43270aSJacques Vidrine fcc_unlock(context, fd); 6831c43270aSJacques Vidrine close(fd); 6841c43270aSJacques Vidrine return ret; 685b528cefcSMark Murray } 686b528cefcSMark Murray 687*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 688b528cefcSMark Murray fcc_get_principal(krb5_context context, 689b528cefcSMark Murray krb5_ccache id, 690b528cefcSMark Murray krb5_principal *principal) 691b528cefcSMark Murray { 692b528cefcSMark Murray krb5_error_code ret; 693b528cefcSMark Murray int fd; 694b528cefcSMark Murray krb5_storage *sp; 695b528cefcSMark Murray 696*ae771770SStanislav Sedov ret = init_fcc (context, id, &sp, &fd, NULL); 697b528cefcSMark Murray if (ret) 698b528cefcSMark Murray return ret; 6995e9cd1aeSAssar Westerlund ret = krb5_ret_principal(sp, principal); 700c19800e8SDoug Rabson if (ret) 701*ae771770SStanislav Sedov krb5_clear_error_message(context); 702b528cefcSMark Murray krb5_storage_free(sp); 7031c43270aSJacques Vidrine fcc_unlock(context, fd); 704b528cefcSMark Murray close(fd); 7055e9cd1aeSAssar Westerlund return ret; 706b528cefcSMark Murray } 707b528cefcSMark Murray 708*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 7091c43270aSJacques Vidrine fcc_end_get (krb5_context context, 7101c43270aSJacques Vidrine krb5_ccache id, 7111c43270aSJacques Vidrine krb5_cc_cursor *cursor); 7121c43270aSJacques Vidrine 713*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 714b528cefcSMark Murray fcc_get_first (krb5_context context, 715b528cefcSMark Murray krb5_ccache id, 716b528cefcSMark Murray krb5_cc_cursor *cursor) 717b528cefcSMark Murray { 718b528cefcSMark Murray krb5_error_code ret; 719b528cefcSMark Murray krb5_principal principal; 720b528cefcSMark Murray 721*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 722*ae771770SStanislav Sedov return krb5_einval(context, 2); 723*ae771770SStanislav Sedov 724b528cefcSMark Murray *cursor = malloc(sizeof(struct fcc_cursor)); 725c19800e8SDoug Rabson if (*cursor == NULL) { 726*ae771770SStanislav Sedov krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 727c19800e8SDoug Rabson return ENOMEM; 728c19800e8SDoug Rabson } 729c19800e8SDoug Rabson memset(*cursor, 0, sizeof(struct fcc_cursor)); 730b528cefcSMark Murray 7311c43270aSJacques Vidrine ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp, 732*ae771770SStanislav Sedov &FCC_CURSOR(*cursor)->fd, NULL); 7331c43270aSJacques Vidrine if (ret) { 7341c43270aSJacques Vidrine free(*cursor); 735c19800e8SDoug Rabson *cursor = NULL; 736b528cefcSMark Murray return ret; 7371c43270aSJacques Vidrine } 7381c43270aSJacques Vidrine ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 7391c43270aSJacques Vidrine if(ret) { 740*ae771770SStanislav Sedov krb5_clear_error_message(context); 7411c43270aSJacques Vidrine fcc_end_get(context, id, cursor); 7421c43270aSJacques Vidrine return ret; 7431c43270aSJacques Vidrine } 744b528cefcSMark Murray krb5_free_principal (context, principal); 7451c43270aSJacques Vidrine fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 746b528cefcSMark Murray return 0; 747b528cefcSMark Murray } 748b528cefcSMark Murray 749*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 750b528cefcSMark Murray fcc_get_next (krb5_context context, 751b528cefcSMark Murray krb5_ccache id, 752b528cefcSMark Murray krb5_cc_cursor *cursor, 753b528cefcSMark Murray krb5_creds *creds) 754b528cefcSMark Murray { 7551c43270aSJacques Vidrine krb5_error_code ret; 756*ae771770SStanislav Sedov 757*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 758*ae771770SStanislav Sedov return krb5_einval(context, 2); 759*ae771770SStanislav Sedov 760*ae771770SStanislav Sedov if (FCC_CURSOR(*cursor) == NULL) 761*ae771770SStanislav Sedov return krb5_einval(context, 3); 762*ae771770SStanislav Sedov 7631c43270aSJacques Vidrine if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0) 7641c43270aSJacques Vidrine return ret; 7651c43270aSJacques Vidrine 7661c43270aSJacques Vidrine ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); 767c19800e8SDoug Rabson if (ret) 768*ae771770SStanislav Sedov krb5_clear_error_message(context); 7691c43270aSJacques Vidrine 7701c43270aSJacques Vidrine fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 7711c43270aSJacques Vidrine return ret; 772b528cefcSMark Murray } 773b528cefcSMark Murray 774*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 775b528cefcSMark Murray fcc_end_get (krb5_context context, 776b528cefcSMark Murray krb5_ccache id, 777b528cefcSMark Murray krb5_cc_cursor *cursor) 778b528cefcSMark Murray { 779*ae771770SStanislav Sedov 780*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 781*ae771770SStanislav Sedov return krb5_einval(context, 2); 782*ae771770SStanislav Sedov 783*ae771770SStanislav Sedov if (FCC_CURSOR(*cursor) == NULL) 784*ae771770SStanislav Sedov return krb5_einval(context, 3); 785*ae771770SStanislav Sedov 786b528cefcSMark Murray krb5_storage_free(FCC_CURSOR(*cursor)->sp); 787b528cefcSMark Murray close (FCC_CURSOR(*cursor)->fd); 788b528cefcSMark Murray free(*cursor); 7891c43270aSJacques Vidrine *cursor = NULL; 790b528cefcSMark Murray return 0; 791b528cefcSMark Murray } 792b528cefcSMark Murray 793*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 794b528cefcSMark Murray fcc_remove_cred(krb5_context context, 795b528cefcSMark Murray krb5_ccache id, 796b528cefcSMark Murray krb5_flags which, 797b528cefcSMark Murray krb5_creds *cred) 798b528cefcSMark Murray { 799c19800e8SDoug Rabson krb5_error_code ret; 800*ae771770SStanislav Sedov krb5_ccache copy, newfile; 801*ae771770SStanislav Sedov char *newname = NULL; 802*ae771770SStanislav Sedov int fd; 803c19800e8SDoug Rabson 804*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 805*ae771770SStanislav Sedov return krb5_einval(context, 2); 806*ae771770SStanislav Sedov 807*ae771770SStanislav Sedov ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, ©); 808c19800e8SDoug Rabson if (ret) 809c19800e8SDoug Rabson return ret; 810c19800e8SDoug Rabson 811c19800e8SDoug Rabson ret = krb5_cc_copy_cache(context, id, copy); 812c19800e8SDoug Rabson if (ret) { 813c19800e8SDoug Rabson krb5_cc_destroy(context, copy); 814c19800e8SDoug Rabson return ret; 815c19800e8SDoug Rabson } 816c19800e8SDoug Rabson 817c19800e8SDoug Rabson ret = krb5_cc_remove_cred(context, copy, which, cred); 818c19800e8SDoug Rabson if (ret) { 819c19800e8SDoug Rabson krb5_cc_destroy(context, copy); 820c19800e8SDoug Rabson return ret; 821c19800e8SDoug Rabson } 822c19800e8SDoug Rabson 823*ae771770SStanislav Sedov ret = asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id)); 824*ae771770SStanislav Sedov if (ret < 0 || newname == NULL) { 825c19800e8SDoug Rabson krb5_cc_destroy(context, copy); 826*ae771770SStanislav Sedov return ENOMEM; 827*ae771770SStanislav Sedov } 828*ae771770SStanislav Sedov 829*ae771770SStanislav Sedov fd = mkstemp(&newname[5]); 830*ae771770SStanislav Sedov if (fd < 0) { 831*ae771770SStanislav Sedov ret = errno; 832*ae771770SStanislav Sedov krb5_cc_destroy(context, copy); 833*ae771770SStanislav Sedov return ret; 834*ae771770SStanislav Sedov } 835*ae771770SStanislav Sedov close(fd); 836*ae771770SStanislav Sedov 837*ae771770SStanislav Sedov ret = krb5_cc_resolve(context, newname, &newfile); 838*ae771770SStanislav Sedov if (ret) { 839*ae771770SStanislav Sedov unlink(&newname[5]); 840*ae771770SStanislav Sedov free(newname); 841*ae771770SStanislav Sedov krb5_cc_destroy(context, copy); 842*ae771770SStanislav Sedov return ret; 843*ae771770SStanislav Sedov } 844*ae771770SStanislav Sedov 845*ae771770SStanislav Sedov ret = krb5_cc_copy_cache(context, copy, newfile); 846*ae771770SStanislav Sedov krb5_cc_destroy(context, copy); 847*ae771770SStanislav Sedov if (ret) { 848*ae771770SStanislav Sedov free(newname); 849*ae771770SStanislav Sedov krb5_cc_destroy(context, newfile); 850*ae771770SStanislav Sedov return ret; 851*ae771770SStanislav Sedov } 852*ae771770SStanislav Sedov 853*ae771770SStanislav Sedov ret = rk_rename(&newname[5], FILENAME(id)); 854*ae771770SStanislav Sedov if (ret) 855*ae771770SStanislav Sedov ret = errno; 856*ae771770SStanislav Sedov free(newname); 857*ae771770SStanislav Sedov krb5_cc_close(context, newfile); 858c19800e8SDoug Rabson 859c19800e8SDoug Rabson return ret; 860b528cefcSMark Murray } 861b528cefcSMark Murray 862*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 863b528cefcSMark Murray fcc_set_flags(krb5_context context, 864b528cefcSMark Murray krb5_ccache id, 865b528cefcSMark Murray krb5_flags flags) 866b528cefcSMark Murray { 867*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 868*ae771770SStanislav Sedov return krb5_einval(context, 2); 869*ae771770SStanislav Sedov 870b528cefcSMark Murray return 0; /* XXX */ 871b528cefcSMark Murray } 872b528cefcSMark Murray 873*ae771770SStanislav Sedov static int KRB5_CALLCONV 874b528cefcSMark Murray fcc_get_version(krb5_context context, 875b528cefcSMark Murray krb5_ccache id) 876b528cefcSMark Murray { 877*ae771770SStanislav Sedov if (FCACHE(id) == NULL) 878*ae771770SStanislav Sedov return -1; 879*ae771770SStanislav Sedov 880b528cefcSMark Murray return FCACHE(id)->version; 881b528cefcSMark Murray } 882b528cefcSMark Murray 883c19800e8SDoug Rabson struct fcache_iter { 884c19800e8SDoug Rabson int first; 885c19800e8SDoug Rabson }; 886c19800e8SDoug Rabson 887*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 888c19800e8SDoug Rabson fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 889c19800e8SDoug Rabson { 890c19800e8SDoug Rabson struct fcache_iter *iter; 891c19800e8SDoug Rabson 892c19800e8SDoug Rabson iter = calloc(1, sizeof(*iter)); 893c19800e8SDoug Rabson if (iter == NULL) { 894*ae771770SStanislav Sedov krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 895c19800e8SDoug Rabson return ENOMEM; 896c19800e8SDoug Rabson } 897c19800e8SDoug Rabson iter->first = 1; 898c19800e8SDoug Rabson *cursor = iter; 899c19800e8SDoug Rabson return 0; 900c19800e8SDoug Rabson } 901c19800e8SDoug Rabson 902*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 903c19800e8SDoug Rabson fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 904c19800e8SDoug Rabson { 905c19800e8SDoug Rabson struct fcache_iter *iter = cursor; 906c19800e8SDoug Rabson krb5_error_code ret; 907c19800e8SDoug Rabson const char *fn; 908c19800e8SDoug Rabson char *expandedfn = NULL; 909c19800e8SDoug Rabson 910*ae771770SStanislav Sedov if (iter == NULL) 911*ae771770SStanislav Sedov return krb5_einval(context, 2); 912*ae771770SStanislav Sedov 913c19800e8SDoug Rabson if (!iter->first) { 914*ae771770SStanislav Sedov krb5_clear_error_message(context); 915c19800e8SDoug Rabson return KRB5_CC_END; 916c19800e8SDoug Rabson } 917c19800e8SDoug Rabson iter->first = 0; 918c19800e8SDoug Rabson 919c19800e8SDoug Rabson fn = krb5_cc_default_name(context); 920*ae771770SStanislav Sedov if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) { 921c19800e8SDoug Rabson ret = _krb5_expand_default_cc_name(context, 922c19800e8SDoug Rabson KRB5_DEFAULT_CCNAME_FILE, 923c19800e8SDoug Rabson &expandedfn); 924c19800e8SDoug Rabson if (ret) 925c19800e8SDoug Rabson return ret; 926*ae771770SStanislav Sedov fn = expandedfn; 927*ae771770SStanislav Sedov } 928*ae771770SStanislav Sedov /* check if file exists, don't return a non existant "next" */ 929*ae771770SStanislav Sedov if (strncasecmp(fn, "FILE:", 5) == 0) { 930*ae771770SStanislav Sedov struct stat sb; 931*ae771770SStanislav Sedov ret = stat(fn + 5, &sb); 932*ae771770SStanislav Sedov if (ret) { 933*ae771770SStanislav Sedov ret = KRB5_CC_END; 934*ae771770SStanislav Sedov goto out; 935*ae771770SStanislav Sedov } 936c19800e8SDoug Rabson } 937c19800e8SDoug Rabson ret = krb5_cc_resolve(context, fn, id); 938*ae771770SStanislav Sedov out: 939c19800e8SDoug Rabson if (expandedfn) 940c19800e8SDoug Rabson free(expandedfn); 941c19800e8SDoug Rabson 942c19800e8SDoug Rabson return ret; 943c19800e8SDoug Rabson } 944c19800e8SDoug Rabson 945*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 946c19800e8SDoug Rabson fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 947c19800e8SDoug Rabson { 948c19800e8SDoug Rabson struct fcache_iter *iter = cursor; 949*ae771770SStanislav Sedov 950*ae771770SStanislav Sedov if (iter == NULL) 951*ae771770SStanislav Sedov return krb5_einval(context, 2); 952*ae771770SStanislav Sedov 953c19800e8SDoug Rabson free(iter); 954c19800e8SDoug Rabson return 0; 955c19800e8SDoug Rabson } 956c19800e8SDoug Rabson 957*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 958c19800e8SDoug Rabson fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 959c19800e8SDoug Rabson { 960c19800e8SDoug Rabson krb5_error_code ret = 0; 961c19800e8SDoug Rabson 962*ae771770SStanislav Sedov ret = rk_rename(FILENAME(from), FILENAME(to)); 963*ae771770SStanislav Sedov 964c19800e8SDoug Rabson if (ret && errno != EXDEV) { 965*ae771770SStanislav Sedov char buf[128]; 966c19800e8SDoug Rabson ret = errno; 967*ae771770SStanislav Sedov rk_strerror_r(ret, buf, sizeof(buf)); 968*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 969*ae771770SStanislav Sedov N_("Rename of file from %s " 970*ae771770SStanislav Sedov "to %s failed: %s", ""), 971*ae771770SStanislav Sedov FILENAME(from), FILENAME(to), buf); 972c19800e8SDoug Rabson return ret; 973c19800e8SDoug Rabson } else if (ret && errno == EXDEV) { 974c19800e8SDoug Rabson /* make a copy and delete the orignal */ 975c19800e8SDoug Rabson krb5_ssize_t sz1, sz2; 976c19800e8SDoug Rabson int fd1, fd2; 977c19800e8SDoug Rabson char buf[BUFSIZ]; 978c19800e8SDoug Rabson 979*ae771770SStanislav Sedov ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 980c19800e8SDoug Rabson if(ret) 981c19800e8SDoug Rabson return ret; 982c19800e8SDoug Rabson 983c19800e8SDoug Rabson unlink(FILENAME(to)); 984c19800e8SDoug Rabson 985c19800e8SDoug Rabson ret = fcc_open(context, to, &fd2, 986*ae771770SStanislav Sedov O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 987c19800e8SDoug Rabson if(ret) 988c19800e8SDoug Rabson goto out1; 989c19800e8SDoug Rabson 990c19800e8SDoug Rabson while((sz1 = read(fd1, buf, sizeof(buf))) > 0) { 991c19800e8SDoug Rabson sz2 = write(fd2, buf, sz1); 992c19800e8SDoug Rabson if (sz1 != sz2) { 993c19800e8SDoug Rabson ret = EIO; 994*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 995*ae771770SStanislav Sedov N_("Failed to write data from one file " 996*ae771770SStanislav Sedov "credential cache to the other", "")); 997c19800e8SDoug Rabson goto out2; 998c19800e8SDoug Rabson } 999c19800e8SDoug Rabson } 1000c19800e8SDoug Rabson if (sz1 < 0) { 1001c19800e8SDoug Rabson ret = EIO; 1002*ae771770SStanislav Sedov krb5_set_error_message(context, ret, 1003*ae771770SStanislav Sedov N_("Failed to read data from one file " 1004*ae771770SStanislav Sedov "credential cache to the other", "")); 1005c19800e8SDoug Rabson goto out2; 1006c19800e8SDoug Rabson } 1007c19800e8SDoug Rabson out2: 1008c19800e8SDoug Rabson fcc_unlock(context, fd2); 1009c19800e8SDoug Rabson close(fd2); 1010c19800e8SDoug Rabson 1011c19800e8SDoug Rabson out1: 1012c19800e8SDoug Rabson fcc_unlock(context, fd1); 1013c19800e8SDoug Rabson close(fd1); 1014c19800e8SDoug Rabson 1015*ae771770SStanislav Sedov _krb5_erase_file(context, FILENAME(from)); 1016*ae771770SStanislav Sedov 1017c19800e8SDoug Rabson if (ret) { 1018*ae771770SStanislav Sedov _krb5_erase_file(context, FILENAME(to)); 1019c19800e8SDoug Rabson return ret; 1020c19800e8SDoug Rabson } 1021c19800e8SDoug Rabson } 1022c19800e8SDoug Rabson 1023c19800e8SDoug Rabson /* make sure ->version is uptodate */ 1024c19800e8SDoug Rabson { 1025c19800e8SDoug Rabson krb5_storage *sp; 1026c19800e8SDoug Rabson int fd; 1027*ae771770SStanislav Sedov if ((ret = init_fcc (context, to, &sp, &fd, NULL)) == 0) { 1028*ae771770SStanislav Sedov if (sp) 1029c19800e8SDoug Rabson krb5_storage_free(sp); 1030c19800e8SDoug Rabson fcc_unlock(context, fd); 1031c19800e8SDoug Rabson close(fd); 1032c19800e8SDoug Rabson } 1033*ae771770SStanislav Sedov } 1034*ae771770SStanislav Sedov 1035*ae771770SStanislav Sedov fcc_close(context, from); 1036*ae771770SStanislav Sedov 1037c19800e8SDoug Rabson return ret; 1038c19800e8SDoug Rabson } 1039c19800e8SDoug Rabson 1040*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 1041*ae771770SStanislav Sedov fcc_get_default_name(krb5_context context, char **str) 1042c19800e8SDoug Rabson { 1043c19800e8SDoug Rabson return _krb5_expand_default_cc_name(context, 1044c19800e8SDoug Rabson KRB5_DEFAULT_CCNAME_FILE, 1045c19800e8SDoug Rabson str); 1046c19800e8SDoug Rabson } 1047c19800e8SDoug Rabson 1048*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 1049*ae771770SStanislav Sedov fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1050*ae771770SStanislav Sedov { 1051*ae771770SStanislav Sedov krb5_error_code ret; 1052*ae771770SStanislav Sedov struct stat sb; 1053*ae771770SStanislav Sedov int fd; 1054*ae771770SStanislav Sedov 1055*ae771770SStanislav Sedov ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 1056*ae771770SStanislav Sedov if(ret) 1057*ae771770SStanislav Sedov return ret; 1058*ae771770SStanislav Sedov ret = fstat(fd, &sb); 1059*ae771770SStanislav Sedov close(fd); 1060*ae771770SStanislav Sedov if (ret) { 1061*ae771770SStanislav Sedov ret = errno; 1062*ae771770SStanislav Sedov krb5_set_error_message(context, ret, N_("Failed to stat cache file", "")); 1063*ae771770SStanislav Sedov return ret; 1064*ae771770SStanislav Sedov } 1065*ae771770SStanislav Sedov *mtime = sb.st_mtime; 1066*ae771770SStanislav Sedov return 0; 1067*ae771770SStanislav Sedov } 1068*ae771770SStanislav Sedov 1069*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 1070*ae771770SStanislav Sedov fcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1071*ae771770SStanislav Sedov { 1072*ae771770SStanislav Sedov return 0; 1073*ae771770SStanislav Sedov } 1074*ae771770SStanislav Sedov 1075*ae771770SStanislav Sedov static krb5_error_code KRB5_CALLCONV 1076*ae771770SStanislav Sedov fcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1077*ae771770SStanislav Sedov { 1078*ae771770SStanislav Sedov krb5_error_code ret; 1079*ae771770SStanislav Sedov krb5_storage *sp = NULL; 1080*ae771770SStanislav Sedov int fd; 1081*ae771770SStanislav Sedov ret = init_fcc(context, id, &sp, &fd, kdc_offset); 1082*ae771770SStanislav Sedov if (sp) 1083*ae771770SStanislav Sedov krb5_storage_free(sp); 1084*ae771770SStanislav Sedov fcc_unlock(context, fd); 1085*ae771770SStanislav Sedov close(fd); 1086*ae771770SStanislav Sedov 1087*ae771770SStanislav Sedov return ret; 1088*ae771770SStanislav Sedov } 1089*ae771770SStanislav Sedov 1090*ae771770SStanislav Sedov 1091c19800e8SDoug Rabson /** 1092c19800e8SDoug Rabson * Variable containing the FILE based credential cache implemention. 1093c19800e8SDoug Rabson * 1094c19800e8SDoug Rabson * @ingroup krb5_ccache 1095c19800e8SDoug Rabson */ 1096c19800e8SDoug Rabson 1097*ae771770SStanislav Sedov KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = { 1098*ae771770SStanislav Sedov KRB5_CC_OPS_VERSION, 1099b528cefcSMark Murray "FILE", 1100b528cefcSMark Murray fcc_get_name, 1101b528cefcSMark Murray fcc_resolve, 1102b528cefcSMark Murray fcc_gen_new, 1103b528cefcSMark Murray fcc_initialize, 1104b528cefcSMark Murray fcc_destroy, 1105b528cefcSMark Murray fcc_close, 1106b528cefcSMark Murray fcc_store_cred, 1107b528cefcSMark Murray NULL, /* fcc_retrieve */ 1108b528cefcSMark Murray fcc_get_principal, 1109b528cefcSMark Murray fcc_get_first, 1110b528cefcSMark Murray fcc_get_next, 1111b528cefcSMark Murray fcc_end_get, 1112b528cefcSMark Murray fcc_remove_cred, 1113b528cefcSMark Murray fcc_set_flags, 1114c19800e8SDoug Rabson fcc_get_version, 1115c19800e8SDoug Rabson fcc_get_cache_first, 1116c19800e8SDoug Rabson fcc_get_cache_next, 1117c19800e8SDoug Rabson fcc_end_cache_get, 1118c19800e8SDoug Rabson fcc_move, 1119*ae771770SStanislav Sedov fcc_get_default_name, 1120*ae771770SStanislav Sedov NULL, 1121*ae771770SStanislav Sedov fcc_lastchange, 1122*ae771770SStanislav Sedov fcc_set_kdc_offset, 1123*ae771770SStanislav Sedov fcc_get_kdc_offset 1124b528cefcSMark Murray }; 1125