root/source4/heimdal/lib/krb5/fcache.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fcc_get_name
  2. _krb5_xlock
  3. _krb5_xunlock
  4. write_storage
  5. fcc_lock
  6. fcc_unlock
  7. fcc_resolve
  8. scrub_file
  9. erase_file
  10. fcc_gen_new
  11. storage_set_flags
  12. fcc_open
  13. fcc_initialize
  14. fcc_close
  15. fcc_destroy
  16. fcc_store_cred
  17. init_fcc
  18. fcc_get_principal
  19. fcc_get_first
  20. fcc_get_next
  21. fcc_end_get
  22. fcc_remove_cred
  23. fcc_set_flags
  24. fcc_get_version
  25. fcc_get_cache_first
  26. fcc_get_cache_next
  27. fcc_end_cache_get
  28. fcc_move
  29. fcc_get_default_name
  30. fcc_lastchange

   1 /*
   2  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
   3  * (Royal Institute of Technology, Stockholm, Sweden).
   4  * All rights reserved.
   5  *
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  *
  10  * 1. Redistributions of source code must retain the above copyright
  11  *    notice, this list of conditions and the following disclaimer.
  12  *
  13  * 2. Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in the
  15  *    documentation and/or other materials provided with the distribution.
  16  *
  17  * 3. Neither the name of the Institute nor the names of its contributors
  18  *    may be used to endorse or promote products derived from this software
  19  *    without specific prior written permission.
  20  *
  21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31  * SUCH DAMAGE.
  32  */
  33 
  34 #include "krb5_locl.h"
  35 
  36 RCSID("$Id$");
  37 
  38 typedef struct krb5_fcache{
  39     char *filename;
  40     int version;
  41 }krb5_fcache;
  42 
  43 struct fcc_cursor {
  44     int fd;
  45     krb5_storage *sp;
  46 };
  47 
  48 #define KRB5_FCC_FVNO_1 1
  49 #define KRB5_FCC_FVNO_2 2
  50 #define KRB5_FCC_FVNO_3 3
  51 #define KRB5_FCC_FVNO_4 4
  52 
  53 #define FCC_TAG_DELTATIME 1
  54 
  55 #define FCACHE(X) ((krb5_fcache*)(X)->data.data)
  56 
  57 #define FILENAME(X) (FCACHE(X)->filename)
  58 
  59 #define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
  60 
  61 static const char*
  62 fcc_get_name(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
  63              krb5_ccache id)
  64 {
  65     return FILENAME(id);
  66 }
  67 
  68 int
  69 _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
     /* [<][>][^][v][top][bottom][index][help] */
  70             const char *filename)
  71 {
  72     int ret;
  73 #ifdef HAVE_FCNTL
  74     struct flock l;
  75 
  76     l.l_start = 0;
  77     l.l_len = 0;
  78     l.l_type = exclusive ? F_WRLCK : F_RDLCK;
  79     l.l_whence = SEEK_SET;
  80     ret = fcntl(fd, F_SETLKW, &l);
  81 #else
  82     ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH);
  83 #endif
  84     if(ret < 0)
  85         ret = errno;
  86     if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */
  87         ret = EAGAIN;
  88 
  89     switch (ret) {
  90     case 0:
  91         break;
  92     case EINVAL: /* filesystem doesn't support locking, let the user have it */
  93         ret = 0;
  94         break;
  95     case EAGAIN:
  96         krb5_set_error_message(context, ret,
  97                                N_("timed out locking cache file %s", "file"),
  98                                filename);
  99         break;
 100     default:
 101         krb5_set_error_message(context, ret,
 102                                N_("error locking cache file %s: %s",
 103                                   "file, error"),
 104                                filename, strerror(ret));
 105         break;
 106     }
 107     return ret;
 108 }
 109 
 110 int
 111 _krb5_xunlock(krb5_context context, int fd)
     /* [<][>][^][v][top][bottom][index][help] */
 112 {
 113     int ret;
 114 #ifdef HAVE_FCNTL
 115     struct flock l;
 116     l.l_start = 0;
 117     l.l_len = 0;
 118     l.l_type = F_UNLCK;
 119     l.l_whence = SEEK_SET;
 120     ret = fcntl(fd, F_SETLKW, &l);
 121 #else
 122     ret = flock(fd, LOCK_UN);
 123 #endif
 124     if (ret < 0)
 125         ret = errno;
 126     switch (ret) {
 127     case 0:
 128         break;
 129     case EINVAL: /* filesystem doesn't support locking, let the user have it */
 130         ret = 0;
 131         break;
 132     default:
 133         krb5_set_error_message(context, ret,
 134                                N_("Failed to unlock file: %s", ""),
 135                                strerror(ret));
 136         break;
 137     }
 138     return ret;
 139 }
 140 
 141 static krb5_error_code
 142 write_storage(krb5_context context, krb5_storage *sp, int fd)
     /* [<][>][^][v][top][bottom][index][help] */
 143 {
 144     krb5_error_code ret;
 145     krb5_data data;
 146     ssize_t sret;
 147 
 148     ret = krb5_storage_to_data(sp, &data);
 149     if (ret) {
 150         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 151         return ret;
 152     }
 153     sret = write(fd, data.data, data.length);
 154     ret = (sret != data.length);
 155     krb5_data_free(&data);
 156     if (ret) {
 157         ret = errno;
 158         krb5_set_error_message(context, ret,
 159                                N_("Failed to write FILE credential data", ""));
 160         return ret;
 161     }
 162     return 0;
 163 }
 164 
 165 
 166 static krb5_error_code
 167 fcc_lock(krb5_context context, krb5_ccache id,
     /* [<][>][^][v][top][bottom][index][help] */
 168          int fd, krb5_boolean exclusive)
 169 {
 170     return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
 171 }
 172 
 173 static krb5_error_code
 174 fcc_unlock(krb5_context context, int fd)
     /* [<][>][^][v][top][bottom][index][help] */
 175 {
 176     return _krb5_xunlock(context, fd);
 177 }
 178 
 179 static krb5_error_code
 180 fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
     /* [<][>][^][v][top][bottom][index][help] */
 181 {
 182     krb5_fcache *f;
 183     f = malloc(sizeof(*f));
 184     if(f == NULL) {
 185         krb5_set_error_message(context, KRB5_CC_NOMEM,
 186                                N_("malloc: out of memory", ""));
 187         return KRB5_CC_NOMEM;
 188     }
 189     f->filename = strdup(res);
 190     if(f->filename == NULL){
 191         free(f);
 192         krb5_set_error_message(context, KRB5_CC_NOMEM,
 193                                N_("malloc: out of memory", ""));
 194         return KRB5_CC_NOMEM;
 195     }
 196     f->version = 0;
 197     (*id)->data.data = f;
 198     (*id)->data.length = sizeof(*f);
 199     return 0;
 200 }
 201 
 202 /*
 203  * Try to scrub the contents of `filename' safely.
 204  */
 205 
 206 static int
 207 scrub_file (int fd)
     /* [<][>][^][v][top][bottom][index][help] */
 208 {
 209     off_t pos;
 210     char buf[128];
 211 
 212     pos = lseek(fd, 0, SEEK_END);
 213     if (pos < 0)
 214         return errno;
 215     if (lseek(fd, 0, SEEK_SET) < 0)
 216         return errno;
 217     memset(buf, 0, sizeof(buf));
 218     while(pos > 0) {
 219         ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
 220 
 221         if (tmp < 0)
 222             return errno;
 223         pos -= tmp;
 224     }
 225     fsync (fd);
 226     return 0;
 227 }
 228 
 229 /*
 230  * Erase `filename' if it exists, trying to remove the contents if
 231  * it's `safe'.  We always try to remove the file, it it exists.  It's
 232  * only overwritten if it's a regular file (not a symlink and not a
 233  * hardlink)
 234  */
 235 
 236 static krb5_error_code
 237 erase_file(krb5_context context, const char *filename)
     /* [<][>][^][v][top][bottom][index][help] */
 238 {
 239     int fd;
 240     struct stat sb1, sb2;
 241     int ret;
 242 
 243     ret = lstat (filename, &sb1);
 244     if (ret < 0)
 245         return errno;
 246 
 247     fd = open(filename, O_RDWR | O_BINARY);
 248     if(fd < 0) {
 249         if(errno == ENOENT)
 250             return 0;
 251         else
 252             return errno;
 253     }
 254     rk_cloexec(fd);
 255     ret = _krb5_xlock(context, fd, 1, filename);
 256     if (ret) {
 257         close(fd);
 258         return ret;
 259     }
 260     if (unlink(filename) < 0) {
 261         _krb5_xunlock(context, fd);
 262         close (fd);
 263         return errno;
 264     }
 265     ret = fstat (fd, &sb2);
 266     if (ret < 0) {
 267         _krb5_xunlock(context, fd);
 268         close (fd);
 269         return errno;
 270     }
 271 
 272     /* check if someone was playing with symlinks */
 273 
 274     if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
 275         _krb5_xunlock(context, fd);
 276         close (fd);
 277         return EPERM;
 278     }
 279 
 280     /* there are still hard links to this file */
 281 
 282     if (sb2.st_nlink != 0) {
 283         _krb5_xunlock(context, fd);
 284         close (fd);
 285         return 0;
 286     }
 287 
 288     ret = scrub_file (fd);
 289     if (ret) {
 290         _krb5_xunlock(context, fd);
 291         close(fd);
 292         return ret;
 293     }
 294     ret = _krb5_xunlock(context, fd);
 295     close (fd);
 296     return ret;
 297 }
 298 
 299 static krb5_error_code
 300 fcc_gen_new(krb5_context context, krb5_ccache *id)
     /* [<][>][^][v][top][bottom][index][help] */
 301 {
 302     krb5_fcache *f;
 303     int fd;
 304     char *file;
 305 
 306     f = malloc(sizeof(*f));
 307     if(f == NULL) {
 308         krb5_set_error_message(context, KRB5_CC_NOMEM,
 309                                N_("malloc: out of memory", ""));
 310         return KRB5_CC_NOMEM;
 311     }
 312     asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
 313     if(file == NULL) {
 314         free(f);
 315         krb5_set_error_message(context, KRB5_CC_NOMEM,
 316                                N_("malloc: out of memory", ""));
 317         return KRB5_CC_NOMEM;
 318     }
 319     fd = mkstemp(file);
 320     if(fd < 0) {
 321         int ret = errno;
 322         krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), file);
 323         free(f);
 324         free(file);
 325         return ret;
 326     }
 327     close(fd);
 328     f->filename = file;
 329     f->version = 0;
 330     (*id)->data.data = f;
 331     (*id)->data.length = sizeof(*f);
 332     return 0;
 333 }
 334 
 335 static void
 336 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
     /* [<][>][^][v][top][bottom][index][help] */
 337 {
 338     int flags = 0;
 339     switch(vno) {
 340     case KRB5_FCC_FVNO_1:
 341         flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
 342         flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
 343         flags |= KRB5_STORAGE_HOST_BYTEORDER;
 344         break;
 345     case KRB5_FCC_FVNO_2:
 346         flags |= KRB5_STORAGE_HOST_BYTEORDER;
 347         break;
 348     case KRB5_FCC_FVNO_3:
 349         flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
 350         break;
 351     case KRB5_FCC_FVNO_4:
 352         break;
 353     default:
 354         krb5_abortx(context,
 355                     "storage_set_flags called with bad vno (%x)", vno);
 356     }
 357     krb5_storage_set_flags(sp, flags);
 358 }
 359 
 360 static krb5_error_code
 361 fcc_open(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 362          krb5_ccache id,
 363          int *fd_ret,
 364          int flags,
 365          mode_t mode)
 366 {
 367     krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
 368                               (flags | O_RDWR) == flags);
 369     krb5_error_code ret;
 370     const char *filename = FILENAME(id);
 371     int fd;
 372     fd = open(filename, flags, mode);
 373     if(fd < 0) {
 374         ret = errno;
 375         krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"),
 376                                filename, strerror(ret));
 377         return ret;
 378     }
 379     rk_cloexec(fd);
 380 
 381     if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
 382         close(fd);
 383         return ret;
 384     }
 385     *fd_ret = fd;
 386     return 0;
 387 }
 388 
 389 static krb5_error_code
 390 fcc_initialize(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 391                krb5_ccache id,
 392                krb5_principal primary_principal)
 393 {
 394     krb5_fcache *f = FCACHE(id);
 395     int ret = 0;
 396     int fd;
 397     char *filename = f->filename;
 398 
 399     unlink (filename);
 400 
 401     ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
 402     if(ret)
 403         return ret;
 404     {
 405         krb5_storage *sp;
 406         sp = krb5_storage_emem();
 407         krb5_storage_set_eof_code(sp, KRB5_CC_END);
 408         if(context->fcache_vno != 0)
 409             f->version = context->fcache_vno;
 410         else
 411             f->version = KRB5_FCC_FVNO_4;
 412         ret |= krb5_store_int8(sp, 5);
 413         ret |= krb5_store_int8(sp, f->version);
 414         storage_set_flags(context, sp, f->version);
 415         if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
 416             /* V4 stuff */
 417             if (context->kdc_sec_offset) {
 418                 ret |= krb5_store_int16 (sp, 12); /* length */
 419                 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
 420                 ret |= krb5_store_int16 (sp, 8); /* length of data */
 421                 ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
 422                 ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
 423             } else {
 424                 ret |= krb5_store_int16 (sp, 0);
 425             }
 426         }
 427         ret |= krb5_store_principal(sp, primary_principal);
 428         
 429         ret |= write_storage(context, sp, fd);
 430 
 431         krb5_storage_free(sp);
 432     }
 433     fcc_unlock(context, fd);
 434     if (close(fd) < 0)
 435         if (ret == 0) {
 436             ret = errno;
 437             krb5_set_error_message (context, ret, N_("close %s: %s", ""),
 438                                     FILENAME(id), strerror(ret));
 439         }
 440     return ret;
 441 }
 442 
 443 static krb5_error_code
 444 fcc_close(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 445           krb5_ccache id)
 446 {
 447     free (FILENAME(id));
 448     krb5_data_free(&id->data);
 449     return 0;
 450 }
 451 
 452 static krb5_error_code
 453 fcc_destroy(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 454             krb5_ccache id)
 455 {
 456     erase_file(context, FILENAME(id));
 457     return 0;
 458 }
 459 
 460 static krb5_error_code
 461 fcc_store_cred(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 462                krb5_ccache id,
 463                krb5_creds *creds)
 464 {
 465     int ret;
 466     int fd;
 467 
 468     ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0);
 469     if(ret)
 470         return ret;
 471     {
 472         krb5_storage *sp;
 473 
 474         sp = krb5_storage_emem();
 475         krb5_storage_set_eof_code(sp, KRB5_CC_END);
 476         storage_set_flags(context, sp, FCACHE(id)->version);
 477         if (!krb5_config_get_bool_default(context, NULL, TRUE,
 478                                           "libdefaults",
 479                                           "fcc-mit-ticketflags",
 480                                           NULL))
 481             krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
 482         ret = krb5_store_creds(sp, creds);
 483         if (ret == 0)
 484             ret = write_storage(context, sp, fd);
 485         krb5_storage_free(sp);
 486     }
 487     fcc_unlock(context, fd);
 488     if (close(fd) < 0) {
 489         if (ret == 0) {
 490             ret = errno;
 491             krb5_set_error_message (context, ret, N_("close %s: %s", ""),
 492                                     FILENAME(id), strerror(ret));
 493         }
 494     }
 495     return ret;
 496 }
 497 
 498 static krb5_error_code
 499 init_fcc (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 500           krb5_ccache id,
 501           krb5_storage **ret_sp,
 502           int *ret_fd)
 503 {
 504     int fd;
 505     int8_t pvno, tag;
 506     krb5_storage *sp;
 507     krb5_error_code ret;
 508 
 509     ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
 510     if(ret)
 511         return ret;
 512 
 513     sp = krb5_storage_from_fd(fd);
 514     if(sp == NULL) {
 515         krb5_clear_error_message(context);
 516         ret = ENOMEM;
 517         goto out;
 518     }
 519     krb5_storage_set_eof_code(sp, KRB5_CC_END);
 520     ret = krb5_ret_int8(sp, &pvno);
 521     if(ret != 0) {
 522         if(ret == KRB5_CC_END) {
 523             ret = ENOENT;
 524             krb5_set_error_message(context, ret,
 525                                    N_("Empty credential cache file: %s", ""),
 526                                    FILENAME(id));
 527         } else
 528             krb5_set_error_message(context, ret, N_("Error reading pvno "
 529                                                     "in cache file: %s", ""),
 530                                    FILENAME(id));
 531         goto out;
 532     }
 533     if(pvno != 5) {
 534         ret = KRB5_CCACHE_BADVNO;
 535         krb5_set_error_message(context, ret, N_("Bad version number in credential "
 536                                                 "cache file: %s", ""),
 537                                FILENAME(id));
 538         goto out;
 539     }
 540     ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
 541     if(ret != 0) {
 542         ret = KRB5_CC_FORMAT;
 543         krb5_set_error_message(context, ret, "Error reading tag in "
 544                               "cache file: %s", FILENAME(id));
 545         goto out;
 546     }
 547     FCACHE(id)->version = tag;
 548     storage_set_flags(context, sp, FCACHE(id)->version);
 549     switch (tag) {
 550     case KRB5_FCC_FVNO_4: {
 551         int16_t length;
 552 
 553         ret = krb5_ret_int16 (sp, &length);
 554         if(ret) {
 555             ret = KRB5_CC_FORMAT;
 556             krb5_set_error_message(context, ret,
 557                                    N_("Error reading tag length in "
 558                                       "cache file: %s", ""), FILENAME(id));
 559             goto out;
 560         }
 561         while(length > 0) {
 562             int16_t dtag, data_len;
 563             int i;
 564             int8_t dummy;
 565 
 566             ret = krb5_ret_int16 (sp, &dtag);
 567             if(ret) {
 568                 ret = KRB5_CC_FORMAT;
 569                 krb5_set_error_message(context, ret, N_("Error reading dtag in "
 570                                                         "cache file: %s", ""),
 571                                        FILENAME(id));
 572                 goto out;
 573             }
 574             ret = krb5_ret_int16 (sp, &data_len);
 575             if(ret) {
 576                 ret = KRB5_CC_FORMAT;
 577                 krb5_set_error_message(context, ret,
 578                                        N_("Error reading dlength "
 579                                           "in cache file: %s",""),
 580                                        FILENAME(id));
 581                 goto out;
 582             }
 583             switch (dtag) {
 584             case FCC_TAG_DELTATIME :
 585                 ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
 586                 if(ret) {
 587                     ret = KRB5_CC_FORMAT;
 588                     krb5_set_error_message(context, ret,
 589                                            N_("Error reading kdc_sec in "
 590                                               "cache file: %s", ""),
 591                                            FILENAME(id));
 592                     goto out;
 593                 }
 594                 ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
 595                 if(ret) {
 596                     ret = KRB5_CC_FORMAT;
 597                     krb5_set_error_message(context, ret,
 598                                            N_("Error reading kdc_usec in "
 599                                               "cache file: %s", ""),
 600                                            FILENAME(id));
 601                     goto out;
 602                 }
 603                 break;
 604             default :
 605                 for (i = 0; i < data_len; ++i) {
 606                     ret = krb5_ret_int8 (sp, &dummy);
 607                     if(ret) {
 608                         ret = KRB5_CC_FORMAT;
 609                         krb5_set_error_message(context, ret,
 610                                                N_("Error reading unknown "
 611                                                   "tag in cache file: %s", ""),
 612                                                FILENAME(id));
 613                         goto out;
 614                     }
 615                 }
 616                 break;
 617             }
 618             length -= 4 + data_len;
 619         }
 620         break;
 621     }
 622     case KRB5_FCC_FVNO_3:
 623     case KRB5_FCC_FVNO_2:
 624     case KRB5_FCC_FVNO_1:
 625         break;
 626     default :
 627         ret = KRB5_CCACHE_BADVNO;
 628         krb5_set_error_message(context, ret,
 629                                N_("Unknown version number (%d) in "
 630                                   "credential cache file: %s", ""),
 631                                (int)tag, FILENAME(id));
 632         goto out;
 633     }
 634     *ret_sp = sp;
 635     *ret_fd = fd;
 636 
 637     return 0;
 638   out:
 639     if(sp != NULL)
 640         krb5_storage_free(sp);
 641     fcc_unlock(context, fd);
 642     close(fd);
 643     return ret;
 644 }
 645 
 646 static krb5_error_code
 647 fcc_get_principal(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 648                   krb5_ccache id,
 649                   krb5_principal *principal)
 650 {
 651     krb5_error_code ret;
 652     int fd;
 653     krb5_storage *sp;
 654 
 655     ret = init_fcc (context, id, &sp, &fd);
 656     if (ret)
 657         return ret;
 658     ret = krb5_ret_principal(sp, principal);
 659     if (ret)
 660         krb5_clear_error_message(context);
 661     krb5_storage_free(sp);
 662     fcc_unlock(context, fd);
 663     close(fd);
 664     return ret;
 665 }
 666 
 667 static krb5_error_code
 668 fcc_end_get (krb5_context context,
 669              krb5_ccache id,
 670              krb5_cc_cursor *cursor);
 671 
 672 static krb5_error_code
 673 fcc_get_first (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 674                krb5_ccache id,
 675                krb5_cc_cursor *cursor)
 676 {
 677     krb5_error_code ret;
 678     krb5_principal principal;
 679 
 680     *cursor = malloc(sizeof(struct fcc_cursor));
 681     if (*cursor == NULL) {
 682         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 683         return ENOMEM;
 684     }
 685     memset(*cursor, 0, sizeof(struct fcc_cursor));
 686 
 687     ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
 688                     &FCC_CURSOR(*cursor)->fd);
 689     if (ret) {
 690         free(*cursor);
 691         *cursor = NULL;
 692         return ret;
 693     }
 694     ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
 695     if(ret) {
 696         krb5_clear_error_message(context);
 697         fcc_end_get(context, id, cursor);
 698         return ret;
 699     }
 700     krb5_free_principal (context, principal);
 701     fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
 702     return 0;
 703 }
 704 
 705 static krb5_error_code
 706 fcc_get_next (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 707               krb5_ccache id,
 708               krb5_cc_cursor *cursor,
 709               krb5_creds *creds)
 710 {
 711     krb5_error_code ret;
 712     if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
 713         return ret;
 714 
 715     ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
 716     if (ret)
 717         krb5_clear_error_message(context);
 718 
 719     fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
 720     return ret;
 721 }
 722 
 723 static krb5_error_code
 724 fcc_end_get (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 725              krb5_ccache id,
 726              krb5_cc_cursor *cursor)
 727 {
 728     krb5_storage_free(FCC_CURSOR(*cursor)->sp);
 729     close (FCC_CURSOR(*cursor)->fd);
 730     free(*cursor);
 731     *cursor = NULL;
 732     return 0;
 733 }
 734 
 735 static krb5_error_code
 736 fcc_remove_cred(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 737                  krb5_ccache id,
 738                  krb5_flags which,
 739                  krb5_creds *cred)
 740 {
 741     krb5_error_code ret;
 742     krb5_ccache copy, newfile;
 743 
 744     ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &copy);
 745     if (ret)
 746         return ret;
 747 
 748     ret = krb5_cc_copy_cache(context, id, copy);
 749     if (ret) {
 750         krb5_cc_destroy(context, copy);
 751         return ret;
 752     }
 753 
 754     ret = krb5_cc_remove_cred(context, copy, which, cred);
 755     if (ret) {
 756         krb5_cc_destroy(context, copy);
 757         return ret;
 758     }
 759 
 760     ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &newfile);
 761     if (ret) {
 762         krb5_cc_destroy(context, copy);
 763         return ret;
 764     }
 765 
 766     ret = krb5_cc_copy_cache(context, copy, newfile);
 767     krb5_cc_destroy(context, copy);
 768     if (ret) {
 769         krb5_cc_destroy(context, newfile);
 770         return ret;
 771     }
 772 
 773     return krb5_cc_move(context, newfile, id);
 774 }
 775 
 776 static krb5_error_code
 777 fcc_set_flags(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 778               krb5_ccache id,
 779               krb5_flags flags)
 780 {
 781     return 0; /* XXX */
 782 }
 783 
 784 static int
 785 fcc_get_version(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 786                 krb5_ccache id)
 787 {
 788     return FCACHE(id)->version;
 789 }
 790                 
 791 struct fcache_iter {
 792     int first;
 793 };
 794 
 795 static krb5_error_code
 796 fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
     /* [<][>][^][v][top][bottom][index][help] */
 797 {
 798     struct fcache_iter *iter;
 799 
 800     iter = calloc(1, sizeof(*iter));
 801     if (iter == NULL) {
 802         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 803         return ENOMEM;
 804     }
 805     iter->first = 1;
 806     *cursor = iter;
 807     return 0;
 808 }
 809 
 810 static krb5_error_code
 811 fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
     /* [<][>][^][v][top][bottom][index][help] */
 812 {
 813     struct fcache_iter *iter = cursor;
 814     krb5_error_code ret;
 815     const char *fn;
 816     char *expandedfn = NULL;
 817 
 818     if (!iter->first) {
 819         krb5_clear_error_message(context);
 820         return KRB5_CC_END;
 821     }
 822     iter->first = 0;
 823 
 824     fn = krb5_cc_default_name(context);
 825     if (strncasecmp(fn, "FILE:", 5) != 0) {
 826         ret = _krb5_expand_default_cc_name(context,
 827                                            KRB5_DEFAULT_CCNAME_FILE,
 828                                            &expandedfn);
 829         if (ret)
 830             return ret;
 831     }
 832     ret = krb5_cc_resolve(context, fn, id);
 833     if (expandedfn)
 834         free(expandedfn);
 835 
 836     return ret;
 837 }
 838 
 839 static krb5_error_code
 840 fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
     /* [<][>][^][v][top][bottom][index][help] */
 841 {
 842     struct fcache_iter *iter = cursor;
 843     free(iter);
 844     return 0;
 845 }
 846 
 847 static krb5_error_code
 848 fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
     /* [<][>][^][v][top][bottom][index][help] */
 849 {
 850     krb5_error_code ret = 0;
 851 
 852     ret = rename(FILENAME(from), FILENAME(to));
 853     if (ret && errno != EXDEV) {
 854         ret = errno;
 855         krb5_set_error_message(context, ret,
 856                                N_("Rename of file from %s "
 857                                   "to %s failed: %s", ""),
 858                                FILENAME(from), FILENAME(to),
 859                                strerror(ret));
 860         return ret;
 861     } else if (ret && errno == EXDEV) {
 862         /* make a copy and delete the orignal */
 863         krb5_ssize_t sz1, sz2;
 864         int fd1, fd2;
 865         char buf[BUFSIZ];
 866 
 867         ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
 868         if(ret)
 869             return ret;
 870 
 871         unlink(FILENAME(to));
 872 
 873         ret = fcc_open(context, to, &fd2,
 874                        O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
 875         if(ret)
 876             goto out1;
 877 
 878         while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
 879             sz2 = write(fd2, buf, sz1);
 880             if (sz1 != sz2) {
 881                 ret = EIO;
 882                 krb5_set_error_message(context, ret,
 883                                        N_("Failed to write data from one file "
 884                                           "credential cache to the other", ""));
 885                 goto out2;
 886             }
 887         }
 888         if (sz1 < 0) {
 889             ret = EIO;
 890             krb5_set_error_message(context, ret,
 891                                    N_("Failed to read data from one file "
 892                                       "credential cache to the other", ""));
 893             goto out2;
 894         }
 895     out2:
 896         fcc_unlock(context, fd2);
 897         close(fd2);
 898 
 899     out1:
 900         fcc_unlock(context, fd1);
 901         close(fd1);
 902 
 903         erase_file(context, FILENAME(from));
 904 
 905         if (ret) {
 906             erase_file(context, FILENAME(to));
 907             return ret;
 908         }
 909     }
 910 
 911     /* make sure ->version is uptodate */
 912     {
 913         krb5_storage *sp;
 914         int fd;
 915         ret = init_fcc (context, to, &sp, &fd);
 916         krb5_storage_free(sp);
 917         fcc_unlock(context, fd);
 918         close(fd);
 919     }
 920     return ret;
 921 }
 922 
 923 static krb5_error_code
 924 fcc_get_default_name(krb5_context context, char **str)
     /* [<][>][^][v][top][bottom][index][help] */
 925 {
 926     return _krb5_expand_default_cc_name(context,
 927                                         KRB5_DEFAULT_CCNAME_FILE,
 928                                         str);
 929 }
 930 
 931 static krb5_error_code
 932 fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
     /* [<][>][^][v][top][bottom][index][help] */
 933 {
 934     krb5_error_code ret;
 935     struct stat sb;
 936     int fd;
 937 
 938     ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
 939     if(ret)
 940         return ret;
 941     ret = fstat(fd, &sb);
 942     close(fd);
 943     if (ret) {
 944         ret = errno;
 945         krb5_set_error_message(context, ret, N_("Failed to stat cache file", ""));
 946         return ret;
 947     }
 948     *mtime = sb.st_mtime;
 949     return 0;
 950 }
 951 
 952 /**
 953  * Variable containing the FILE based credential cache implemention.
 954  *
 955  * @ingroup krb5_ccache
 956  */
 957 
 958 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = {
 959     KRB5_CC_OPS_VERSION,
 960     "FILE",
 961     fcc_get_name,
 962     fcc_resolve,
 963     fcc_gen_new,
 964     fcc_initialize,
 965     fcc_destroy,
 966     fcc_close,
 967     fcc_store_cred,
 968     NULL, /* fcc_retrieve */
 969     fcc_get_principal,
 970     fcc_get_first,
 971     fcc_get_next,
 972     fcc_end_get,
 973     fcc_remove_cred,
 974     fcc_set_flags,
 975     fcc_get_version,
 976     fcc_get_cache_first,
 977     fcc_get_cache_next,
 978     fcc_end_cache_get,
 979     fcc_move,
 980     fcc_get_default_name,
 981     NULL,
 982     fcc_lastchange
 983 };

/* [<][>][^][v][top][bottom][index][help] */