xref: /freebsd/crypto/heimdal/lib/krb5/fcache.c (revision 1c43270ada91f3473174c2bf81fba64963b7e4ac)
1b528cefcSMark Murray /*
21c43270aSJacques Vidrine  * Copyright (c) 1997 - 2004 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 
361c43270aSJacques Vidrine RCSID("$Id: fcache.c,v 1.34.6.6 2004/03/10 13:30:59 lha Exp $");
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
1081c43270aSJacques Vidrine _krb5_xunlock(int fd)
1091c43270aSJacques Vidrine {
1101c43270aSJacques Vidrine #ifdef HAVE_FCNTL_LOCK
1111c43270aSJacques Vidrine     struct flock l;
1121c43270aSJacques Vidrine     l.l_start = 0;
1131c43270aSJacques Vidrine     l.l_len = 0;
1141c43270aSJacques Vidrine     l.l_type = F_UNLCK;
1151c43270aSJacques Vidrine     l.l_whence = SEEK_SET;
1161c43270aSJacques Vidrine     return fcntl(fd, F_SETLKW, &l);
1171c43270aSJacques Vidrine #else
1181c43270aSJacques Vidrine     return flock(fd, LOCK_UN);
1191c43270aSJacques Vidrine #endif
1201c43270aSJacques Vidrine }
1211c43270aSJacques Vidrine 
1221c43270aSJacques Vidrine static krb5_error_code
1231c43270aSJacques Vidrine fcc_lock(krb5_context context, krb5_ccache id,
1241c43270aSJacques Vidrine 	 int fd, krb5_boolean exclusive)
1251c43270aSJacques Vidrine {
1261c43270aSJacques Vidrine     return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
1271c43270aSJacques Vidrine }
1281c43270aSJacques Vidrine 
1291c43270aSJacques Vidrine static krb5_error_code
1301c43270aSJacques Vidrine fcc_unlock(krb5_context context, int fd)
1311c43270aSJacques Vidrine {
1321c43270aSJacques Vidrine     return _krb5_xunlock(fd);
1331c43270aSJacques Vidrine }
1341c43270aSJacques Vidrine 
135b528cefcSMark Murray static krb5_error_code
136b528cefcSMark Murray fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
137b528cefcSMark Murray {
138b528cefcSMark Murray     krb5_fcache *f;
139b528cefcSMark Murray     f = malloc(sizeof(*f));
140adb0ddaeSAssar Westerlund     if(f == NULL) {
141adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
142b528cefcSMark Murray 	return KRB5_CC_NOMEM;
143adb0ddaeSAssar Westerlund     }
144b528cefcSMark Murray     f->filename = strdup(res);
145b528cefcSMark Murray     if(f->filename == NULL){
146b528cefcSMark Murray 	free(f);
147adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
148b528cefcSMark Murray 	return KRB5_CC_NOMEM;
149b528cefcSMark Murray     }
150b528cefcSMark Murray     f->version = 0;
151b528cefcSMark Murray     (*id)->data.data = f;
152b528cefcSMark Murray     (*id)->data.length = sizeof(*f);
153b528cefcSMark Murray     return 0;
154b528cefcSMark Murray }
155b528cefcSMark Murray 
1565e9cd1aeSAssar Westerlund /*
1575e9cd1aeSAssar Westerlund  * Try to scrub the contents of `filename' safely.
1585e9cd1aeSAssar Westerlund  */
1595e9cd1aeSAssar Westerlund 
1605e9cd1aeSAssar Westerlund static int
1615e9cd1aeSAssar Westerlund scrub_file (int fd)
1625e9cd1aeSAssar Westerlund {
1635e9cd1aeSAssar Westerlund     off_t pos;
1645e9cd1aeSAssar Westerlund     char buf[128];
1655e9cd1aeSAssar Westerlund 
1665e9cd1aeSAssar Westerlund     pos = lseek(fd, 0, SEEK_END);
1675e9cd1aeSAssar Westerlund     if (pos < 0)
1685e9cd1aeSAssar Westerlund         return errno;
1695e9cd1aeSAssar Westerlund     if (lseek(fd, 0, SEEK_SET) < 0)
1705e9cd1aeSAssar Westerlund         return errno;
1715e9cd1aeSAssar Westerlund     memset(buf, 0, sizeof(buf));
1725e9cd1aeSAssar Westerlund     while(pos > 0) {
1735e9cd1aeSAssar Westerlund         ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
1745e9cd1aeSAssar Westerlund 
1755e9cd1aeSAssar Westerlund 	if (tmp < 0)
1765e9cd1aeSAssar Westerlund 	    return errno;
1775e9cd1aeSAssar Westerlund 	pos -= tmp;
1785e9cd1aeSAssar Westerlund     }
1795e9cd1aeSAssar Westerlund     fsync (fd);
1805e9cd1aeSAssar Westerlund     return 0;
1815e9cd1aeSAssar Westerlund }
1825e9cd1aeSAssar Westerlund 
1835e9cd1aeSAssar Westerlund /*
1845e9cd1aeSAssar Westerlund  * Erase `filename' if it exists, trying to remove the contents if
1855e9cd1aeSAssar Westerlund  * it's `safe'.  We always try to remove the file, it it exists.  It's
1865e9cd1aeSAssar Westerlund  * only overwritten if it's a regular file (not a symlink and not a
1875e9cd1aeSAssar Westerlund  * hardlink)
1885e9cd1aeSAssar Westerlund  */
1895e9cd1aeSAssar Westerlund 
190b528cefcSMark Murray static krb5_error_code
191b528cefcSMark Murray erase_file(const char *filename)
192b528cefcSMark Murray {
193b528cefcSMark Murray     int fd;
1945e9cd1aeSAssar Westerlund     struct stat sb1, sb2;
1955e9cd1aeSAssar Westerlund     int ret;
1965e9cd1aeSAssar Westerlund 
1975e9cd1aeSAssar Westerlund     ret = lstat (filename, &sb1);
1985e9cd1aeSAssar Westerlund     if (ret < 0)
1995e9cd1aeSAssar Westerlund 	return errno;
200b528cefcSMark Murray 
201b528cefcSMark Murray     fd = open(filename, O_RDWR | O_BINARY);
202b528cefcSMark Murray     if(fd < 0) {
203b528cefcSMark Murray 	if(errno == ENOENT)
204b528cefcSMark Murray 	    return 0;
205b528cefcSMark Murray 	else
206b528cefcSMark Murray 	    return errno;
207b528cefcSMark Murray     }
2085e9cd1aeSAssar Westerlund     if (unlink(filename) < 0) {
209b528cefcSMark Murray         close (fd);
2105e9cd1aeSAssar Westerlund         return errno;
2115e9cd1aeSAssar Westerlund     }
2125e9cd1aeSAssar Westerlund     ret = fstat (fd, &sb2);
2135e9cd1aeSAssar Westerlund     if (ret < 0) {
2145e9cd1aeSAssar Westerlund 	close (fd);
2155e9cd1aeSAssar Westerlund 	return errno;
2165e9cd1aeSAssar Westerlund     }
2175e9cd1aeSAssar Westerlund 
2185e9cd1aeSAssar Westerlund     /* check if someone was playing with symlinks */
2195e9cd1aeSAssar Westerlund 
2205e9cd1aeSAssar Westerlund     if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
2215e9cd1aeSAssar Westerlund 	close (fd);
2225e9cd1aeSAssar Westerlund 	return EPERM;
2235e9cd1aeSAssar Westerlund     }
2245e9cd1aeSAssar Westerlund 
2255e9cd1aeSAssar Westerlund     /* there are still hard links to this file */
2265e9cd1aeSAssar Westerlund 
2275e9cd1aeSAssar Westerlund     if (sb2.st_nlink != 0) {
2285e9cd1aeSAssar Westerlund         close (fd);
229b528cefcSMark Murray         return 0;
230b528cefcSMark Murray     }
231b528cefcSMark Murray 
2325e9cd1aeSAssar Westerlund     ret = scrub_file (fd);
2335e9cd1aeSAssar Westerlund     close (fd);
2345e9cd1aeSAssar Westerlund     return ret;
2355e9cd1aeSAssar Westerlund }
2365e9cd1aeSAssar Westerlund 
237b528cefcSMark Murray static krb5_error_code
238b528cefcSMark Murray fcc_gen_new(krb5_context context, krb5_ccache *id)
239b528cefcSMark Murray {
240b528cefcSMark Murray     krb5_fcache *f;
241b528cefcSMark Murray     int fd;
242b528cefcSMark Murray     char *file;
243adb0ddaeSAssar Westerlund 
244b528cefcSMark Murray     f = malloc(sizeof(*f));
245adb0ddaeSAssar Westerlund     if(f == NULL) {
246adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
247b528cefcSMark Murray 	return KRB5_CC_NOMEM;
248adb0ddaeSAssar Westerlund     }
2495e9cd1aeSAssar Westerlund     asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
250b528cefcSMark Murray     if(file == NULL) {
251b528cefcSMark Murray 	free(f);
252adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "malloc: out of memory");
253b528cefcSMark Murray 	return KRB5_CC_NOMEM;
254b528cefcSMark Murray     }
255b528cefcSMark Murray     fd = mkstemp(file);
256b528cefcSMark Murray     if(fd < 0) {
257b528cefcSMark Murray 	free(f);
258b528cefcSMark Murray 	free(file);
259adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "mkstemp %s", file);
260b528cefcSMark Murray 	return errno;
261b528cefcSMark Murray     }
262b528cefcSMark Murray     close(fd);
263b528cefcSMark Murray     f->filename = file;
264b528cefcSMark Murray     f->version = 0;
265b528cefcSMark Murray     (*id)->data.data = f;
266b528cefcSMark Murray     (*id)->data.length = sizeof(*f);
267b528cefcSMark Murray     return 0;
268b528cefcSMark Murray }
269b528cefcSMark Murray 
270b528cefcSMark Murray static void
271b528cefcSMark Murray storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
272b528cefcSMark Murray {
273b528cefcSMark Murray     int flags = 0;
274b528cefcSMark Murray     switch(vno) {
275b528cefcSMark Murray     case KRB5_FCC_FVNO_1:
276b528cefcSMark Murray 	flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
277b528cefcSMark Murray 	flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
278b528cefcSMark Murray 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
279b528cefcSMark Murray 	break;
280b528cefcSMark Murray     case KRB5_FCC_FVNO_2:
281b528cefcSMark Murray 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
282b528cefcSMark Murray 	break;
283b528cefcSMark Murray     case KRB5_FCC_FVNO_3:
284b528cefcSMark Murray 	flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
285b528cefcSMark Murray 	break;
286b528cefcSMark Murray     case KRB5_FCC_FVNO_4:
287b528cefcSMark Murray 	break;
288b528cefcSMark Murray     default:
289b528cefcSMark Murray 	krb5_abortx(context,
290b528cefcSMark Murray 		    "storage_set_flags called with bad vno (%x)", vno);
291b528cefcSMark Murray     }
292b528cefcSMark Murray     krb5_storage_set_flags(sp, flags);
293b528cefcSMark Murray }
294b528cefcSMark Murray 
295b528cefcSMark Murray static krb5_error_code
2961c43270aSJacques Vidrine fcc_open(krb5_context context,
2971c43270aSJacques Vidrine 	 krb5_ccache id,
2981c43270aSJacques Vidrine 	 int *fd_ret,
2991c43270aSJacques Vidrine 	 int flags,
3001c43270aSJacques Vidrine 	 mode_t mode)
3011c43270aSJacques Vidrine {
3021c43270aSJacques Vidrine     krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
3031c43270aSJacques Vidrine 			      (flags | O_RDWR) == flags);
3041c43270aSJacques Vidrine     krb5_error_code ret;
3051c43270aSJacques Vidrine     const char *filename = FILENAME(id);
3061c43270aSJacques Vidrine     int fd;
3071c43270aSJacques Vidrine     fd = open(filename, flags, mode);
3081c43270aSJacques Vidrine     if(fd < 0) {
3091c43270aSJacques Vidrine 	ret = errno;
3101c43270aSJacques Vidrine 	krb5_set_error_string(context, "open(%s): %s", filename,
3111c43270aSJacques Vidrine 			      strerror(ret));
3121c43270aSJacques Vidrine 	return ret;
3131c43270aSJacques Vidrine     }
3141c43270aSJacques Vidrine 
3151c43270aSJacques Vidrine     if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
3161c43270aSJacques Vidrine 	close(fd);
3171c43270aSJacques Vidrine 	return ret;
3181c43270aSJacques Vidrine     }
3191c43270aSJacques Vidrine     *fd_ret = fd;
3201c43270aSJacques Vidrine     return 0;
3211c43270aSJacques Vidrine }
3221c43270aSJacques Vidrine 
3231c43270aSJacques Vidrine static krb5_error_code
324b528cefcSMark Murray fcc_initialize(krb5_context context,
325b528cefcSMark Murray 	       krb5_ccache id,
326b528cefcSMark Murray 	       krb5_principal primary_principal)
327b528cefcSMark Murray {
328b528cefcSMark Murray     krb5_fcache *f = FCACHE(id);
3295e9cd1aeSAssar Westerlund     int ret = 0;
330b528cefcSMark Murray     int fd;
331b528cefcSMark Murray     char *filename = f->filename;
332b528cefcSMark Murray 
3335e9cd1aeSAssar Westerlund     unlink (filename);
334b528cefcSMark Murray 
3351c43270aSJacques Vidrine     ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
3361c43270aSJacques Vidrine     if(ret)
337adb0ddaeSAssar Westerlund 	return ret;
338b528cefcSMark Murray     {
339b528cefcSMark Murray 	krb5_storage *sp;
340b528cefcSMark Murray 	sp = krb5_storage_from_fd(fd);
3418373020dSJacques Vidrine 	krb5_storage_set_eof_code(sp, KRB5_CC_END);
342b528cefcSMark Murray 	if(context->fcache_vno != 0)
343b528cefcSMark Murray 	    f->version = context->fcache_vno;
344b528cefcSMark Murray 	else
345b528cefcSMark Murray 	    f->version = KRB5_FCC_FVNO_4;
3465e9cd1aeSAssar Westerlund 	ret |= krb5_store_int8(sp, 5);
3475e9cd1aeSAssar Westerlund 	ret |= krb5_store_int8(sp, f->version);
348b528cefcSMark Murray 	storage_set_flags(context, sp, f->version);
3495e9cd1aeSAssar Westerlund 	if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
350b528cefcSMark Murray 	    /* V4 stuff */
351b528cefcSMark Murray 	    if (context->kdc_sec_offset) {
3525e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, 12); /* length */
3535e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
3545e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, 8); /* length of data */
3555e9cd1aeSAssar Westerlund 		ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
3565e9cd1aeSAssar Westerlund 		ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
357b528cefcSMark Murray 	    } else {
3585e9cd1aeSAssar Westerlund 		ret |= krb5_store_int16 (sp, 0);
359b528cefcSMark Murray 	    }
360b528cefcSMark Murray 	}
3615e9cd1aeSAssar Westerlund 	ret |= krb5_store_principal(sp, primary_principal);
3621c43270aSJacques Vidrine 
363b528cefcSMark Murray 	krb5_storage_free(sp);
364b528cefcSMark Murray     }
3651c43270aSJacques Vidrine     fcc_unlock(context, fd);
3665e9cd1aeSAssar Westerlund     if (close(fd) < 0)
367adb0ddaeSAssar Westerlund 	if (ret == 0) {
3685e9cd1aeSAssar Westerlund 	    ret = errno;
3691c43270aSJacques Vidrine 	    krb5_set_error_string (context, "close %s: %s",
3701c43270aSJacques Vidrine 				   FILENAME(id), strerror(ret));
371adb0ddaeSAssar Westerlund 	}
3725e9cd1aeSAssar Westerlund     return ret;
373b528cefcSMark Murray }
374b528cefcSMark Murray 
375b528cefcSMark Murray static krb5_error_code
376b528cefcSMark Murray fcc_close(krb5_context context,
377b528cefcSMark Murray 	  krb5_ccache id)
378b528cefcSMark Murray {
379b528cefcSMark Murray     free (FILENAME(id));
380b528cefcSMark Murray     krb5_data_free(&id->data);
381b528cefcSMark Murray     return 0;
382b528cefcSMark Murray }
383b528cefcSMark Murray 
384b528cefcSMark Murray static krb5_error_code
385b528cefcSMark Murray fcc_destroy(krb5_context context,
386b528cefcSMark Murray 	    krb5_ccache id)
387b528cefcSMark Murray {
3881c43270aSJacques Vidrine     erase_file(FILENAME(id));
389b528cefcSMark Murray     return 0;
390b528cefcSMark Murray }
391b528cefcSMark Murray 
392b528cefcSMark Murray static krb5_error_code
393b528cefcSMark Murray fcc_store_cred(krb5_context context,
394b528cefcSMark Murray 	       krb5_ccache id,
395b528cefcSMark Murray 	       krb5_creds *creds)
396b528cefcSMark Murray {
3975e9cd1aeSAssar Westerlund     int ret;
398b528cefcSMark Murray     int fd;
399b528cefcSMark Murray 
4001c43270aSJacques Vidrine     ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY, 0);
4011c43270aSJacques Vidrine     if(ret)
402adb0ddaeSAssar Westerlund 	return ret;
403b528cefcSMark Murray     {
404b528cefcSMark Murray 	krb5_storage *sp;
405b528cefcSMark Murray 	sp = krb5_storage_from_fd(fd);
4068373020dSJacques Vidrine 	krb5_storage_set_eof_code(sp, KRB5_CC_END);
407b528cefcSMark Murray 	storage_set_flags(context, sp, FCACHE(id)->version);
4081c43270aSJacques Vidrine 	if (krb5_config_get_bool_default(context, NULL, FALSE,
4091c43270aSJacques Vidrine 					 "libdefaults",
4101c43270aSJacques Vidrine 					 "fcc-mit-ticketflags",
4111c43270aSJacques Vidrine 					 NULL))
4121c43270aSJacques Vidrine 	    ret = _krb5_store_creds_heimdal_0_7(sp, creds);
4131c43270aSJacques Vidrine 	else
4141c43270aSJacques Vidrine 	    ret = _krb5_store_creds_heimdal_pre_0_7(sp, creds);
415b528cefcSMark Murray 	krb5_storage_free(sp);
416b528cefcSMark Murray     }
4171c43270aSJacques Vidrine     fcc_unlock(context, fd);
4185e9cd1aeSAssar Westerlund     if (close(fd) < 0)
419adb0ddaeSAssar Westerlund 	if (ret == 0) {
4205e9cd1aeSAssar Westerlund 	    ret = errno;
4211c43270aSJacques Vidrine 	    krb5_set_error_string (context, "close %s: %s",
4221c43270aSJacques Vidrine 				   FILENAME(id), strerror(ret));
423adb0ddaeSAssar Westerlund 	}
4245e9cd1aeSAssar Westerlund     return ret;
425b528cefcSMark Murray }
426b528cefcSMark Murray 
427b528cefcSMark Murray static krb5_error_code
428b528cefcSMark Murray init_fcc (krb5_context context,
4291c43270aSJacques Vidrine 	  krb5_ccache id,
430b528cefcSMark Murray 	  krb5_storage **ret_sp,
431b528cefcSMark Murray 	  int *ret_fd)
432b528cefcSMark Murray {
433b528cefcSMark Murray     int fd;
434b528cefcSMark Murray     int8_t pvno, tag;
435b528cefcSMark Murray     krb5_storage *sp;
4365e9cd1aeSAssar Westerlund     krb5_error_code ret;
437b528cefcSMark Murray 
4381c43270aSJacques Vidrine     ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY, 0);
4391c43270aSJacques Vidrine 
4405e9cd1aeSAssar Westerlund     if(ret)
4415e9cd1aeSAssar Westerlund 	return ret;
4421c43270aSJacques Vidrine 
4431c43270aSJacques Vidrine     sp = krb5_storage_from_fd(fd);
4441c43270aSJacques Vidrine     if(sp == NULL) {
4451c43270aSJacques Vidrine 	ret = ENOMEM;
4461c43270aSJacques Vidrine 	goto out;
447b528cefcSMark Murray     }
4481c43270aSJacques Vidrine     krb5_storage_set_eof_code(sp, KRB5_CC_END);
4491c43270aSJacques Vidrine     ret = krb5_ret_int8(sp, &pvno);
4501c43270aSJacques Vidrine     if(ret != 0) {
4511c43270aSJacques Vidrine 	if(ret == KRB5_CC_END)
4521c43270aSJacques Vidrine 	    ret = ENOENT; /* empty file */
4531c43270aSJacques Vidrine 	goto out;
4541c43270aSJacques Vidrine     }
4551c43270aSJacques Vidrine     if(pvno != 5) {
4561c43270aSJacques Vidrine 	ret = KRB5_CCACHE_BADVNO;
4571c43270aSJacques Vidrine 	goto out;
4581c43270aSJacques Vidrine     }
4591c43270aSJacques Vidrine     ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
4601c43270aSJacques Vidrine     if(ret != 0) {
4611c43270aSJacques Vidrine 	ret = KRB5_CC_FORMAT;
4621c43270aSJacques Vidrine 	goto out;
4631c43270aSJacques Vidrine     }
4641c43270aSJacques Vidrine     FCACHE(id)->version = tag;
4651c43270aSJacques Vidrine     storage_set_flags(context, sp, FCACHE(id)->version);
466b528cefcSMark Murray     switch (tag) {
467b528cefcSMark Murray     case KRB5_FCC_FVNO_4: {
468b528cefcSMark Murray 	int16_t length;
469b528cefcSMark Murray 
4701c43270aSJacques Vidrine 	ret = krb5_ret_int16 (sp, &length);
4711c43270aSJacques Vidrine 	if(ret) {
4721c43270aSJacques Vidrine 	    ret = KRB5_CC_FORMAT;
4731c43270aSJacques Vidrine 	    goto out;
4741c43270aSJacques Vidrine 	}
475b528cefcSMark Murray 	while(length > 0) {
476b528cefcSMark Murray 	    int16_t tag, data_len;
477b528cefcSMark Murray 	    int i;
478b528cefcSMark Murray 	    int8_t dummy;
479b528cefcSMark Murray 
4801c43270aSJacques Vidrine 	    ret = krb5_ret_int16 (sp, &tag);
4811c43270aSJacques Vidrine 	    if(ret) {
4821c43270aSJacques Vidrine 		ret = KRB5_CC_FORMAT;
4831c43270aSJacques Vidrine 		goto out;
4841c43270aSJacques Vidrine 	    }
4851c43270aSJacques Vidrine 	    ret = krb5_ret_int16 (sp, &data_len);
4861c43270aSJacques Vidrine 	    if(ret) {
4871c43270aSJacques Vidrine 		ret = KRB5_CC_FORMAT;
4881c43270aSJacques Vidrine 		goto out;
4891c43270aSJacques Vidrine 	    }
490b528cefcSMark Murray 	    switch (tag) {
491b528cefcSMark Murray 	    case FCC_TAG_DELTATIME :
4921c43270aSJacques Vidrine 		ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
4931c43270aSJacques Vidrine 		if(ret) {
4941c43270aSJacques Vidrine 		    ret = KRB5_CC_FORMAT;
4951c43270aSJacques Vidrine 		    goto out;
4961c43270aSJacques Vidrine 		}
4971c43270aSJacques Vidrine 		ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
4981c43270aSJacques Vidrine 		if(ret) {
4991c43270aSJacques Vidrine 		    ret = KRB5_CC_FORMAT;
5001c43270aSJacques Vidrine 		    goto out;
5011c43270aSJacques Vidrine 		}
502b528cefcSMark Murray 		break;
503b528cefcSMark Murray 	    default :
5041c43270aSJacques Vidrine 		for (i = 0; i < data_len; ++i) {
5051c43270aSJacques Vidrine 		    ret = krb5_ret_int8 (sp, &dummy);
5061c43270aSJacques Vidrine 		    if(ret) {
5071c43270aSJacques Vidrine 			ret = KRB5_CC_FORMAT;
5081c43270aSJacques Vidrine 			goto out;
5091c43270aSJacques Vidrine 		    }
5101c43270aSJacques Vidrine 		}
511b528cefcSMark Murray 		break;
512b528cefcSMark Murray 	    }
513b528cefcSMark Murray 	    length -= 4 + data_len;
514b528cefcSMark Murray 	}
515b528cefcSMark Murray 	break;
516b528cefcSMark Murray     }
517b528cefcSMark Murray     case KRB5_FCC_FVNO_3:
518b528cefcSMark Murray     case KRB5_FCC_FVNO_2:
519b528cefcSMark Murray     case KRB5_FCC_FVNO_1:
520b528cefcSMark Murray 	break;
521b528cefcSMark Murray     default :
5221c43270aSJacques Vidrine 	ret = KRB5_CCACHE_BADVNO;
5231c43270aSJacques Vidrine 	goto out;
524b528cefcSMark Murray     }
525b528cefcSMark Murray     *ret_sp = sp;
526b528cefcSMark Murray     *ret_fd = fd;
5271c43270aSJacques Vidrine 
528b528cefcSMark Murray     return 0;
5291c43270aSJacques Vidrine   out:
5301c43270aSJacques Vidrine     if(sp != NULL)
5311c43270aSJacques Vidrine 	krb5_storage_free(sp);
5321c43270aSJacques Vidrine     fcc_unlock(context, fd);
5331c43270aSJacques Vidrine     close(fd);
5341c43270aSJacques Vidrine     return ret;
535b528cefcSMark Murray }
536b528cefcSMark Murray 
537b528cefcSMark Murray static krb5_error_code
538b528cefcSMark Murray fcc_get_principal(krb5_context context,
539b528cefcSMark Murray 		  krb5_ccache id,
540b528cefcSMark Murray 		  krb5_principal *principal)
541b528cefcSMark Murray {
542b528cefcSMark Murray     krb5_error_code ret;
543b528cefcSMark Murray     int fd;
544b528cefcSMark Murray     krb5_storage *sp;
545b528cefcSMark Murray 
5461c43270aSJacques Vidrine     ret = init_fcc (context, id, &sp, &fd);
547b528cefcSMark Murray     if (ret)
548b528cefcSMark Murray 	return ret;
5495e9cd1aeSAssar Westerlund     ret = krb5_ret_principal(sp, principal);
550b528cefcSMark Murray     krb5_storage_free(sp);
5511c43270aSJacques Vidrine     fcc_unlock(context, fd);
552b528cefcSMark Murray     close(fd);
5535e9cd1aeSAssar Westerlund     return ret;
554b528cefcSMark Murray }
555b528cefcSMark Murray 
556b528cefcSMark Murray static krb5_error_code
5571c43270aSJacques Vidrine fcc_end_get (krb5_context context,
5581c43270aSJacques Vidrine 	     krb5_ccache id,
5591c43270aSJacques Vidrine 	     krb5_cc_cursor *cursor);
5601c43270aSJacques Vidrine 
5611c43270aSJacques Vidrine static krb5_error_code
562b528cefcSMark Murray fcc_get_first (krb5_context context,
563b528cefcSMark Murray 	       krb5_ccache id,
564b528cefcSMark Murray 	       krb5_cc_cursor *cursor)
565b528cefcSMark Murray {
566b528cefcSMark Murray     krb5_error_code ret;
567b528cefcSMark Murray     krb5_principal principal;
568b528cefcSMark Murray 
569b528cefcSMark Murray     *cursor = malloc(sizeof(struct fcc_cursor));
570b528cefcSMark Murray 
5711c43270aSJacques Vidrine     ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
572b528cefcSMark Murray 		    &FCC_CURSOR(*cursor)->fd);
5731c43270aSJacques Vidrine     if (ret) {
5741c43270aSJacques Vidrine 	free(*cursor);
575b528cefcSMark Murray 	return ret;
5761c43270aSJacques Vidrine     }
5771c43270aSJacques Vidrine     ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
5781c43270aSJacques Vidrine     if(ret) {
5791c43270aSJacques Vidrine 	fcc_end_get(context, id, cursor);
5801c43270aSJacques Vidrine 	return ret;
5811c43270aSJacques Vidrine     }
582b528cefcSMark Murray     krb5_free_principal (context, principal);
5831c43270aSJacques Vidrine     fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
584b528cefcSMark Murray     return 0;
585b528cefcSMark Murray }
586b528cefcSMark Murray 
587b528cefcSMark Murray static krb5_error_code
588b528cefcSMark Murray fcc_get_next (krb5_context context,
589b528cefcSMark Murray 	      krb5_ccache id,
590b528cefcSMark Murray 	      krb5_cc_cursor *cursor,
591b528cefcSMark Murray 	      krb5_creds *creds)
592b528cefcSMark Murray {
5931c43270aSJacques Vidrine     krb5_error_code ret;
5941c43270aSJacques Vidrine     if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
5951c43270aSJacques Vidrine 	return ret;
5961c43270aSJacques Vidrine 
5971c43270aSJacques Vidrine     ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
5981c43270aSJacques Vidrine 
5991c43270aSJacques Vidrine     fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
6001c43270aSJacques Vidrine     return ret;
601b528cefcSMark Murray }
602b528cefcSMark Murray 
603b528cefcSMark Murray static krb5_error_code
604b528cefcSMark Murray fcc_end_get (krb5_context context,
605b528cefcSMark Murray 	     krb5_ccache id,
606b528cefcSMark Murray 	     krb5_cc_cursor *cursor)
607b528cefcSMark Murray {
608b528cefcSMark Murray     krb5_storage_free(FCC_CURSOR(*cursor)->sp);
609b528cefcSMark Murray     close (FCC_CURSOR(*cursor)->fd);
610b528cefcSMark Murray     free(*cursor);
6111c43270aSJacques Vidrine     *cursor = NULL;
612b528cefcSMark Murray     return 0;
613b528cefcSMark Murray }
614b528cefcSMark Murray 
615b528cefcSMark Murray static krb5_error_code
616b528cefcSMark Murray fcc_remove_cred(krb5_context context,
617b528cefcSMark Murray 		 krb5_ccache id,
618b528cefcSMark Murray 		 krb5_flags which,
619b528cefcSMark Murray 		 krb5_creds *cred)
620b528cefcSMark Murray {
621b528cefcSMark Murray     return 0; /* XXX */
622b528cefcSMark Murray }
623b528cefcSMark Murray 
624b528cefcSMark Murray static krb5_error_code
625b528cefcSMark Murray fcc_set_flags(krb5_context context,
626b528cefcSMark Murray 	      krb5_ccache id,
627b528cefcSMark Murray 	      krb5_flags flags)
628b528cefcSMark Murray {
629b528cefcSMark Murray     return 0; /* XXX */
630b528cefcSMark Murray }
631b528cefcSMark Murray 
632b528cefcSMark Murray static krb5_error_code
633b528cefcSMark Murray fcc_get_version(krb5_context context,
634b528cefcSMark Murray 		krb5_ccache id)
635b528cefcSMark Murray {
636b528cefcSMark Murray     return FCACHE(id)->version;
637b528cefcSMark Murray }
638b528cefcSMark Murray 
639b528cefcSMark Murray const krb5_cc_ops krb5_fcc_ops = {
640b528cefcSMark Murray     "FILE",
641b528cefcSMark Murray     fcc_get_name,
642b528cefcSMark Murray     fcc_resolve,
643b528cefcSMark Murray     fcc_gen_new,
644b528cefcSMark Murray     fcc_initialize,
645b528cefcSMark Murray     fcc_destroy,
646b528cefcSMark Murray     fcc_close,
647b528cefcSMark Murray     fcc_store_cred,
648b528cefcSMark Murray     NULL, /* fcc_retrieve */
649b528cefcSMark Murray     fcc_get_principal,
650b528cefcSMark Murray     fcc_get_first,
651b528cefcSMark Murray     fcc_get_next,
652b528cefcSMark Murray     fcc_end_get,
653b528cefcSMark Murray     fcc_remove_cred,
654b528cefcSMark Murray     fcc_set_flags,
655b528cefcSMark Murray     fcc_get_version
656b528cefcSMark Murray };
657