xref: /freebsd/crypto/heimdal/lib/krb5/fcache.c (revision c19800e8cd5640693f36f2040db4ab5e8d738146)
1b528cefcSMark Murray /*
2c19800e8SDoug Rabson  * Copyright (c) 1997 - 2008 Kungliga Tekniska H�gskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #include "krb5_locl.h"
35b528cefcSMark Murray 
36c19800e8SDoug Rabson RCSID("$Id: fcache.c 22522 2008-01-24 11:56:25Z lha $");
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 
618373020dSJacques Vidrine static const char*
62b528cefcSMark Murray fcc_get_name(krb5_context context,
63b528cefcSMark Murray 	     krb5_ccache id)
64b528cefcSMark Murray {
65b528cefcSMark Murray     return FILENAME(id);
66b528cefcSMark Murray }
67b528cefcSMark Murray 
681c43270aSJacques Vidrine int
691c43270aSJacques Vidrine _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
701c43270aSJacques Vidrine 	    const char *filename)
711c43270aSJacques Vidrine {
721c43270aSJacques Vidrine     int ret;
731c43270aSJacques Vidrine #ifdef HAVE_FCNTL
741c43270aSJacques Vidrine     struct flock l;
751c43270aSJacques Vidrine 
761c43270aSJacques Vidrine     l.l_start = 0;
771c43270aSJacques Vidrine     l.l_len = 0;
781c43270aSJacques Vidrine     l.l_type = exclusive ? F_WRLCK : F_RDLCK;
791c43270aSJacques Vidrine     l.l_whence = SEEK_SET;
801c43270aSJacques Vidrine     ret = fcntl(fd, F_SETLKW, &l);
811c43270aSJacques Vidrine #else
821c43270aSJacques Vidrine     ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH);
831c43270aSJacques Vidrine #endif
841c43270aSJacques Vidrine     if(ret < 0)
851c43270aSJacques Vidrine 	ret = errno;
861c43270aSJacques Vidrine     if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */
871c43270aSJacques Vidrine 	ret = EAGAIN;
881c43270aSJacques Vidrine 
891c43270aSJacques Vidrine     switch (ret) {
901c43270aSJacques Vidrine     case 0:
911c43270aSJacques Vidrine 	break;
921c43270aSJacques Vidrine     case EINVAL: /* filesystem doesn't support locking, let the user have it */
931c43270aSJacques Vidrine 	ret = 0;
941c43270aSJacques Vidrine 	break;
951c43270aSJacques Vidrine     case EAGAIN:
961c43270aSJacques Vidrine 	krb5_set_error_string(context, "timed out locking cache file %s",
971c43270aSJacques Vidrine 			      filename);
981c43270aSJacques Vidrine 	break;
991c43270aSJacques Vidrine     default:
1001c43270aSJacques Vidrine 	krb5_set_error_string(context, "error locking cache file %s: %s",
1011c43270aSJacques Vidrine 			      filename, strerror(ret));
1021c43270aSJacques Vidrine 	break;
1031c43270aSJacques Vidrine     }
1041c43270aSJacques Vidrine     return ret;
1051c43270aSJacques Vidrine }
1061c43270aSJacques Vidrine 
1071c43270aSJacques Vidrine int
108c19800e8SDoug Rabson _krb5_xunlock(krb5_context context, int fd)
1091c43270aSJacques Vidrine {
110c19800e8SDoug Rabson     int ret;
111c19800e8SDoug Rabson #ifdef HAVE_FCNTL
1121c43270aSJacques Vidrine     struct flock l;
1131c43270aSJacques Vidrine     l.l_start = 0;
1141c43270aSJacques Vidrine     l.l_len = 0;
1151c43270aSJacques Vidrine     l.l_type = F_UNLCK;
1161c43270aSJacques Vidrine     l.l_whence = SEEK_SET;
117c19800e8SDoug Rabson     ret = fcntl(fd, F_SETLKW, &l);
1181c43270aSJacques Vidrine #else
119c19800e8SDoug Rabson     ret = flock(fd, LOCK_UN);
1201c43270aSJacques Vidrine #endif
121c19800e8SDoug Rabson     if (ret < 0)
122c19800e8SDoug Rabson 	ret = errno;
123c19800e8SDoug Rabson     switch (ret) {
124c19800e8SDoug Rabson     case 0:
125c19800e8SDoug Rabson 	break;
126c19800e8SDoug Rabson     case EINVAL: /* filesystem doesn't support locking, let the user have it */
127c19800e8SDoug Rabson 	ret = 0;
128c19800e8SDoug Rabson 	break;
129c19800e8SDoug Rabson     default:
130c19800e8SDoug Rabson 	krb5_set_error_string(context,
131c19800e8SDoug Rabson 			      "Failed to unlock file: %s", strerror(ret));
132c19800e8SDoug Rabson 	break;
133c19800e8SDoug Rabson     }
134c19800e8SDoug Rabson     return ret;
1351c43270aSJacques Vidrine }
1361c43270aSJacques Vidrine 
1371c43270aSJacques Vidrine static krb5_error_code
1381c43270aSJacques Vidrine fcc_lock(krb5_context context, krb5_ccache id,
1391c43270aSJacques Vidrine 	 int fd, krb5_boolean exclusive)
1401c43270aSJacques Vidrine {
1411c43270aSJacques Vidrine     return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
1421c43270aSJacques Vidrine }
1431c43270aSJacques Vidrine 
1441c43270aSJacques Vidrine static krb5_error_code
1451c43270aSJacques Vidrine fcc_unlock(krb5_context context, int fd)
1461c43270aSJacques Vidrine {
147c19800e8SDoug Rabson     return _krb5_xunlock(context, fd);
1481c43270aSJacques Vidrine }
1491c43270aSJacques Vidrine 
150b528cefcSMark Murray static krb5_error_code
151b528cefcSMark Murray fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
152b528cefcSMark Murray {
153b528cefcSMark Murray     krb5_fcache *f;
154b528cefcSMark Murray     f = malloc(sizeof(*f));
155adb0ddaeSAssar Westerlund     if(f == NULL) {
156adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
157b528cefcSMark Murray 	return KRB5_CC_NOMEM;
158adb0ddaeSAssar Westerlund     }
159b528cefcSMark Murray     f->filename = strdup(res);
160b528cefcSMark Murray     if(f->filename == NULL){
161b528cefcSMark Murray 	free(f);
162adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
163b528cefcSMark Murray 	return KRB5_CC_NOMEM;
164b528cefcSMark Murray     }
165b528cefcSMark Murray     f->version = 0;
166b528cefcSMark Murray     (*id)->data.data = f;
167b528cefcSMark Murray     (*id)->data.length = sizeof(*f);
168b528cefcSMark Murray     return 0;
169b528cefcSMark Murray }
170b528cefcSMark Murray 
1715e9cd1aeSAssar Westerlund /*
1725e9cd1aeSAssar Westerlund  * Try to scrub the contents of `filename' safely.
1735e9cd1aeSAssar Westerlund  */
1745e9cd1aeSAssar Westerlund 
1755e9cd1aeSAssar Westerlund static int
1765e9cd1aeSAssar Westerlund scrub_file (int fd)
1775e9cd1aeSAssar Westerlund {
1785e9cd1aeSAssar Westerlund     off_t pos;
1795e9cd1aeSAssar Westerlund     char buf[128];
1805e9cd1aeSAssar Westerlund 
1815e9cd1aeSAssar Westerlund     pos = lseek(fd, 0, SEEK_END);
1825e9cd1aeSAssar Westerlund     if (pos < 0)
1835e9cd1aeSAssar Westerlund         return errno;
1845e9cd1aeSAssar Westerlund     if (lseek(fd, 0, SEEK_SET) < 0)
1855e9cd1aeSAssar Westerlund         return errno;
1865e9cd1aeSAssar Westerlund     memset(buf, 0, sizeof(buf));
1875e9cd1aeSAssar Westerlund     while(pos > 0) {
1885e9cd1aeSAssar Westerlund         ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
1895e9cd1aeSAssar Westerlund 
1905e9cd1aeSAssar Westerlund 	if (tmp < 0)
1915e9cd1aeSAssar Westerlund 	    return errno;
1925e9cd1aeSAssar Westerlund 	pos -= tmp;
1935e9cd1aeSAssar Westerlund     }
1945e9cd1aeSAssar Westerlund     fsync (fd);
1955e9cd1aeSAssar Westerlund     return 0;
1965e9cd1aeSAssar Westerlund }
1975e9cd1aeSAssar Westerlund 
1985e9cd1aeSAssar Westerlund /*
1995e9cd1aeSAssar Westerlund  * Erase `filename' if it exists, trying to remove the contents if
2005e9cd1aeSAssar Westerlund  * it's `safe'.  We always try to remove the file, it it exists.  It's
2015e9cd1aeSAssar Westerlund  * only overwritten if it's a regular file (not a symlink and not a
2025e9cd1aeSAssar Westerlund  * hardlink)
2035e9cd1aeSAssar Westerlund  */
2045e9cd1aeSAssar Westerlund 
205b528cefcSMark Murray static krb5_error_code
206b528cefcSMark Murray erase_file(const char *filename)
207b528cefcSMark Murray {
208b528cefcSMark Murray     int fd;
2095e9cd1aeSAssar Westerlund     struct stat sb1, sb2;
2105e9cd1aeSAssar Westerlund     int ret;
2115e9cd1aeSAssar Westerlund 
2125e9cd1aeSAssar Westerlund     ret = lstat (filename, &sb1);
2135e9cd1aeSAssar Westerlund     if (ret < 0)
2145e9cd1aeSAssar Westerlund 	return errno;
215b528cefcSMark Murray 
216b528cefcSMark Murray     fd = open(filename, O_RDWR | O_BINARY);
217b528cefcSMark Murray     if(fd < 0) {
218b528cefcSMark Murray 	if(errno == ENOENT)
219b528cefcSMark Murray 	    return 0;
220b528cefcSMark Murray 	else
221b528cefcSMark Murray 	    return errno;
222b528cefcSMark Murray     }
2235e9cd1aeSAssar Westerlund     if (unlink(filename) < 0) {
224b528cefcSMark Murray         close (fd);
2255e9cd1aeSAssar Westerlund         return errno;
2265e9cd1aeSAssar Westerlund     }
2275e9cd1aeSAssar Westerlund     ret = fstat (fd, &sb2);
2285e9cd1aeSAssar Westerlund     if (ret < 0) {
2295e9cd1aeSAssar Westerlund 	close (fd);
2305e9cd1aeSAssar Westerlund 	return errno;
2315e9cd1aeSAssar Westerlund     }
2325e9cd1aeSAssar Westerlund 
2335e9cd1aeSAssar Westerlund     /* check if someone was playing with symlinks */
2345e9cd1aeSAssar Westerlund 
2355e9cd1aeSAssar Westerlund     if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
2365e9cd1aeSAssar Westerlund 	close (fd);
2375e9cd1aeSAssar Westerlund 	return EPERM;
2385e9cd1aeSAssar Westerlund     }
2395e9cd1aeSAssar Westerlund 
2405e9cd1aeSAssar Westerlund     /* there are still hard links to this file */
2415e9cd1aeSAssar Westerlund 
2425e9cd1aeSAssar Westerlund     if (sb2.st_nlink != 0) {
2435e9cd1aeSAssar Westerlund         close (fd);
244b528cefcSMark Murray         return 0;
245b528cefcSMark Murray     }
246b528cefcSMark Murray 
2475e9cd1aeSAssar Westerlund     ret = scrub_file (fd);
2485e9cd1aeSAssar Westerlund     close (fd);
2495e9cd1aeSAssar Westerlund     return ret;
2505e9cd1aeSAssar Westerlund }
2515e9cd1aeSAssar Westerlund 
252b528cefcSMark Murray static krb5_error_code
253b528cefcSMark Murray fcc_gen_new(krb5_context context, krb5_ccache *id)
254b528cefcSMark Murray {
255b528cefcSMark Murray     krb5_fcache *f;
256b528cefcSMark Murray     int fd;
257b528cefcSMark Murray     char *file;
258adb0ddaeSAssar Westerlund 
259b528cefcSMark Murray     f = malloc(sizeof(*f));
260adb0ddaeSAssar Westerlund     if(f == NULL) {
261adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
262b528cefcSMark Murray 	return KRB5_CC_NOMEM;
263adb0ddaeSAssar Westerlund     }
2645e9cd1aeSAssar Westerlund     asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
265b528cefcSMark Murray     if(file == NULL) {
266b528cefcSMark Murray 	free(f);
267adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
268b528cefcSMark Murray 	return KRB5_CC_NOMEM;
269b528cefcSMark Murray     }
270b528cefcSMark Murray     fd = mkstemp(file);
271b528cefcSMark Murray     if(fd < 0) {
272c19800e8SDoug Rabson 	int ret = errno;
273c19800e8SDoug Rabson 	krb5_set_error_string(context, "mkstemp %s", file);
274b528cefcSMark Murray 	free(f);
275b528cefcSMark Murray 	free(file);
276c19800e8SDoug Rabson 	return ret;
277b528cefcSMark Murray     }
278b528cefcSMark Murray     close(fd);
279b528cefcSMark Murray     f->filename = file;
280b528cefcSMark Murray     f->version = 0;
281b528cefcSMark Murray     (*id)->data.data = f;
282b528cefcSMark Murray     (*id)->data.length = sizeof(*f);
283b528cefcSMark Murray     return 0;
284b528cefcSMark Murray }
285b528cefcSMark Murray 
286b528cefcSMark Murray static void
287b528cefcSMark Murray storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
288b528cefcSMark Murray {
289b528cefcSMark Murray     int flags = 0;
290b528cefcSMark Murray     switch(vno) {
291b528cefcSMark Murray     case KRB5_FCC_FVNO_1:
292b528cefcSMark Murray 	flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
293b528cefcSMark Murray 	flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
294b528cefcSMark Murray 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
295b528cefcSMark Murray 	break;
296b528cefcSMark Murray     case KRB5_FCC_FVNO_2:
297b528cefcSMark Murray 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
298b528cefcSMark Murray 	break;
299b528cefcSMark Murray     case KRB5_FCC_FVNO_3:
300b528cefcSMark Murray 	flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
301b528cefcSMark Murray 	break;
302b528cefcSMark Murray     case KRB5_FCC_FVNO_4:
303b528cefcSMark Murray 	break;
304b528cefcSMark Murray     default:
305b528cefcSMark Murray 	krb5_abortx(context,
306b528cefcSMark Murray 		    "storage_set_flags called with bad vno (%x)", vno);
307b528cefcSMark Murray     }
308b528cefcSMark Murray     krb5_storage_set_flags(sp, flags);
309b528cefcSMark Murray }
310b528cefcSMark Murray 
311b528cefcSMark Murray static krb5_error_code
3121c43270aSJacques Vidrine fcc_open(krb5_context context,
3131c43270aSJacques Vidrine 	 krb5_ccache id,
3141c43270aSJacques Vidrine 	 int *fd_ret,
3151c43270aSJacques Vidrine 	 int flags,
3161c43270aSJacques Vidrine 	 mode_t mode)
3171c43270aSJacques Vidrine {
3181c43270aSJacques Vidrine     krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
3191c43270aSJacques Vidrine 			      (flags | O_RDWR) == flags);
3201c43270aSJacques Vidrine     krb5_error_code ret;
3211c43270aSJacques Vidrine     const char *filename = FILENAME(id);
3221c43270aSJacques Vidrine     int fd;
3231c43270aSJacques Vidrine     fd = open(filename, flags, mode);
3241c43270aSJacques Vidrine     if(fd < 0) {
3251c43270aSJacques Vidrine 	ret = errno;
3261c43270aSJacques Vidrine 	krb5_set_error_string(context, "open(%s): %s", filename,
3271c43270aSJacques Vidrine 			      strerror(ret));
3281c43270aSJacques Vidrine 	return ret;
3291c43270aSJacques Vidrine     }
3301c43270aSJacques Vidrine 
3311c43270aSJacques Vidrine     if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
3321c43270aSJacques Vidrine 	close(fd);
3331c43270aSJacques Vidrine 	return ret;
3341c43270aSJacques Vidrine     }
3351c43270aSJacques Vidrine     *fd_ret = fd;
3361c43270aSJacques Vidrine     return 0;
3371c43270aSJacques Vidrine }
3381c43270aSJacques Vidrine 
3391c43270aSJacques Vidrine static krb5_error_code
340b528cefcSMark Murray fcc_initialize(krb5_context context,
341b528cefcSMark Murray 	       krb5_ccache id,
342b528cefcSMark Murray 	       krb5_principal primary_principal)
343b528cefcSMark Murray {
344b528cefcSMark Murray     krb5_fcache *f = FCACHE(id);
3455e9cd1aeSAssar Westerlund     int ret = 0;
346b528cefcSMark Murray     int fd;
347b528cefcSMark Murray     char *filename = f->filename;
348b528cefcSMark Murray 
3495e9cd1aeSAssar Westerlund     unlink (filename);
350b528cefcSMark Murray 
3511c43270aSJacques Vidrine     ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
3521c43270aSJacques Vidrine     if(ret)
353adb0ddaeSAssar Westerlund 	return ret;
354b528cefcSMark Murray     {
355b528cefcSMark Murray 	krb5_storage *sp;
356b528cefcSMark Murray 	sp = krb5_storage_from_fd(fd);
3578373020dSJacques Vidrine 	krb5_storage_set_eof_code(sp, KRB5_CC_END);
358b528cefcSMark Murray 	if(context->fcache_vno != 0)
359b528cefcSMark Murray 	    f->version = context->fcache_vno;
360b528cefcSMark Murray 	else
361b528cefcSMark Murray 	    f->version = KRB5_FCC_FVNO_4;
3625e9cd1aeSAssar Westerlund 	ret |= krb5_store_int8(sp, 5);
3635e9cd1aeSAssar Westerlund 	ret |= krb5_store_int8(sp, f->version);
364b528cefcSMark Murray 	storage_set_flags(context, sp, f->version);
3655e9cd1aeSAssar Westerlund 	if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
366b528cefcSMark Murray 	    /* V4 stuff */
367b528cefcSMark Murray 	    if (context->kdc_sec_offset) {
3685e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, 12); /* length */
3695e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
3705e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, 8); /* length of data */
3715e9cd1aeSAssar Westerlund 		ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
3725e9cd1aeSAssar Westerlund 		ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
373b528cefcSMark Murray 	    } else {
3745e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, 0);
375b528cefcSMark Murray 	    }
376b528cefcSMark Murray 	}
3775e9cd1aeSAssar Westerlund 	ret |= krb5_store_principal(sp, primary_principal);
3781c43270aSJacques Vidrine 
379b528cefcSMark Murray 	krb5_storage_free(sp);
380b528cefcSMark Murray     }
3811c43270aSJacques Vidrine     fcc_unlock(context, fd);
3825e9cd1aeSAssar Westerlund     if (close(fd) < 0)
383adb0ddaeSAssar Westerlund 	if (ret == 0) {
3845e9cd1aeSAssar Westerlund 	    ret = errno;
3851c43270aSJacques Vidrine 	    krb5_set_error_string (context, "close %s: %s",
3861c43270aSJacques Vidrine 				   FILENAME(id), strerror(ret));
387adb0ddaeSAssar Westerlund 	}
3885e9cd1aeSAssar Westerlund     return ret;
389b528cefcSMark Murray }
390b528cefcSMark Murray 
391b528cefcSMark Murray static krb5_error_code
392b528cefcSMark Murray fcc_close(krb5_context context,
393b528cefcSMark Murray 	  krb5_ccache id)
394b528cefcSMark Murray {
395b528cefcSMark Murray     free (FILENAME(id));
396b528cefcSMark Murray     krb5_data_free(&id->data);
397b528cefcSMark Murray     return 0;
398b528cefcSMark Murray }
399b528cefcSMark Murray 
400b528cefcSMark Murray static krb5_error_code
401b528cefcSMark Murray fcc_destroy(krb5_context context,
402b528cefcSMark Murray 	    krb5_ccache id)
403b528cefcSMark Murray {
4041c43270aSJacques Vidrine     erase_file(FILENAME(id));
405b528cefcSMark Murray     return 0;
406b528cefcSMark Murray }
407b528cefcSMark Murray 
408b528cefcSMark Murray static krb5_error_code
409b528cefcSMark Murray fcc_store_cred(krb5_context context,
410b528cefcSMark Murray 	       krb5_ccache id,
411b528cefcSMark Murray 	       krb5_creds *creds)
412b528cefcSMark Murray {
4135e9cd1aeSAssar Westerlund     int ret;
414b528cefcSMark Murray     int fd;
415b528cefcSMark Murray 
4161c43270aSJacques Vidrine     ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY, 0);
4171c43270aSJacques Vidrine     if(ret)
418adb0ddaeSAssar Westerlund 	return ret;
419b528cefcSMark Murray     {
420b528cefcSMark Murray 	krb5_storage *sp;
421b528cefcSMark Murray 	sp = krb5_storage_from_fd(fd);
4228373020dSJacques Vidrine 	krb5_storage_set_eof_code(sp, KRB5_CC_END);
423b528cefcSMark Murray 	storage_set_flags(context, sp, FCACHE(id)->version);
424c19800e8SDoug Rabson 	if (!krb5_config_get_bool_default(context, NULL, TRUE,
4251c43270aSJacques Vidrine 					  "libdefaults",
4261c43270aSJacques Vidrine 					  "fcc-mit-ticketflags",
4271c43270aSJacques Vidrine 					  NULL))
428c19800e8SDoug Rabson 	    krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
429c19800e8SDoug Rabson 	ret = krb5_store_creds(sp, creds);
430b528cefcSMark Murray 	krb5_storage_free(sp);
431b528cefcSMark Murray     }
4321c43270aSJacques Vidrine     fcc_unlock(context, fd);
4335e9cd1aeSAssar Westerlund     if (close(fd) < 0)
434adb0ddaeSAssar Westerlund 	if (ret == 0) {
4355e9cd1aeSAssar Westerlund 	    ret = errno;
4361c43270aSJacques Vidrine 	    krb5_set_error_string (context, "close %s: %s",
4371c43270aSJacques Vidrine 				   FILENAME(id), strerror(ret));
438adb0ddaeSAssar Westerlund 	}
4395e9cd1aeSAssar Westerlund     return ret;
440b528cefcSMark Murray }
441b528cefcSMark Murray 
442b528cefcSMark Murray static krb5_error_code
443b528cefcSMark Murray init_fcc (krb5_context context,
4441c43270aSJacques Vidrine 	  krb5_ccache id,
445b528cefcSMark Murray 	  krb5_storage **ret_sp,
446b528cefcSMark Murray 	  int *ret_fd)
447b528cefcSMark Murray {
448b528cefcSMark Murray     int fd;
449b528cefcSMark Murray     int8_t pvno, tag;
450b528cefcSMark Murray     krb5_storage *sp;
4515e9cd1aeSAssar Westerlund     krb5_error_code ret;
452b528cefcSMark Murray 
4531c43270aSJacques Vidrine     ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY, 0);
4545e9cd1aeSAssar Westerlund     if(ret)
4555e9cd1aeSAssar Westerlund 	return ret;
4561c43270aSJacques Vidrine 
4571c43270aSJacques Vidrine     sp = krb5_storage_from_fd(fd);
4581c43270aSJacques Vidrine     if(sp == NULL) {
459c19800e8SDoug Rabson 	krb5_clear_error_string(context);
4601c43270aSJacques Vidrine 	ret = ENOMEM;
4611c43270aSJacques Vidrine 	goto out;
462b528cefcSMark Murray     }
4631c43270aSJacques Vidrine     krb5_storage_set_eof_code(sp, KRB5_CC_END);
4641c43270aSJacques Vidrine     ret = krb5_ret_int8(sp, &pvno);
4651c43270aSJacques Vidrine     if(ret != 0) {
466c19800e8SDoug Rabson 	if(ret == KRB5_CC_END) {
467c19800e8SDoug Rabson 	    krb5_set_error_string(context, "Empty credential cache file: %s",
468c19800e8SDoug Rabson 				  FILENAME(id));
469c19800e8SDoug Rabson 	    ret = ENOENT;
470c19800e8SDoug Rabson 	} else
471c19800e8SDoug Rabson 	    krb5_set_error_string(context, "Error reading pvno in "
472c19800e8SDoug Rabson 				  "cache file: %s", FILENAME(id));
4731c43270aSJacques Vidrine 	goto out;
4741c43270aSJacques Vidrine     }
4751c43270aSJacques Vidrine     if(pvno != 5) {
476c19800e8SDoug Rabson 	krb5_set_error_string(context, "Bad version number in credential "
477c19800e8SDoug Rabson 			      "cache file: %s", FILENAME(id));
4781c43270aSJacques Vidrine 	ret = KRB5_CCACHE_BADVNO;
4791c43270aSJacques Vidrine 	goto out;
4801c43270aSJacques Vidrine     }
4811c43270aSJacques Vidrine     ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
4821c43270aSJacques Vidrine     if(ret != 0) {
483c19800e8SDoug Rabson 	krb5_set_error_string(context, "Error reading tag in "
484c19800e8SDoug Rabson 			      "cache file: %s", FILENAME(id));
4851c43270aSJacques Vidrine 	ret = KRB5_CC_FORMAT;
4861c43270aSJacques Vidrine 	goto out;
4871c43270aSJacques Vidrine     }
4881c43270aSJacques Vidrine     FCACHE(id)->version = tag;
4891c43270aSJacques Vidrine     storage_set_flags(context, sp, FCACHE(id)->version);
490b528cefcSMark Murray     switch (tag) {
491b528cefcSMark Murray     case KRB5_FCC_FVNO_4: {
492b528cefcSMark Murray 	int16_t length;
493b528cefcSMark Murray 
4941c43270aSJacques Vidrine 	ret = krb5_ret_int16 (sp, &length);
4951c43270aSJacques Vidrine 	if(ret) {
4961c43270aSJacques Vidrine 	    ret = KRB5_CC_FORMAT;
497c19800e8SDoug Rabson 	    krb5_set_error_string(context, "Error reading tag length in "
498c19800e8SDoug Rabson 			      "cache file: %s", FILENAME(id));
4991c43270aSJacques Vidrine 	    goto out;
5001c43270aSJacques Vidrine 	}
501b528cefcSMark Murray 	while(length > 0) {
502c19800e8SDoug Rabson 	    int16_t dtag, data_len;
503b528cefcSMark Murray 	    int i;
504b528cefcSMark Murray 	    int8_t dummy;
505b528cefcSMark Murray 
506c19800e8SDoug Rabson 	    ret = krb5_ret_int16 (sp, &dtag);
5071c43270aSJacques Vidrine 	    if(ret) {
508c19800e8SDoug Rabson 		krb5_set_error_string(context, "Error reading dtag in "
509c19800e8SDoug Rabson 				      "cache file: %s", FILENAME(id));
5101c43270aSJacques Vidrine 		ret = KRB5_CC_FORMAT;
5111c43270aSJacques Vidrine 		goto out;
5121c43270aSJacques Vidrine 	    }
5131c43270aSJacques Vidrine 	    ret = krb5_ret_int16 (sp, &data_len);
5141c43270aSJacques Vidrine 	    if(ret) {
515c19800e8SDoug Rabson 		krb5_set_error_string(context, "Error reading dlength in "
516c19800e8SDoug Rabson 				      "cache file: %s", FILENAME(id));
5171c43270aSJacques Vidrine 		ret = KRB5_CC_FORMAT;
5181c43270aSJacques Vidrine 		goto out;
5191c43270aSJacques Vidrine 	    }
520c19800e8SDoug Rabson 	    switch (dtag) {
521b528cefcSMark Murray 	    case FCC_TAG_DELTATIME :
5221c43270aSJacques Vidrine 		ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
5231c43270aSJacques Vidrine 		if(ret) {
524c19800e8SDoug Rabson 		    krb5_set_error_string(context, "Error reading kdc_sec in "
525c19800e8SDoug Rabson 					  "cache file: %s", FILENAME(id));
5261c43270aSJacques Vidrine 		    ret = KRB5_CC_FORMAT;
5271c43270aSJacques Vidrine 		    goto out;
5281c43270aSJacques Vidrine 		}
5291c43270aSJacques Vidrine 		ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
5301c43270aSJacques Vidrine 		if(ret) {
531c19800e8SDoug Rabson 		    krb5_set_error_string(context, "Error reading kdc_usec in "
532c19800e8SDoug Rabson 					  "cache file: %s", FILENAME(id));
5331c43270aSJacques Vidrine 		    ret = KRB5_CC_FORMAT;
5341c43270aSJacques Vidrine 		    goto out;
5351c43270aSJacques Vidrine 		}
536b528cefcSMark Murray 		break;
537b528cefcSMark Murray 	    default :
5381c43270aSJacques Vidrine 		for (i = 0; i < data_len; ++i) {
5391c43270aSJacques Vidrine 		    ret = krb5_ret_int8 (sp, &dummy);
5401c43270aSJacques Vidrine 		    if(ret) {
541c19800e8SDoug Rabson 			krb5_set_error_string(context, "Error reading unknown "
542c19800e8SDoug Rabson 					      "tag in cache file: %s",
543c19800e8SDoug Rabson 					      FILENAME(id));
5441c43270aSJacques Vidrine 			ret = KRB5_CC_FORMAT;
5451c43270aSJacques Vidrine 			goto out;
5461c43270aSJacques Vidrine 		    }
5471c43270aSJacques Vidrine 		}
548b528cefcSMark Murray 		break;
549b528cefcSMark Murray 	    }
550b528cefcSMark Murray 	    length -= 4 + data_len;
551b528cefcSMark Murray 	}
552b528cefcSMark Murray 	break;
553b528cefcSMark Murray     }
554b528cefcSMark Murray     case KRB5_FCC_FVNO_3:
555b528cefcSMark Murray     case KRB5_FCC_FVNO_2:
556b528cefcSMark Murray     case KRB5_FCC_FVNO_1:
557b528cefcSMark Murray 	break;
558b528cefcSMark Murray     default :
5591c43270aSJacques Vidrine 	ret = KRB5_CCACHE_BADVNO;
560c19800e8SDoug Rabson 	krb5_set_error_string(context, "Unknown version number (%d) in "
561c19800e8SDoug Rabson 			      "credential cache file: %s",
562c19800e8SDoug Rabson 			      (int)tag, FILENAME(id));
5631c43270aSJacques Vidrine 	goto out;
564b528cefcSMark Murray     }
565b528cefcSMark Murray     *ret_sp = sp;
566b528cefcSMark Murray     *ret_fd = fd;
5671c43270aSJacques Vidrine 
568b528cefcSMark Murray     return 0;
5691c43270aSJacques Vidrine   out:
5701c43270aSJacques Vidrine     if(sp != NULL)
5711c43270aSJacques Vidrine 	krb5_storage_free(sp);
5721c43270aSJacques Vidrine     fcc_unlock(context, fd);
5731c43270aSJacques Vidrine     close(fd);
5741c43270aSJacques Vidrine     return ret;
575b528cefcSMark Murray }
576b528cefcSMark Murray 
577b528cefcSMark Murray static krb5_error_code
578b528cefcSMark Murray fcc_get_principal(krb5_context context,
579b528cefcSMark Murray 		  krb5_ccache id,
580b528cefcSMark Murray 		  krb5_principal *principal)
581b528cefcSMark Murray {
582b528cefcSMark Murray     krb5_error_code ret;
583b528cefcSMark Murray     int fd;
584b528cefcSMark Murray     krb5_storage *sp;
585b528cefcSMark Murray 
5861c43270aSJacques Vidrine     ret = init_fcc (context, id, &sp, &fd);
587b528cefcSMark Murray     if (ret)
588b528cefcSMark Murray 	return ret;
5895e9cd1aeSAssar Westerlund     ret = krb5_ret_principal(sp, principal);
590c19800e8SDoug Rabson     if (ret)
591c19800e8SDoug Rabson 	krb5_clear_error_string(context);
592b528cefcSMark Murray     krb5_storage_free(sp);
5931c43270aSJacques Vidrine     fcc_unlock(context, fd);
594b528cefcSMark Murray     close(fd);
5955e9cd1aeSAssar Westerlund     return ret;
596b528cefcSMark Murray }
597b528cefcSMark Murray 
598b528cefcSMark Murray static krb5_error_code
5991c43270aSJacques Vidrine fcc_end_get (krb5_context context,
6001c43270aSJacques Vidrine 	     krb5_ccache id,
6011c43270aSJacques Vidrine 	     krb5_cc_cursor *cursor);
6021c43270aSJacques Vidrine 
6031c43270aSJacques Vidrine static krb5_error_code
604b528cefcSMark Murray fcc_get_first (krb5_context context,
605b528cefcSMark Murray 	       krb5_ccache id,
606b528cefcSMark Murray 	       krb5_cc_cursor *cursor)
607b528cefcSMark Murray {
608b528cefcSMark Murray     krb5_error_code ret;
609b528cefcSMark Murray     krb5_principal principal;
610b528cefcSMark Murray 
611b528cefcSMark Murray     *cursor = malloc(sizeof(struct fcc_cursor));
612c19800e8SDoug Rabson     if (*cursor == NULL) {
613c19800e8SDoug Rabson         krb5_set_error_string (context, "malloc: out of memory");
614c19800e8SDoug Rabson 	return ENOMEM;
615c19800e8SDoug Rabson     }
616c19800e8SDoug Rabson     memset(*cursor, 0, sizeof(struct fcc_cursor));
617b528cefcSMark Murray 
6181c43270aSJacques Vidrine     ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
619b528cefcSMark Murray 		    &FCC_CURSOR(*cursor)->fd);
6201c43270aSJacques Vidrine     if (ret) {
6211c43270aSJacques Vidrine 	free(*cursor);
622c19800e8SDoug Rabson 	*cursor = NULL;
623b528cefcSMark Murray 	return ret;
6241c43270aSJacques Vidrine     }
6251c43270aSJacques Vidrine     ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
6261c43270aSJacques Vidrine     if(ret) {
627c19800e8SDoug Rabson 	krb5_clear_error_string(context);
6281c43270aSJacques Vidrine 	fcc_end_get(context, id, cursor);
6291c43270aSJacques Vidrine 	return ret;
6301c43270aSJacques Vidrine     }
631b528cefcSMark Murray     krb5_free_principal (context, principal);
6321c43270aSJacques Vidrine     fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
633b528cefcSMark Murray     return 0;
634b528cefcSMark Murray }
635b528cefcSMark Murray 
636b528cefcSMark Murray static krb5_error_code
637b528cefcSMark Murray fcc_get_next (krb5_context context,
638b528cefcSMark Murray 	      krb5_ccache id,
639b528cefcSMark Murray 	      krb5_cc_cursor *cursor,
640b528cefcSMark Murray 	      krb5_creds *creds)
641b528cefcSMark Murray {
6421c43270aSJacques Vidrine     krb5_error_code ret;
6431c43270aSJacques Vidrine     if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
6441c43270aSJacques Vidrine 	return ret;
6451c43270aSJacques Vidrine 
6461c43270aSJacques Vidrine     ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
647c19800e8SDoug Rabson     if (ret)
648c19800e8SDoug Rabson 	krb5_clear_error_string(context);
6491c43270aSJacques Vidrine 
6501c43270aSJacques Vidrine     fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
6511c43270aSJacques Vidrine     return ret;
652b528cefcSMark Murray }
653b528cefcSMark Murray 
654b528cefcSMark Murray static krb5_error_code
655b528cefcSMark Murray fcc_end_get (krb5_context context,
656b528cefcSMark Murray 	     krb5_ccache id,
657b528cefcSMark Murray 	     krb5_cc_cursor *cursor)
658b528cefcSMark Murray {
659b528cefcSMark Murray     krb5_storage_free(FCC_CURSOR(*cursor)->sp);
660b528cefcSMark Murray     close (FCC_CURSOR(*cursor)->fd);
661b528cefcSMark Murray     free(*cursor);
6621c43270aSJacques Vidrine     *cursor = NULL;
663b528cefcSMark Murray     return 0;
664b528cefcSMark Murray }
665b528cefcSMark Murray 
666b528cefcSMark Murray static krb5_error_code
667b528cefcSMark Murray fcc_remove_cred(krb5_context context,
668b528cefcSMark Murray 		 krb5_ccache id,
669b528cefcSMark Murray 		 krb5_flags which,
670b528cefcSMark Murray 		 krb5_creds *cred)
671b528cefcSMark Murray {
672c19800e8SDoug Rabson     krb5_error_code ret;
673c19800e8SDoug Rabson     krb5_ccache copy;
674c19800e8SDoug Rabson 
675c19800e8SDoug Rabson     ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &copy);
676c19800e8SDoug Rabson     if (ret)
677c19800e8SDoug Rabson 	return ret;
678c19800e8SDoug Rabson 
679c19800e8SDoug Rabson     ret = krb5_cc_copy_cache(context, id, copy);
680c19800e8SDoug Rabson     if (ret) {
681c19800e8SDoug Rabson 	krb5_cc_destroy(context, copy);
682c19800e8SDoug Rabson 	return ret;
683c19800e8SDoug Rabson     }
684c19800e8SDoug Rabson 
685c19800e8SDoug Rabson     ret = krb5_cc_remove_cred(context, copy, which, cred);
686c19800e8SDoug Rabson     if (ret) {
687c19800e8SDoug Rabson 	krb5_cc_destroy(context, copy);
688c19800e8SDoug Rabson 	return ret;
689c19800e8SDoug Rabson     }
690c19800e8SDoug Rabson 
691c19800e8SDoug Rabson     fcc_destroy(context, id);
692c19800e8SDoug Rabson 
693c19800e8SDoug Rabson     ret = krb5_cc_copy_cache(context, copy, id);
694c19800e8SDoug Rabson     krb5_cc_destroy(context, copy);
695c19800e8SDoug Rabson 
696c19800e8SDoug Rabson     return ret;
697b528cefcSMark Murray }
698b528cefcSMark Murray 
699b528cefcSMark Murray static krb5_error_code
700b528cefcSMark Murray fcc_set_flags(krb5_context context,
701b528cefcSMark Murray 	      krb5_ccache id,
702b528cefcSMark Murray 	      krb5_flags flags)
703b528cefcSMark Murray {
704b528cefcSMark Murray     return 0; /* XXX */
705b528cefcSMark Murray }
706b528cefcSMark Murray 
707b528cefcSMark Murray static krb5_error_code
708b528cefcSMark Murray fcc_get_version(krb5_context context,
709b528cefcSMark Murray 		krb5_ccache id)
710b528cefcSMark Murray {
711b528cefcSMark Murray     return FCACHE(id)->version;
712b528cefcSMark Murray }
713b528cefcSMark Murray 
714c19800e8SDoug Rabson struct fcache_iter {
715c19800e8SDoug Rabson     int first;
716c19800e8SDoug Rabson };
717c19800e8SDoug Rabson 
718c19800e8SDoug Rabson static krb5_error_code
719c19800e8SDoug Rabson fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
720c19800e8SDoug Rabson {
721c19800e8SDoug Rabson     struct fcache_iter *iter;
722c19800e8SDoug Rabson 
723c19800e8SDoug Rabson     iter = calloc(1, sizeof(*iter));
724c19800e8SDoug Rabson     if (iter == NULL) {
725c19800e8SDoug Rabson 	krb5_set_error_string(context, "malloc - out of memory");
726c19800e8SDoug Rabson 	return ENOMEM;
727c19800e8SDoug Rabson     }
728c19800e8SDoug Rabson     iter->first = 1;
729c19800e8SDoug Rabson     *cursor = iter;
730c19800e8SDoug Rabson     return 0;
731c19800e8SDoug Rabson }
732c19800e8SDoug Rabson 
733c19800e8SDoug Rabson static krb5_error_code
734c19800e8SDoug Rabson fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
735c19800e8SDoug Rabson {
736c19800e8SDoug Rabson     struct fcache_iter *iter = cursor;
737c19800e8SDoug Rabson     krb5_error_code ret;
738c19800e8SDoug Rabson     const char *fn;
739c19800e8SDoug Rabson     char *expandedfn = NULL;
740c19800e8SDoug Rabson 
741c19800e8SDoug Rabson     if (!iter->first) {
742c19800e8SDoug Rabson 	krb5_clear_error_string(context);
743c19800e8SDoug Rabson 	return KRB5_CC_END;
744c19800e8SDoug Rabson     }
745c19800e8SDoug Rabson     iter->first = 0;
746c19800e8SDoug Rabson 
747c19800e8SDoug Rabson     fn = krb5_cc_default_name(context);
748c19800e8SDoug Rabson     if (strncasecmp(fn, "FILE:", 5) != 0) {
749c19800e8SDoug Rabson 	ret = _krb5_expand_default_cc_name(context,
750c19800e8SDoug Rabson 					   KRB5_DEFAULT_CCNAME_FILE,
751c19800e8SDoug Rabson 					   &expandedfn);
752c19800e8SDoug Rabson 	if (ret)
753c19800e8SDoug Rabson 	    return ret;
754c19800e8SDoug Rabson     }
755c19800e8SDoug Rabson     ret = krb5_cc_resolve(context, fn, id);
756c19800e8SDoug Rabson     if (expandedfn)
757c19800e8SDoug Rabson 	free(expandedfn);
758c19800e8SDoug Rabson 
759c19800e8SDoug Rabson     return ret;
760c19800e8SDoug Rabson }
761c19800e8SDoug Rabson 
762c19800e8SDoug Rabson static krb5_error_code
763c19800e8SDoug Rabson fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
764c19800e8SDoug Rabson {
765c19800e8SDoug Rabson     struct fcache_iter *iter = cursor;
766c19800e8SDoug Rabson     free(iter);
767c19800e8SDoug Rabson     return 0;
768c19800e8SDoug Rabson }
769c19800e8SDoug Rabson 
770c19800e8SDoug Rabson static krb5_error_code
771c19800e8SDoug Rabson fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
772c19800e8SDoug Rabson {
773c19800e8SDoug Rabson     krb5_error_code ret = 0;
774c19800e8SDoug Rabson 
775c19800e8SDoug Rabson     ret = rename(FILENAME(from), FILENAME(to));
776c19800e8SDoug Rabson     if (ret && errno != EXDEV) {
777c19800e8SDoug Rabson 	ret = errno;
778c19800e8SDoug Rabson 	krb5_set_error_string(context,
779c19800e8SDoug Rabson 			      "Rename of file from %s to %s failed: %s",
780c19800e8SDoug Rabson 			      FILENAME(from), FILENAME(to),
781c19800e8SDoug Rabson 			      strerror(ret));
782c19800e8SDoug Rabson 	return ret;
783c19800e8SDoug Rabson     } else if (ret && errno == EXDEV) {
784c19800e8SDoug Rabson 	/* make a copy and delete the orignal */
785c19800e8SDoug Rabson 	krb5_ssize_t sz1, sz2;
786c19800e8SDoug Rabson 	int fd1, fd2;
787c19800e8SDoug Rabson 	char buf[BUFSIZ];
788c19800e8SDoug Rabson 
789c19800e8SDoug Rabson 	ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY, 0);
790c19800e8SDoug Rabson 	if(ret)
791c19800e8SDoug Rabson 	    return ret;
792c19800e8SDoug Rabson 
793c19800e8SDoug Rabson 	unlink(FILENAME(to));
794c19800e8SDoug Rabson 
795c19800e8SDoug Rabson 	ret = fcc_open(context, to, &fd2,
796c19800e8SDoug Rabson 		       O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600);
797c19800e8SDoug Rabson 	if(ret)
798c19800e8SDoug Rabson 	    goto out1;
799c19800e8SDoug Rabson 
800c19800e8SDoug Rabson 	while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
801c19800e8SDoug Rabson 	    sz2 = write(fd2, buf, sz1);
802c19800e8SDoug Rabson 	    if (sz1 != sz2) {
803c19800e8SDoug Rabson 		ret = EIO;
804c19800e8SDoug Rabson 		krb5_set_error_string(context,
805c19800e8SDoug Rabson 				      "Failed to write data from one file "
806c19800e8SDoug Rabson 				      "credential cache to the other");
807c19800e8SDoug Rabson 		goto out2;
808c19800e8SDoug Rabson 	    }
809c19800e8SDoug Rabson 	}
810c19800e8SDoug Rabson 	if (sz1 < 0) {
811c19800e8SDoug Rabson 	    ret = EIO;
812c19800e8SDoug Rabson 	    krb5_set_error_string(context,
813c19800e8SDoug Rabson 				  "Failed to read data from one file "
814c19800e8SDoug Rabson 				  "credential cache to the other");
815c19800e8SDoug Rabson 	    goto out2;
816c19800e8SDoug Rabson 	}
817c19800e8SDoug Rabson 	erase_file(FILENAME(from));
818c19800e8SDoug Rabson 
819c19800e8SDoug Rabson     out2:
820c19800e8SDoug Rabson 	fcc_unlock(context, fd2);
821c19800e8SDoug Rabson 	close(fd2);
822c19800e8SDoug Rabson 
823c19800e8SDoug Rabson     out1:
824c19800e8SDoug Rabson 	fcc_unlock(context, fd1);
825c19800e8SDoug Rabson 	close(fd1);
826c19800e8SDoug Rabson 
827c19800e8SDoug Rabson 	if (ret) {
828c19800e8SDoug Rabson 	    erase_file(FILENAME(to));
829c19800e8SDoug Rabson 	    return ret;
830c19800e8SDoug Rabson 	}
831c19800e8SDoug Rabson     }
832c19800e8SDoug Rabson 
833c19800e8SDoug Rabson     /* make sure ->version is uptodate */
834c19800e8SDoug Rabson     {
835c19800e8SDoug Rabson 	krb5_storage *sp;
836c19800e8SDoug Rabson 	int fd;
837c19800e8SDoug Rabson 	ret = init_fcc (context, to, &sp, &fd);
838c19800e8SDoug Rabson 	krb5_storage_free(sp);
839c19800e8SDoug Rabson 	fcc_unlock(context, fd);
840c19800e8SDoug Rabson 	close(fd);
841c19800e8SDoug Rabson     }
842c19800e8SDoug Rabson     return ret;
843c19800e8SDoug Rabson }
844c19800e8SDoug Rabson 
845c19800e8SDoug Rabson static krb5_error_code
846c19800e8SDoug Rabson fcc_default_name(krb5_context context, char **str)
847c19800e8SDoug Rabson {
848c19800e8SDoug Rabson     return _krb5_expand_default_cc_name(context,
849c19800e8SDoug Rabson 					KRB5_DEFAULT_CCNAME_FILE,
850c19800e8SDoug Rabson 					str);
851c19800e8SDoug Rabson }
852c19800e8SDoug Rabson 
853c19800e8SDoug Rabson /**
854c19800e8SDoug Rabson  * Variable containing the FILE based credential cache implemention.
855c19800e8SDoug Rabson  *
856c19800e8SDoug Rabson  * @ingroup krb5_ccache
857c19800e8SDoug Rabson  */
858c19800e8SDoug Rabson 
859b528cefcSMark Murray const krb5_cc_ops krb5_fcc_ops = {
860b528cefcSMark Murray     "FILE",
861b528cefcSMark Murray     fcc_get_name,
862b528cefcSMark Murray     fcc_resolve,
863b528cefcSMark Murray     fcc_gen_new,
864b528cefcSMark Murray     fcc_initialize,
865b528cefcSMark Murray     fcc_destroy,
866b528cefcSMark Murray     fcc_close,
867b528cefcSMark Murray     fcc_store_cred,
868b528cefcSMark Murray     NULL, /* fcc_retrieve */
869b528cefcSMark Murray     fcc_get_principal,
870b528cefcSMark Murray     fcc_get_first,
871b528cefcSMark Murray     fcc_get_next,
872b528cefcSMark Murray     fcc_end_get,
873b528cefcSMark Murray     fcc_remove_cred,
874b528cefcSMark Murray     fcc_set_flags,
875c19800e8SDoug Rabson     fcc_get_version,
876c19800e8SDoug Rabson     fcc_get_cache_first,
877c19800e8SDoug Rabson     fcc_get_cache_next,
878c19800e8SDoug Rabson     fcc_end_cache_get,
879c19800e8SDoug Rabson     fcc_move,
880c19800e8SDoug Rabson     fcc_default_name
881b528cefcSMark Murray };
882