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