LCOV - code coverage report
Current view: top level - source4/heimdal/lib/krb5 - fcache.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 314 591 53.1 %
Date: 2021-09-23 10:06:22 Functions: 27 32 84.4 %

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

Generated by: LCOV version 1.13