117cdd54661fbf1e4b9c43b5cfcc64fc58797334
[krb5.git] / src / lib / krb5 / ccache / cc_file.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_file.c - File-based credential cache */
3 /*
4  * Copyright 1990,1991,1992,1993,1994,2000,2004,2007 Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Original stdio support copyright 1995 by Cygnus Support.
8  *
9  * Export of this software from the United States of America may
10  *   require a specific license from the United States Government.
11  *   It is the responsibility of any person or organization contemplating
12  *   export to obtain such a license before exporting.
13  *
14  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
15  * distribute this software and its documentation for any purpose and
16  * without fee is hereby granted, provided that the above copyright
17  * notice appear in all copies and that both that copyright notice and
18  * this permission notice appear in supporting documentation, and that
19  * the name of M.I.T. not be used in advertising or publicity pertaining
20  * to distribution of the software without specific, written prior
21  * permission.  Furthermore if you modify this software you must label
22  * your software as modified software and not distribute it in such a
23  * fashion that it might be confused with the original M.I.T. software.
24  * M.I.T. makes no representations about the suitability of
25  * this software for any purpose.  It is provided "as is" without express
26  * or implied warranty.
27  */
28
29 /*
30   If OPENCLOSE is defined, each of the functions opens and closes the
31   file whenever it needs to access it.  Otherwise, the file is opened
32   once in initialize and closed once is close.
33
34   This library depends on UNIX-like file descriptors, and UNIX-like
35   behavior from the functions: open, close, read, write, lseek.
36
37   The quasi-BNF grammar for a credentials cache:
38
39   file ::=
40   principal list-of-credentials
41
42   credential ::=
43   client (principal)
44   server (principal)
45   keyblock (keyblock)
46   times (ticket_times)
47   is_skey (boolean)
48   ticket_flags (flags)
49   ticket (data)
50   second_ticket (data)
51
52   principal ::=
53   number of components (int32)
54   component 1 (data)
55   component 2 (data)
56   ...
57
58   data ::=
59   length (int32)
60   string of length bytes
61
62   etc.
63 */
64 /* todo:
65    Make sure that each time a function returns KRB5_NOMEM, everything
66    allocated earlier in the function and stack tree is freed.
67
68    File locking
69
70    Use pread/pwrite if available, so multiple threads can read
71    simultaneously.  (That may require reader/writer locks.)
72
73    fcc_nseq.c and fcc_read don't check return values a lot.
74 */
75 #include "k5-int.h"
76 #include "cc-int.h"
77
78 #include <stdio.h>
79 #include <errno.h>
80
81 #if HAVE_UNISTD_H
82 #include <unistd.h>
83 #endif
84
85 #ifdef HAVE_NETINET_IN_H
86 #if !defined(_WIN32)
87 #include <netinet/in.h>
88 #else
89 #include "port-sockets.h"
90 #endif
91 #else
92 # error find some way to use net-byte-order file version numbers.
93 #endif
94
95 static krb5_error_code KRB5_CALLCONV krb5_fcc_close
96 (krb5_context, krb5_ccache id);
97
98 static krb5_error_code KRB5_CALLCONV krb5_fcc_destroy
99 (krb5_context, krb5_ccache id);
100
101 static krb5_error_code KRB5_CALLCONV krb5_fcc_end_seq_get
102 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
103
104 static krb5_error_code KRB5_CALLCONV krb5_fcc_generate_new
105 (krb5_context, krb5_ccache *id);
106
107 static const char * KRB5_CALLCONV krb5_fcc_get_name
108 (krb5_context, krb5_ccache id);
109
110 static krb5_error_code KRB5_CALLCONV krb5_fcc_get_principal
111 (krb5_context, krb5_ccache id, krb5_principal *princ);
112
113 static krb5_error_code KRB5_CALLCONV krb5_fcc_initialize
114 (krb5_context, krb5_ccache id, krb5_principal princ);
115
116 static krb5_error_code KRB5_CALLCONV krb5_fcc_next_cred
117 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor,
118  krb5_creds *creds);
119
120 static krb5_error_code krb5_fcc_read
121 (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
122 static krb5_error_code krb5_fcc_read_principal
123 (krb5_context, krb5_ccache id, krb5_principal *princ);
124 static krb5_error_code krb5_fcc_read_keyblock
125 (krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
126 static krb5_error_code krb5_fcc_read_data
127 (krb5_context, krb5_ccache id, krb5_data *data);
128 static krb5_error_code krb5_fcc_read_int32
129 (krb5_context, krb5_ccache id, krb5_int32 *i);
130 static krb5_error_code krb5_fcc_read_ui_2
131 (krb5_context, krb5_ccache id, krb5_ui_2 *i);
132 static krb5_error_code krb5_fcc_read_octet
133 (krb5_context, krb5_ccache id, krb5_octet *i);
134 static krb5_error_code krb5_fcc_read_times
135 (krb5_context, krb5_ccache id, krb5_ticket_times *t);
136 static krb5_error_code krb5_fcc_read_addrs
137 (krb5_context, krb5_ccache, krb5_address ***);
138 static krb5_error_code krb5_fcc_read_addr
139 (krb5_context, krb5_ccache, krb5_address *);
140 static krb5_error_code krb5_fcc_read_authdata
141 (krb5_context, krb5_ccache, krb5_authdata ***);
142 static krb5_error_code krb5_fcc_read_authdatum
143 (krb5_context, krb5_ccache, krb5_authdata *);
144
145 static krb5_error_code KRB5_CALLCONV krb5_fcc_resolve
146 (krb5_context, krb5_ccache *id, const char *residual);
147
148 static krb5_error_code KRB5_CALLCONV krb5_fcc_retrieve
149 (krb5_context, krb5_ccache id, krb5_flags whichfields,
150  krb5_creds *mcreds, krb5_creds *creds);
151
152 static krb5_error_code KRB5_CALLCONV krb5_fcc_start_seq_get
153 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
154
155 static krb5_error_code KRB5_CALLCONV krb5_fcc_store
156 (krb5_context, krb5_ccache id, krb5_creds *creds);
157
158 static krb5_error_code krb5_fcc_skip_header
159 (krb5_context, krb5_ccache);
160 static krb5_error_code krb5_fcc_skip_principal
161 (krb5_context, krb5_ccache id);
162
163 static krb5_error_code KRB5_CALLCONV krb5_fcc_set_flags
164 (krb5_context, krb5_ccache id, krb5_flags flags);
165
166 static krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_new
167 (krb5_context context, krb5_cc_ptcursor *cursor);
168
169 static krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_next
170 (krb5_context context, krb5_cc_ptcursor cursor, krb5_ccache *ccache);
171
172 static krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_free
173 (krb5_context context, krb5_cc_ptcursor *cursor);
174
175 static krb5_error_code KRB5_CALLCONV krb5_fcc_last_change_time
176 (krb5_context context, krb5_ccache id, krb5_timestamp *change_time);
177
178 static krb5_error_code KRB5_CALLCONV krb5_fcc_lock
179 (krb5_context context, krb5_ccache id);
180
181 static krb5_error_code KRB5_CALLCONV krb5_fcc_unlock
182 (krb5_context context, krb5_ccache id);
183
184
185 extern const krb5_cc_ops krb5_cc_file_ops;
186
187 krb5_error_code krb5_change_cache (void);
188
189 static krb5_error_code krb5_fcc_write
190 (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
191 static krb5_error_code krb5_fcc_store_principal
192 (krb5_context, krb5_ccache id, krb5_principal princ);
193 static krb5_error_code krb5_fcc_store_keyblock
194 (krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
195 static krb5_error_code krb5_fcc_store_data
196 (krb5_context, krb5_ccache id, krb5_data *data);
197 static krb5_error_code krb5_fcc_store_int32
198 (krb5_context, krb5_ccache id, krb5_int32 i);
199 static krb5_error_code krb5_fcc_store_ui_4
200 (krb5_context, krb5_ccache id, krb5_ui_4 i);
201 static krb5_error_code krb5_fcc_store_ui_2
202 (krb5_context, krb5_ccache id, krb5_int32 i);
203 static krb5_error_code krb5_fcc_store_octet
204 (krb5_context, krb5_ccache id, krb5_int32 i);
205 static krb5_error_code krb5_fcc_store_times
206 (krb5_context, krb5_ccache id, krb5_ticket_times *t);
207 static krb5_error_code krb5_fcc_store_addrs
208 (krb5_context, krb5_ccache, krb5_address **);
209 static krb5_error_code krb5_fcc_store_addr
210 (krb5_context, krb5_ccache, krb5_address *);
211 static krb5_error_code krb5_fcc_store_authdata
212 (krb5_context, krb5_ccache, krb5_authdata **);
213 static krb5_error_code krb5_fcc_store_authdatum
214 (krb5_context, krb5_ccache, krb5_authdata *);
215
216 static krb5_error_code krb5_fcc_interpret
217 (krb5_context, int);
218
219 struct _krb5_fcc_data;
220 static krb5_error_code krb5_fcc_close_file
221 (krb5_context, struct _krb5_fcc_data *data);
222 static krb5_error_code krb5_fcc_open_file
223 (krb5_context, krb5_ccache, int);
224 static krb5_error_code krb5_fcc_data_last_change_time
225 (krb5_context context, struct _krb5_fcc_data *data,
226  krb5_timestamp *change_time);
227
228
229 #define KRB5_OK 0
230
231 #define KRB5_FCC_MAXLEN 100
232
233 /*
234  * FCC version 2 contains type information for principals.  FCC
235  * version 1 does not.
236  *
237  * FCC version 3 contains keyblock encryption type information, and is
238  * architecture independent.  Previous versions are not.
239  *
240  * The code will accept version 1, 2, and 3 ccaches, and depending
241  * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2,
242  * or 3 FCC caches.
243  *
244  * The default credentials cache should be type 3 for now (see
245  * init_ctx.c).
246  */
247
248 #define KRB5_FCC_FVNO_1 0x0501          /* krb v5, fcc v1 */
249 #define KRB5_FCC_FVNO_2 0x0502          /* krb v5, fcc v2 */
250 #define KRB5_FCC_FVNO_3 0x0503          /* krb v5, fcc v3 */
251 #define KRB5_FCC_FVNO_4 0x0504          /* krb v5, fcc v4 */
252
253 #define FCC_OPEN_AND_ERASE      1
254 #define FCC_OPEN_RDWR           2
255 #define FCC_OPEN_RDONLY         3
256
257 /* Credential file header tags.
258  * The header tags are constructed as:
259  *      krb5_ui_2       tag
260  *      krb5_ui_2       len
261  *      krb5_octet      data[len]
262  * This format allows for older versions of the fcc processing code to skip
263  * past unrecognized tag formats.
264  */
265 #define FCC_TAG_DELTATIME       1
266
267 #ifndef TKT_ROOT
268 #ifdef MSDOS_FILESYSTEM
269 #define TKT_ROOT "\\tkt"
270 #else
271 #define TKT_ROOT "/tmp/tkt"
272 #endif
273 #endif
274
275 /* macros to make checking flags easier */
276 #define OPENCLOSE(id) (((krb5_fcc_data *)id->data)->flags & KRB5_TC_OPENCLOSE)
277
278 typedef struct _krb5_fcc_data {
279     char *filename;
280     /* Lock this one before reading or modifying the data stored here
281        that can be changed.  (Filename is fixed after
282        initialization.)  */
283     k5_cc_mutex lock;
284     int file;
285     krb5_flags flags;
286     int mode;                           /* needed for locking code */
287     int version;                        /* version number of the file */
288
289     /* Buffer data on reading, for performance.
290        We used to have a stdio option, but we get more precise control
291        by using the POSIX I/O functions.  */
292 #define FCC_BUFSIZ 1024
293     size_t valid_bytes;
294     size_t cur_offset;
295     char buf[FCC_BUFSIZ];
296 } krb5_fcc_data;
297
298 static inline void invalidate_cache(krb5_fcc_data *data)
299 {
300     data->valid_bytes = 0;
301 }
302
303 static off_t fcc_lseek(krb5_fcc_data *data, off_t offset, int whence)
304 {
305     /* If we read some extra data in advance, and then want to know or
306        use our "current" position, we need to back up a little.  */
307     if (whence == SEEK_CUR && data->valid_bytes) {
308         assert(data->cur_offset > 0);
309         assert(data->cur_offset <= data->valid_bytes);
310         offset -= (data->valid_bytes - data->cur_offset);
311     }
312     invalidate_cache(data);
313     return lseek(data->file, offset, whence);
314 }
315
316 struct fcc_set {
317     struct fcc_set *next;
318     krb5_fcc_data *data;
319     unsigned int refcount;
320 };
321
322 k5_cc_mutex krb5int_cc_file_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
323 static struct fcc_set *fccs = NULL;
324
325 /* Iterator over file caches.  */
326 struct krb5_fcc_ptcursor_data {
327     struct fcc_set *cur;
328 };
329
330 /* An off_t can be arbitrarily complex */
331 typedef struct _krb5_fcc_cursor {
332     off_t pos;
333 } krb5_fcc_cursor;
334
335 #define MAYBE_OPEN(CONTEXT, ID, MODE)                                   \
336     {                                                                   \
337         k5_cc_mutex_assert_locked(CONTEXT, &((krb5_fcc_data *)(ID)->data)->lock); \
338         if (OPENCLOSE (ID)) {                                           \
339             krb5_error_code maybe_open_ret;                             \
340             maybe_open_ret = krb5_fcc_open_file (CONTEXT,ID,MODE);      \
341             if (maybe_open_ret) {                                       \
342                 k5_cc_mutex_unlock(CONTEXT, &((krb5_fcc_data *)(ID)->data)->lock); \
343                 return maybe_open_ret;                                  \
344             }                                                           \
345         }                                                               \
346     }
347
348 #define MAYBE_CLOSE(CONTEXT, ID, RET)                                   \
349     {                                                                   \
350         if (OPENCLOSE (ID)) {                                           \
351             krb5_error_code maybe_close_ret;                            \
352             maybe_close_ret = krb5_fcc_close_file (CONTEXT,             \
353                                                    (krb5_fcc_data *)(ID)->data); \
354             if (!(RET)) RET = maybe_close_ret; } }
355
356 #define MAYBE_CLOSE_IGNORE(CONTEXT, ID)                                 \
357     {                                                                   \
358         if (OPENCLOSE (ID)) {                                           \
359             (void) krb5_fcc_close_file (CONTEXT,(krb5_fcc_data *)(ID)->data); } }
360
361 #define CHECK(ret) if (ret != KRB5_OK) goto errout;
362
363 #define NO_FILE -1
364
365 /*
366  * Effects:
367  * Reads len bytes from the cache id, storing them in buf.
368  *
369  * Requires:
370  * Must be called with mutex locked.
371  *
372  * Errors:
373  * KRB5_CC_END - there were not len bytes available
374  * system errors (read)
375  */
376 static krb5_error_code
377 krb5_fcc_read(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
378 {
379 #if 0
380     int ret;
381
382     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
383
384     ret = read(((krb5_fcc_data *) id->data)->file, (char *) buf, len);
385     if (ret == -1)
386         return krb5_fcc_interpret(context, errno);
387     if (ret != len)
388         return KRB5_CC_END;
389     else
390         return KRB5_OK;
391 #else
392     krb5_fcc_data *data = (krb5_fcc_data *) id->data;
393
394     k5_cc_mutex_assert_locked(context, &data->lock);
395
396     while (len > 0) {
397         int nread, e;
398         size_t ncopied;
399
400         if (data->valid_bytes > 0)
401             assert(data->cur_offset <= data->valid_bytes);
402         if (data->valid_bytes == 0
403             || data->cur_offset == data->valid_bytes) {
404             /* Fill buffer from current file position.  */
405             nread = read(data->file, data->buf, sizeof(data->buf));
406             e = errno;
407             if (nread < 0)
408                 return krb5_fcc_interpret(context, e);
409             if (nread == 0)
410                 /* EOF */
411                 return KRB5_CC_END;
412             data->valid_bytes = nread;
413             data->cur_offset = 0;
414         }
415         assert(data->cur_offset < data->valid_bytes);
416         ncopied = len;
417         assert(ncopied == len);
418         if (data->valid_bytes - data->cur_offset < ncopied)
419             ncopied = data->valid_bytes - data->cur_offset;
420         memcpy(buf, data->buf + data->cur_offset, ncopied);
421         data->cur_offset += ncopied;
422         assert(data->cur_offset > 0);
423         assert(data->cur_offset <= data->valid_bytes);
424         len -= ncopied;
425         /* Don't do arithmetic on void pointers.  */
426         buf = (char*)buf + ncopied;
427     }
428     return 0;
429 #endif
430 }
431
432 /*
433  * FOR ALL OF THE FOLLOWING FUNCTIONS:
434  *
435  * Requires:
436  * id is open and set to read at the appropriate place in the file
437  *
438  * mutex is locked
439  *
440  * Effects:
441  * Fills in the second argument with data of the appropriate type from
442  * the file.  In some cases, the functions have to allocate space for
443  * variable length fields; therefore, krb5_destroy_<type> must be
444  * called for each filled in structure.
445  *
446  * Errors:
447  * system errors (read errors)
448  * KRB5_CC_NOMEM
449  */
450
451 #define ALLOC(NUM,TYPE)                         \
452     (((NUM) <= (((size_t)0-1)/ sizeof(TYPE)))   \
453      ? (TYPE *) calloc((NUM), sizeof(TYPE))     \
454      : (errno = ENOMEM,(TYPE *) 0))
455
456 static krb5_error_code
457 krb5_fcc_read_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
458 {
459     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
460     krb5_error_code kret;
461     register krb5_principal tmpprinc;
462     krb5_int32 length, type;
463     int i;
464
465     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
466
467     *princ = NULL;
468
469     if (data->version == KRB5_FCC_FVNO_1) {
470         type = KRB5_NT_UNKNOWN;
471     } else {
472         /* Read principal type */
473         kret = krb5_fcc_read_int32(context, id, &type);
474         if (kret != KRB5_OK)
475             return kret;
476     }
477
478     /* Read the number of components */
479     kret = krb5_fcc_read_int32(context, id, &length);
480     if (kret != KRB5_OK)
481         return kret;
482
483     /*
484      * DCE includes the principal's realm in the count; the new format
485      * does not.
486      */
487     if (data->version == KRB5_FCC_FVNO_1)
488         length--;
489     if (length < 0)
490         return KRB5_CC_NOMEM;
491
492     tmpprinc = (krb5_principal) malloc(sizeof(krb5_principal_data));
493     if (tmpprinc == NULL)
494         return KRB5_CC_NOMEM;
495     if (length) {
496         size_t msize = length;
497         if (msize != (krb5_ui_4) length) {
498             free(tmpprinc);
499             return KRB5_CC_NOMEM;
500         }
501         tmpprinc->data = ALLOC (msize, krb5_data);
502         if (tmpprinc->data == 0) {
503             free(tmpprinc);
504             return KRB5_CC_NOMEM;
505         }
506     } else
507         tmpprinc->data = 0;
508     tmpprinc->magic = KV5M_PRINCIPAL;
509     tmpprinc->length = length;
510     tmpprinc->type = type;
511
512     kret = krb5_fcc_read_data(context, id, krb5_princ_realm(context, tmpprinc));
513
514     i = 0;
515     CHECK(kret);
516
517     for (i=0; i < length; i++) {
518         kret = krb5_fcc_read_data(context, id, krb5_princ_component(context, tmpprinc, i));
519         CHECK(kret);
520     }
521     *princ = tmpprinc;
522     return KRB5_OK;
523
524 errout:
525     while(--i >= 0)
526         free(krb5_princ_component(context, tmpprinc, i)->data);
527     free(krb5_princ_realm(context, tmpprinc)->data);
528     free(tmpprinc->data);
529     free(tmpprinc);
530     return kret;
531 }
532
533 static krb5_error_code
534 krb5_fcc_read_addrs(krb5_context context, krb5_ccache id, krb5_address ***addrs)
535 {
536     krb5_error_code kret;
537     krb5_int32 length;
538     size_t msize;
539     int i;
540
541     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
542
543     *addrs = 0;
544
545     /* Read the number of components */
546     kret = krb5_fcc_read_int32(context, id, &length);
547     CHECK(kret);
548
549     /* Make *addrs able to hold length pointers to krb5_address structs
550      * Add one extra for a null-terminated list
551      */
552     msize = length;
553     msize += 1;
554     if (msize == 0 || msize - 1 != (krb5_ui_4) length || length < 0)
555         return KRB5_CC_NOMEM;
556     *addrs = ALLOC (msize, krb5_address *);
557     if (*addrs == NULL)
558         return KRB5_CC_NOMEM;
559
560     for (i=0; i < length; i++) {
561         (*addrs)[i] = (krb5_address *) malloc(sizeof(krb5_address));
562         if ((*addrs)[i] == NULL) {
563             krb5_free_addresses(context, *addrs);
564             *addrs = 0;
565             return KRB5_CC_NOMEM;
566         }
567         (*addrs)[i]->contents = NULL;
568         kret = krb5_fcc_read_addr(context, id, (*addrs)[i]);
569         CHECK(kret);
570     }
571
572     return KRB5_OK;
573 errout:
574     if (*addrs) {
575         krb5_free_addresses(context, *addrs);
576         *addrs = NULL;
577     }
578     return kret;
579 }
580
581 static krb5_error_code
582 krb5_fcc_read_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
583 {
584     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
585     krb5_error_code kret;
586     krb5_ui_2 ui2;
587     krb5_int32 int32;
588
589     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
590
591     keyblock->magic = KV5M_KEYBLOCK;
592     keyblock->contents = 0;
593
594     /* Enctype is signed, so sign-extend the 16-bit value we read. */
595     kret = krb5_fcc_read_ui_2(context, id, &ui2);
596     keyblock->enctype = (krb5_int16) ui2;
597     CHECK(kret);
598     if (data->version == KRB5_FCC_FVNO_3) {
599         /* This works because the old etype is the same as the new enctype. */
600         kret = krb5_fcc_read_ui_2(context, id, &ui2);
601         /* keyblock->enctype = ui2; */
602         CHECK(kret);
603     }
604
605     kret = krb5_fcc_read_int32(context, id, &int32);
606     CHECK(kret);
607     if (int32 < 0)
608         return KRB5_CC_NOMEM;
609     keyblock->length = int32;
610     /* Overflow check.  */
611     if (keyblock->length != (krb5_ui_4) int32)
612         return KRB5_CC_NOMEM;
613     if ( keyblock->length == 0 )
614         return KRB5_OK;
615     keyblock->contents = malloc(keyblock->length);
616     if (keyblock->contents == NULL)
617         return KRB5_CC_NOMEM;
618
619     kret = krb5_fcc_read(context, id, keyblock->contents, keyblock->length);
620     if (kret)
621         goto errout;
622
623     return KRB5_OK;
624 errout:
625     if (keyblock->contents) {
626         free(keyblock->contents);
627         keyblock->contents = NULL;
628     }
629     return kret;
630 }
631
632 static krb5_error_code
633 krb5_fcc_read_data(krb5_context context, krb5_ccache id, krb5_data *data)
634 {
635     krb5_error_code kret;
636     krb5_int32 len;
637
638     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
639
640     data->magic = KV5M_DATA;
641     data->data = 0;
642
643     kret = krb5_fcc_read_int32(context, id, &len);
644     CHECK(kret);
645     if (len < 0)
646         return KRB5_CC_NOMEM;
647     data->length = len;
648     if (data->length != (krb5_ui_4) len || data->length + 1 == 0)
649         return KRB5_CC_NOMEM;
650
651     if (data->length == 0) {
652         data->data = 0;
653         return KRB5_OK;
654     }
655
656     data->data = (char *) malloc(data->length+1);
657     if (data->data == NULL)
658         return KRB5_CC_NOMEM;
659
660     kret = krb5_fcc_read(context, id, data->data, (unsigned) data->length);
661     CHECK(kret);
662
663     data->data[data->length] = 0; /* Null terminate, just in case.... */
664     return KRB5_OK;
665 errout:
666     if (data->data) {
667         free(data->data);
668         data->data = NULL;
669     }
670     return kret;
671 }
672
673 static krb5_error_code
674 krb5_fcc_read_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
675 {
676     krb5_error_code kret;
677     krb5_ui_2 ui2;
678     krb5_int32 int32;
679
680     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
681
682     addr->magic = KV5M_ADDRESS;
683     addr->contents = 0;
684
685     kret = krb5_fcc_read_ui_2(context, id, &ui2);
686     CHECK(kret);
687     addr->addrtype = ui2;
688
689     kret = krb5_fcc_read_int32(context, id, &int32);
690     CHECK(kret);
691     if ((int32 & VALID_INT_BITS) != int32)     /* Overflow int??? */
692         return KRB5_CC_NOMEM;
693     addr->length = int32;
694     /* Length field is "unsigned int", which may be smaller than 32
695        bits.  */
696     if (addr->length != (krb5_ui_4) int32)
697         return KRB5_CC_NOMEM;  /* XXX */
698
699     if (addr->length == 0)
700         return KRB5_OK;
701
702     addr->contents = (krb5_octet *) malloc(addr->length);
703     if (addr->contents == NULL)
704         return KRB5_CC_NOMEM;
705
706     kret = krb5_fcc_read(context, id, addr->contents, addr->length);
707     CHECK(kret);
708
709     return KRB5_OK;
710 errout:
711     if (addr->contents) {
712         free(addr->contents);
713         addr->contents = NULL;
714     }
715     return kret;
716 }
717
718 static krb5_error_code
719 krb5_fcc_read_int32(krb5_context context, krb5_ccache id, krb5_int32 *i)
720 {
721     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
722     krb5_error_code retval;
723     unsigned char buf[4];
724
725     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
726
727     if ((data->version == KRB5_FCC_FVNO_1) ||
728         (data->version == KRB5_FCC_FVNO_2))
729         return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_int32));
730     else {
731         retval = krb5_fcc_read(context, id, buf, 4);
732         if (retval)
733             return retval;
734         *i = load_32_be (buf);
735         return 0;
736     }
737 }
738
739 static krb5_error_code
740 krb5_fcc_read_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 *i)
741 {
742     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
743     krb5_error_code retval;
744     unsigned char buf[2];
745
746     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
747
748     if ((data->version == KRB5_FCC_FVNO_1) ||
749         (data->version == KRB5_FCC_FVNO_2))
750         return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_ui_2));
751     else {
752         retval = krb5_fcc_read(context, id, buf, 2);
753         if (retval)
754             return retval;
755         *i = load_16_be (buf);
756         return 0;
757     }
758 }
759
760 static krb5_error_code
761 krb5_fcc_read_octet(krb5_context context, krb5_ccache id, krb5_octet *i)
762 {
763     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
764     return krb5_fcc_read(context, id, (krb5_pointer) i, 1);
765 }
766
767
768 static krb5_error_code
769 krb5_fcc_read_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
770 {
771     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
772     krb5_error_code retval;
773     krb5_int32 i;
774
775     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
776
777     if ((data->version == KRB5_FCC_FVNO_1) ||
778         (data->version == KRB5_FCC_FVNO_2))
779         return krb5_fcc_read(context, id, (krb5_pointer) t, sizeof(krb5_ticket_times));
780     else {
781         retval = krb5_fcc_read_int32(context, id, &i);
782         CHECK(retval);
783         t->authtime = i;
784
785         retval = krb5_fcc_read_int32(context, id, &i);
786         CHECK(retval);
787         t->starttime = i;
788
789         retval = krb5_fcc_read_int32(context, id, &i);
790         CHECK(retval);
791         t->endtime = i;
792
793         retval = krb5_fcc_read_int32(context, id, &i);
794         CHECK(retval);
795         t->renew_till = i;
796     }
797     return 0;
798 errout:
799     return retval;
800 }
801
802 static krb5_error_code
803 krb5_fcc_read_authdata(krb5_context context, krb5_ccache id, krb5_authdata ***a)
804 {
805     krb5_error_code kret;
806     krb5_int32 length;
807     size_t msize;
808     int i;
809
810     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
811
812     *a = 0;
813
814     /* Read the number of components */
815     kret = krb5_fcc_read_int32(context, id, &length);
816     CHECK(kret);
817
818     if (length == 0)
819         return KRB5_OK;
820
821     /* Make *a able to hold length pointers to krb5_authdata structs
822      * Add one extra for a null-terminated list
823      */
824     msize = length;
825     msize += 1;
826     if (msize == 0 || msize - 1 != (krb5_ui_4) length || length < 0)
827         return KRB5_CC_NOMEM;
828     *a = ALLOC (msize, krb5_authdata *);
829     if (*a == NULL)
830         return KRB5_CC_NOMEM;
831
832     for (i=0; i < length; i++) {
833         (*a)[i] = (krb5_authdata *) malloc(sizeof(krb5_authdata));
834         if ((*a)[i] == NULL) {
835             krb5_free_authdata(context, *a);
836             *a = NULL;
837             return KRB5_CC_NOMEM;
838         }
839         (*a)[i]->contents = NULL;
840         kret = krb5_fcc_read_authdatum(context, id, (*a)[i]);
841         CHECK(kret);
842     }
843
844     return KRB5_OK;
845 errout:
846     if (*a) {
847         krb5_free_authdata(context, *a);
848         *a = NULL;
849     }
850     return kret;
851 }
852
853 static krb5_error_code
854 krb5_fcc_read_authdatum(krb5_context context, krb5_ccache id, krb5_authdata *a)
855 {
856     krb5_error_code kret;
857     krb5_int32 int32;
858     krb5_int16 ui2; /* negative authorization data types are allowed */
859
860     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
861
862     a->magic = KV5M_AUTHDATA;
863     a->contents = NULL;
864
865     kret = krb5_fcc_read_ui_2(context, id, (krb5_ui_2 *)&ui2);
866     CHECK(kret);
867     a->ad_type = (krb5_authdatatype)ui2;
868     kret = krb5_fcc_read_int32(context, id, &int32);
869     CHECK(kret);
870     if ((int32 & VALID_INT_BITS) != int32)     /* Overflow int??? */
871         return KRB5_CC_NOMEM;
872     a->length = int32;
873     /* Value could have gotten truncated if int is smaller than 32
874        bits.  */
875     if (a->length != (krb5_ui_4) int32)
876         return KRB5_CC_NOMEM;   /* XXX */
877
878     if (a->length == 0 )
879         return KRB5_OK;
880
881     a->contents = (krb5_octet *) malloc(a->length);
882     if (a->contents == NULL)
883         return KRB5_CC_NOMEM;
884
885     kret = krb5_fcc_read(context, id, a->contents, a->length);
886     CHECK(kret);
887
888     return KRB5_OK;
889 errout:
890     if (a->contents) {
891         free(a->contents);
892         a->contents = NULL;
893     }
894     return kret;
895
896 }
897 #undef CHECK
898
899 #define CHECK(ret) if (ret != KRB5_OK) return ret;
900
901 /*
902  * Requires:
903  * id is open
904  *
905  * Effects:
906  * Writes len bytes from buf into the file cred cache id.
907  *
908  * Errors:
909  * system errors
910  */
911 static krb5_error_code
912 krb5_fcc_write(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
913 {
914     int ret;
915
916     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
917     invalidate_cache((krb5_fcc_data *) id->data);
918
919     ret = write(((krb5_fcc_data *)id->data)->file, (char *) buf, len);
920     if (ret < 0)
921         return krb5_fcc_interpret(context, errno);
922     if ((unsigned int) ret != len)
923         return KRB5_CC_WRITE;
924     return KRB5_OK;
925 }
926
927 /*
928  * FOR ALL OF THE FOLLOWING FUNCTIONS:
929  *
930  * Requires:
931  * ((krb5_fcc_data *) id->data)->file is open and at the right position.
932  *
933  * mutex is locked
934  *
935  * Effects:
936  * Stores an encoded version of the second argument in the
937  * cache file.
938  *
939  * Errors:
940  * system errors
941  */
942
943 static krb5_error_code
944 krb5_fcc_store_principal(krb5_context context, krb5_ccache id, krb5_principal princ)
945 {
946     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
947     krb5_error_code ret;
948     krb5_int32 i, length, tmp, type;
949
950     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
951
952     type = krb5_princ_type(context, princ);
953     tmp = length = krb5_princ_size(context, princ);
954
955     if (data->version == KRB5_FCC_FVNO_1) {
956         /*
957          * DCE-compatible format means that the length count
958          * includes the realm.  (It also doesn't include the
959          * principal type information.)
960          */
961         tmp++;
962     } else {
963         ret = krb5_fcc_store_int32(context, id, type);
964         CHECK(ret);
965     }
966
967     ret = krb5_fcc_store_int32(context, id, tmp);
968     CHECK(ret);
969
970     ret = krb5_fcc_store_data(context, id, krb5_princ_realm(context, princ));
971     CHECK(ret);
972
973     for (i=0; i < length; i++) {
974         ret = krb5_fcc_store_data(context, id, krb5_princ_component(context, princ, i));
975         CHECK(ret);
976     }
977
978     return KRB5_OK;
979 }
980
981 static krb5_error_code
982 krb5_fcc_store_addrs(krb5_context context, krb5_ccache id, krb5_address **addrs)
983 {
984     krb5_error_code ret;
985     krb5_address **temp;
986     krb5_int32 i, length = 0;
987
988     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
989
990     /* Count the number of components */
991     if (addrs) {
992         temp = addrs;
993         while (*temp++)
994             length += 1;
995     }
996
997     ret = krb5_fcc_store_int32(context, id, length);
998     CHECK(ret);
999     for (i=0; i < length; i++) {
1000         ret = krb5_fcc_store_addr(context, id, addrs[i]);
1001         CHECK(ret);
1002     }
1003
1004     return KRB5_OK;
1005 }
1006
1007 static krb5_error_code
1008 krb5_fcc_store_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
1009 {
1010     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1011     krb5_error_code ret;
1012
1013     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1014
1015     ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
1016     CHECK(ret);
1017     if (data->version == KRB5_FCC_FVNO_3) {
1018         ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
1019         CHECK(ret);
1020     }
1021     ret = krb5_fcc_store_ui_4(context, id, keyblock->length);
1022     CHECK(ret);
1023     return krb5_fcc_write(context, id, (char *) keyblock->contents, keyblock->length);
1024 }
1025
1026 static krb5_error_code
1027 krb5_fcc_store_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
1028 {
1029     krb5_error_code ret;
1030
1031     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1032
1033     ret = krb5_fcc_store_ui_2(context, id, addr->addrtype);
1034     CHECK(ret);
1035     ret = krb5_fcc_store_ui_4(context, id, addr->length);
1036     CHECK(ret);
1037     return krb5_fcc_write(context, id, (char *) addr->contents, addr->length);
1038 }
1039
1040
1041 static krb5_error_code
1042 krb5_fcc_store_data(krb5_context context, krb5_ccache id, krb5_data *data)
1043 {
1044     krb5_error_code ret;
1045
1046     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1047
1048     ret = krb5_fcc_store_ui_4(context, id, data->length);
1049     CHECK(ret);
1050     return krb5_fcc_write(context, id, data->data, data->length);
1051 }
1052
1053 static krb5_error_code
1054 krb5_fcc_store_int32(krb5_context context, krb5_ccache id, krb5_int32 i)
1055 {
1056     return krb5_fcc_store_ui_4(context, id, (krb5_ui_4) i);
1057 }
1058
1059 static krb5_error_code
1060 krb5_fcc_store_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i)
1061 {
1062     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1063     unsigned char buf[4];
1064
1065     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1066
1067     if ((data->version == KRB5_FCC_FVNO_1) ||
1068         (data->version == KRB5_FCC_FVNO_2))
1069         return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32));
1070     else {
1071         store_32_be (i, buf);
1072         return krb5_fcc_write(context, id, buf, 4);
1073     }
1074 }
1075
1076 static krb5_error_code
1077 krb5_fcc_store_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i)
1078 {
1079     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1080     krb5_ui_2 ibuf;
1081     unsigned char buf[2];
1082
1083     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1084
1085     if ((data->version == KRB5_FCC_FVNO_1) ||
1086         (data->version == KRB5_FCC_FVNO_2)) {
1087         ibuf = (krb5_ui_2) i;
1088         return krb5_fcc_write(context, id, (char *) &ibuf, sizeof(krb5_ui_2));
1089     } else {
1090         store_16_be (i, buf);
1091         return krb5_fcc_write(context, id, buf, 2);
1092     }
1093 }
1094
1095 static krb5_error_code
1096 krb5_fcc_store_octet(krb5_context context, krb5_ccache id, krb5_int32 i)
1097 {
1098     krb5_octet ibuf;
1099
1100     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1101
1102     ibuf = (krb5_octet) i;
1103     return krb5_fcc_write(context, id, (char *) &ibuf, 1);
1104 }
1105
1106 static krb5_error_code
1107 krb5_fcc_store_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
1108 {
1109     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1110     krb5_error_code retval;
1111
1112     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1113
1114     if ((data->version == KRB5_FCC_FVNO_1) ||
1115         (data->version == KRB5_FCC_FVNO_2))
1116         return krb5_fcc_write(context, id, (char *) t, sizeof(krb5_ticket_times));
1117     else {
1118         retval = krb5_fcc_store_int32(context, id, t->authtime);
1119         CHECK(retval);
1120         retval = krb5_fcc_store_int32(context, id, t->starttime);
1121         CHECK(retval);
1122         retval = krb5_fcc_store_int32(context, id, t->endtime);
1123         CHECK(retval);
1124         retval = krb5_fcc_store_int32(context, id, t->renew_till);
1125         CHECK(retval);
1126         return 0;
1127     }
1128 }
1129
1130 static krb5_error_code
1131 krb5_fcc_store_authdata(krb5_context context, krb5_ccache id, krb5_authdata **a)
1132 {
1133     krb5_error_code ret;
1134     krb5_authdata **temp;
1135     krb5_int32 i, length=0;
1136
1137     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1138
1139     if (a != NULL) {
1140         for (temp=a; *temp; temp++)
1141             length++;
1142     }
1143
1144     ret = krb5_fcc_store_int32(context, id, length);
1145     CHECK(ret);
1146     for (i=0; i<length; i++) {
1147         ret = krb5_fcc_store_authdatum (context, id, a[i]);
1148         CHECK(ret);
1149     }
1150     return KRB5_OK;
1151 }
1152
1153 static krb5_error_code
1154 krb5_fcc_store_authdatum (krb5_context context, krb5_ccache id, krb5_authdata *a)
1155 {
1156     krb5_error_code ret;
1157
1158     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1159
1160     ret = krb5_fcc_store_ui_2(context, id, a->ad_type);
1161     CHECK(ret);
1162     ret = krb5_fcc_store_ui_4(context, id, a->length);
1163     CHECK(ret);
1164     return krb5_fcc_write(context, id, (krb5_pointer) a->contents, a->length);
1165 }
1166 #undef CHECK
1167
1168 static krb5_error_code
1169 krb5_fcc_close_file (krb5_context context, krb5_fcc_data *data)
1170 {
1171     int ret;
1172     krb5_error_code retval;
1173
1174     k5_cc_mutex_assert_locked(context, &data->lock);
1175
1176     if (data->file == NO_FILE)
1177         return KRB5_FCC_INTERNAL;
1178
1179     retval = krb5_unlock_file(context, data->file);
1180     ret = close (data->file);
1181     data->file = NO_FILE;
1182     if (retval)
1183         return retval;
1184
1185     return ret ? krb5_fcc_interpret (context, errno) : 0;
1186 }
1187
1188 #if defined(ANSI_STDIO) || defined(_WIN32)
1189 #define BINARY_MODE "b"
1190 #else
1191 #define BINARY_MODE ""
1192 #endif
1193
1194 #ifndef HAVE_SETVBUF
1195 #undef setvbuf
1196 #define setvbuf(FILE,BUF,MODE,SIZE)                             \
1197     ((SIZE) < BUFSIZE ? (abort(),0) : setbuf(FILE, BUF))
1198 #endif
1199
1200 static krb5_error_code
1201 krb5_fcc_open_file (krb5_context context, krb5_ccache id, int mode)
1202 {
1203     krb5_os_context os_ctx = &context->os_context;
1204     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1205     krb5_ui_2 fcc_fvno;
1206     krb5_ui_2 fcc_flen;
1207     krb5_ui_2 fcc_tag;
1208     krb5_ui_2 fcc_taglen;
1209     int f, open_flag;
1210     int lock_flag;
1211     krb5_error_code retval = 0;
1212
1213     k5_cc_mutex_assert_locked(context, &data->lock);
1214     invalidate_cache(data);
1215
1216     if (data->file != NO_FILE) {
1217         /* Don't know what state it's in; shut down and start anew.  */
1218         (void) krb5_unlock_file(context, data->file);
1219         (void) close (data->file);
1220         data->file = NO_FILE;
1221     }
1222
1223     switch(mode) {
1224     case FCC_OPEN_AND_ERASE:
1225         unlink(data->filename);
1226         open_flag = O_CREAT|O_EXCL|O_TRUNC|O_RDWR;
1227         break;
1228     case FCC_OPEN_RDWR:
1229         open_flag = O_RDWR;
1230         break;
1231     case FCC_OPEN_RDONLY:
1232     default:
1233         open_flag = O_RDONLY;
1234         break;
1235     }
1236
1237     f = THREEPARAMOPEN (data->filename, open_flag | O_BINARY, 0600);
1238     if (f == NO_FILE) {
1239         switch (errno) {
1240         case ENOENT:
1241             retval = KRB5_FCC_NOFILE;
1242             krb5_set_error_message(context, retval,
1243                                    "Credentials cache file '%s' not found",
1244                                    data->filename);
1245             return retval;
1246         default:
1247             return krb5_fcc_interpret (context, errno);
1248         }
1249     }
1250     set_cloexec_fd(f);
1251
1252     data->mode = mode;
1253
1254     if (data->mode == FCC_OPEN_RDONLY)
1255         lock_flag = KRB5_LOCKMODE_SHARED;
1256     else
1257         lock_flag = KRB5_LOCKMODE_EXCLUSIVE;
1258     if ((retval = krb5_lock_file(context, f, lock_flag))) {
1259         (void) close(f);
1260         return retval;
1261     }
1262
1263     if (mode == FCC_OPEN_AND_ERASE) {
1264         /* write the version number */
1265         int cnt;
1266
1267         fcc_fvno = htons(context->fcc_default_format);
1268         data->version = context->fcc_default_format;
1269         if ((cnt = write(f, (char *)&fcc_fvno, sizeof(fcc_fvno))) !=
1270             sizeof(fcc_fvno)) {
1271             retval = ((cnt == -1) ? krb5_fcc_interpret(context, errno) :
1272                       KRB5_CC_IO);
1273             goto done;
1274         }
1275         data->file = f;
1276
1277         if (data->version == KRB5_FCC_FVNO_4) {
1278             /* V4 of the credentials cache format allows for header tags */
1279             fcc_flen = 0;
1280
1281             if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
1282                 fcc_flen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32));
1283
1284             /* Write header length */
1285             retval = krb5_fcc_store_ui_2(context, id, (krb5_int32)fcc_flen);
1286             if (retval) goto done;
1287
1288             if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
1289                 /* Write time offset tag */
1290                 fcc_tag = FCC_TAG_DELTATIME;
1291                 fcc_taglen = 2*sizeof(krb5_int32);
1292
1293                 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_tag);
1294                 if (retval) goto done;
1295                 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_taglen);
1296                 if (retval) goto done;
1297                 retval = krb5_fcc_store_int32(context,id,os_ctx->time_offset);
1298                 if (retval) goto done;
1299                 retval = krb5_fcc_store_int32(context,id,os_ctx->usec_offset);
1300                 if (retval) goto done;
1301             }
1302         }
1303         invalidate_cache(data);
1304         goto done;
1305     }
1306
1307     /* verify a valid version number is there */
1308     invalidate_cache(data);
1309     if (read(f, (char *)&fcc_fvno, sizeof(fcc_fvno)) != sizeof(fcc_fvno)) {
1310         retval = KRB5_CC_FORMAT;
1311         goto done;
1312     }
1313     data->version = ntohs(fcc_fvno);
1314     if ((data->version != KRB5_FCC_FVNO_4) &&
1315         (data->version != KRB5_FCC_FVNO_3) &&
1316         (data->version != KRB5_FCC_FVNO_2) &&
1317         (data->version != KRB5_FCC_FVNO_1)) {
1318         retval = KRB5_CCACHE_BADVNO;
1319         goto done;
1320     }
1321
1322     data->file = f;
1323
1324     if (data->version == KRB5_FCC_FVNO_4) {
1325         char buf[1024];
1326
1327         if (krb5_fcc_read_ui_2(context, id, &fcc_flen) ||
1328             (fcc_flen > sizeof(buf)))
1329         {
1330             retval = KRB5_CC_FORMAT;
1331             goto done;
1332         }
1333
1334         while (fcc_flen) {
1335             if ((fcc_flen < (2 * sizeof(krb5_ui_2))) ||
1336                 krb5_fcc_read_ui_2(context, id, &fcc_tag) ||
1337                 krb5_fcc_read_ui_2(context, id, &fcc_taglen) ||
1338                 (fcc_taglen > (fcc_flen - 2*sizeof(krb5_ui_2))))
1339             {
1340                 retval = KRB5_CC_FORMAT;
1341                 goto done;
1342             }
1343
1344             switch (fcc_tag) {
1345             case FCC_TAG_DELTATIME:
1346                 if (fcc_taglen != 2*sizeof(krb5_int32)) {
1347                     retval = KRB5_CC_FORMAT;
1348                     goto done;
1349                 }
1350                 if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
1351                     (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID))
1352                 {
1353                     if (krb5_fcc_read(context, id, buf, fcc_taglen)) {
1354                         retval = KRB5_CC_FORMAT;
1355                         goto done;
1356                     }
1357                     break;
1358                 }
1359                 if (krb5_fcc_read_int32(context, id, &os_ctx->time_offset) ||
1360                     krb5_fcc_read_int32(context, id, &os_ctx->usec_offset))
1361                 {
1362                     retval = KRB5_CC_FORMAT;
1363                     goto done;
1364                 }
1365                 os_ctx->os_flags =
1366                     ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
1367                      KRB5_OS_TOFFSET_VALID);
1368                 break;
1369             default:
1370                 if (fcc_taglen && krb5_fcc_read(context,id,buf,fcc_taglen)) {
1371                     retval = KRB5_CC_FORMAT;
1372                     goto done;
1373                 }
1374                 break;
1375             }
1376             fcc_flen -= (2*sizeof(krb5_ui_2) + fcc_taglen);
1377         }
1378     }
1379
1380 done:
1381     if (retval) {
1382         data->file = -1;
1383         (void) krb5_unlock_file(context, f);
1384         (void) close(f);
1385     }
1386     return retval;
1387 }
1388
1389 static krb5_error_code
1390 krb5_fcc_skip_header(krb5_context context, krb5_ccache id)
1391 {
1392     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1393     krb5_error_code kret;
1394     krb5_ui_2 fcc_flen;
1395
1396     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1397
1398     fcc_lseek(data, (off_t) sizeof(krb5_ui_2), SEEK_SET);
1399     if (data->version == KRB5_FCC_FVNO_4) {
1400         kret = krb5_fcc_read_ui_2(context, id, &fcc_flen);
1401         if (kret) return kret;
1402         if(fcc_lseek(data, (off_t) fcc_flen, SEEK_CUR) < 0)
1403             return errno;
1404     }
1405     return KRB5_OK;
1406 }
1407
1408 static krb5_error_code
1409 krb5_fcc_skip_principal(krb5_context context, krb5_ccache id)
1410 {
1411     krb5_error_code kret;
1412     krb5_principal princ;
1413
1414     k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1415
1416     kret = krb5_fcc_read_principal(context, id, &princ);
1417     if (kret != KRB5_OK)
1418         return kret;
1419
1420     krb5_free_principal(context, princ);
1421     return KRB5_OK;
1422 }
1423
1424
1425 /*
1426  * Modifies:
1427  * id
1428  *
1429  * Effects:
1430  * Creates/refreshes the file cred cache id.  If the cache exists, its
1431  * contents are destroyed.
1432  *
1433  * Errors:
1434  * system errors
1435  * permission errors
1436  */
1437 static krb5_error_code KRB5_CALLCONV
1438 krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
1439 {
1440     krb5_error_code kret = 0;
1441     int reti = 0;
1442
1443     kret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
1444     if (kret)
1445         return kret;
1446
1447     MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE);
1448
1449 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
1450     {
1451 #ifdef HAVE_FCHMOD
1452         reti = fchmod(((krb5_fcc_data *) id->data)->file, S_IREAD | S_IWRITE);
1453 #else
1454         reti = chmod(((krb5_fcc_data *) id->data)->filename, S_IREAD | S_IWRITE);
1455 #endif
1456         if (reti == -1) {
1457             kret = krb5_fcc_interpret(context, errno);
1458             MAYBE_CLOSE(context, id, kret);
1459             k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
1460             return kret;
1461         }
1462     }
1463 #endif
1464     kret = krb5_fcc_store_principal(context, id, princ);
1465
1466     MAYBE_CLOSE(context, id, kret);
1467     k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
1468     krb5_change_cache ();
1469     return kret;
1470 }
1471
1472 /*
1473  * Drop the ref count; if it hits zero, remove the entry from the
1474  * fcc_set list and free it.
1475  */
1476 static krb5_error_code dereference(krb5_context context, krb5_fcc_data *data)
1477 {
1478     krb5_error_code kerr;
1479     struct fcc_set **fccsp;
1480
1481     kerr = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
1482     if (kerr)
1483         return kerr;
1484     for (fccsp = &fccs; *fccsp != NULL; fccsp = &(*fccsp)->next)
1485         if ((*fccsp)->data == data)
1486             break;
1487     assert(*fccsp != NULL);
1488     assert((*fccsp)->data == data);
1489     (*fccsp)->refcount--;
1490     if ((*fccsp)->refcount == 0) {
1491         struct fcc_set *temp;
1492         data = (*fccsp)->data;
1493         temp = *fccsp;
1494         *fccsp = (*fccsp)->next;
1495         free(temp);
1496         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1497         k5_cc_mutex_assert_unlocked(context, &data->lock);
1498         free(data->filename);
1499         zap(data->buf, sizeof(data->buf));
1500         if (data->file >= 0) {
1501             kerr = k5_cc_mutex_lock(context, &data->lock);
1502             if (kerr)
1503                 return kerr;
1504             krb5_fcc_close_file(context, data);
1505             k5_cc_mutex_unlock(context, &data->lock);
1506         }
1507         k5_cc_mutex_destroy(&data->lock);
1508         free(data);
1509     } else
1510         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1511     return 0;
1512 }
1513
1514 /*
1515  * Modifies:
1516  * id
1517  *
1518  * Effects:
1519  * Closes the file cache, invalidates the id, and frees any resources
1520  * associated with the cache.
1521  */
1522 static krb5_error_code KRB5_CALLCONV
1523 krb5_fcc_close(krb5_context context, krb5_ccache id)
1524 {
1525     dereference(context, (krb5_fcc_data *) id->data);
1526     free(id);
1527     return KRB5_OK;
1528 }
1529
1530 /*
1531  * Effects:
1532  * Destroys the contents of id.
1533  *
1534  * Errors:
1535  * system errors
1536  */
1537 static krb5_error_code KRB5_CALLCONV
1538 krb5_fcc_destroy(krb5_context context, krb5_ccache id)
1539 {
1540     krb5_error_code kret = 0;
1541     krb5_fcc_data *data = (krb5_fcc_data *) id->data;
1542     register int ret;
1543
1544     struct stat buf;
1545     unsigned long i, size;
1546     unsigned int wlen;
1547     char zeros[BUFSIZ];
1548
1549     kret = k5_cc_mutex_lock(context, &data->lock);
1550     if (kret)
1551         return kret;
1552
1553     if (OPENCLOSE(id)) {
1554         invalidate_cache(data);
1555         ret = THREEPARAMOPEN(data->filename,
1556                              O_RDWR | O_BINARY, 0);
1557         if (ret < 0) {
1558             kret = krb5_fcc_interpret(context, errno);
1559             goto cleanup;
1560         }
1561         set_cloexec_fd(ret);
1562         data->file = ret;
1563     }
1564     else
1565         fcc_lseek(data, (off_t) 0, SEEK_SET);
1566
1567 #ifdef MSDOS_FILESYSTEM
1568 /* "disgusting bit of UNIX trivia" - that's how the writers of NFS describe
1569 ** the ability of UNIX to still write to a file which has been unlinked.
1570 ** Naturally, the PC can't do this. As a result, we have to delete the file
1571 ** after we wipe it clean but that throws off all the error handling code.
1572 ** So we have do the work ourselves.
1573 */
1574     ret = fstat(data->file, &buf);
1575     if (ret == -1) {
1576         kret = krb5_fcc_interpret(context, errno);
1577         size = 0;                               /* Nothing to wipe clean */
1578     } else
1579         size = (unsigned long) buf.st_size;
1580
1581     memset(zeros, 0, BUFSIZ);
1582     while (size > 0) {
1583         wlen = (int) ((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */
1584         i = write(data->file, zeros, wlen);
1585         if (i < 0) {
1586             kret = krb5_fcc_interpret(context, errno);
1587             /* Don't jump to cleanup--we still want to delete the file. */
1588             break;
1589         }
1590         size -= i;                              /* We've read this much */
1591     }
1592
1593     if (OPENCLOSE(id)) {
1594         (void) close(((krb5_fcc_data *)id->data)->file);
1595         data->file = -1;
1596     }
1597
1598     ret = unlink(data->filename);
1599     if (ret < 0) {
1600         kret = krb5_fcc_interpret(context, errno);
1601         goto cleanup;
1602     }
1603
1604 #else /* MSDOS_FILESYSTEM */
1605
1606     ret = unlink(data->filename);
1607     if (ret < 0) {
1608         kret = krb5_fcc_interpret(context, errno);
1609         if (OPENCLOSE(id)) {
1610             (void) close(((krb5_fcc_data *)id->data)->file);
1611             data->file = -1;
1612             kret = ret;
1613         }
1614         goto cleanup;
1615     }
1616
1617     ret = fstat(data->file, &buf);
1618     if (ret < 0) {
1619         kret = krb5_fcc_interpret(context, errno);
1620         if (OPENCLOSE(id)) {
1621             (void) close(((krb5_fcc_data *)id->data)->file);
1622             data->file = -1;
1623         }
1624         goto cleanup;
1625     }
1626
1627     /* XXX This may not be legal XXX */
1628     size = (unsigned long) buf.st_size;
1629     memset(zeros, 0, BUFSIZ);
1630     for (i=0; i < size / BUFSIZ; i++)
1631         if (write(data->file, zeros, BUFSIZ) < 0) {
1632             kret = krb5_fcc_interpret(context, errno);
1633             if (OPENCLOSE(id)) {
1634                 (void) close(((krb5_fcc_data *)id->data)->file);
1635                 data->file = -1;
1636             }
1637             goto cleanup;
1638         }
1639
1640     wlen = (unsigned int) (size % BUFSIZ);
1641     if (write(data->file, zeros, wlen) < 0) {
1642         kret = krb5_fcc_interpret(context, errno);
1643         if (OPENCLOSE(id)) {
1644             (void) close(((krb5_fcc_data *)id->data)->file);
1645             data->file = -1;
1646         }
1647         goto cleanup;
1648     }
1649
1650     ret = close(data->file);
1651     data->file = -1;
1652
1653     if (ret)
1654         kret = krb5_fcc_interpret(context, errno);
1655
1656 #endif /* MSDOS_FILESYSTEM */
1657
1658 cleanup:
1659     k5_cc_mutex_unlock(context, &data->lock);
1660     dereference(context, data);
1661     free(id);
1662
1663     krb5_change_cache ();
1664     return kret;
1665 }
1666
1667 extern const krb5_cc_ops krb5_fcc_ops;
1668
1669 /*
1670  * Requires:
1671  * residual is a legal path name, and a null-terminated string
1672  *
1673  * Modifies:
1674  * id
1675  *
1676  * Effects:
1677  * creates a file-based cred cache that will reside in the file
1678  * residual.  The cache is not opened, but the filename is reserved.
1679  *
1680  * Returns:
1681  * A filled in krb5_ccache structure "id".
1682  *
1683  * Errors:
1684  * KRB5_CC_NOMEM - there was insufficient memory to allocate the
1685  *              krb5_ccache.  id is undefined.
1686  * permission errors
1687  */
1688 static krb5_error_code KRB5_CALLCONV
1689 krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
1690 {
1691     krb5_ccache lid;
1692     krb5_error_code kret;
1693     krb5_fcc_data *data;
1694     struct fcc_set *setptr;
1695
1696     kret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
1697     if (kret)
1698         return kret;
1699     for (setptr = fccs; setptr; setptr = setptr->next) {
1700         if (!strcmp(setptr->data->filename, residual))
1701             break;
1702     }
1703     if (setptr) {
1704         data = setptr->data;
1705         assert(setptr->refcount != 0);
1706         setptr->refcount++;
1707         assert(setptr->refcount != 0);
1708         kret = k5_cc_mutex_lock(context, &data->lock);
1709         if (kret) {
1710             k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1711             return kret;
1712         }
1713         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1714     } else {
1715         data = malloc(sizeof(krb5_fcc_data));
1716         if (data == NULL) {
1717             k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1718             return KRB5_CC_NOMEM;
1719         }
1720         data->filename = strdup(residual);
1721         if (data->filename == NULL) {
1722             k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1723             free(data);
1724             return KRB5_CC_NOMEM;
1725         }
1726         kret = k5_cc_mutex_init(&data->lock);
1727         if (kret) {
1728             k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1729             free(data->filename);
1730             free(data);
1731             return kret;
1732         }
1733         kret = k5_cc_mutex_lock(context, &data->lock);
1734         if (kret) {
1735             k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1736             k5_cc_mutex_destroy(&data->lock);
1737             free(data->filename);
1738             free(data);
1739             return kret;
1740         }
1741         /* data->version,mode filled in for real later */
1742         data->version = data->mode = 0;
1743         data->flags = KRB5_TC_OPENCLOSE;
1744         data->file = -1;
1745         data->valid_bytes = 0;
1746         setptr = malloc(sizeof(struct fcc_set));
1747         if (setptr == NULL) {
1748             k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1749             k5_cc_mutex_unlock(context, &data->lock);
1750             k5_cc_mutex_destroy(&data->lock);
1751             free(data->filename);
1752             free(data);
1753             return KRB5_CC_NOMEM;
1754         }
1755         setptr->refcount = 1;
1756         setptr->data = data;
1757         setptr->next = fccs;
1758         fccs = setptr;
1759         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1760     }
1761
1762     k5_cc_mutex_assert_locked(context, &data->lock);
1763     k5_cc_mutex_unlock(context, &data->lock);
1764     lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
1765     if (lid == NULL) {
1766         dereference(context, data);
1767         return KRB5_CC_NOMEM;
1768     }
1769
1770     lid->ops = &krb5_fcc_ops;
1771     lid->data = data;
1772     lid->magic = KV5M_CCACHE;
1773
1774     /* other routines will get errors on open, and callers must expect them,
1775        if cache is non-existent/unusable */
1776     *id = lid;
1777     return KRB5_OK;
1778 }
1779
1780 /*
1781  * Effects:
1782  * Prepares for a sequential search of the credentials cache.
1783  * Returns and krb5_cc_cursor to be used with krb5_fcc_next_cred and
1784  * krb5_fcc_end_seq_get.
1785  *
1786  * If the cache is modified between the time of this call and the time
1787  * of the final krb5_fcc_end_seq_get, the results are undefined.
1788  *
1789  * Errors:
1790  * KRB5_CC_NOMEM
1791  * system errors
1792  */
1793 static krb5_error_code KRB5_CALLCONV
1794 krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id,
1795                        krb5_cc_cursor *cursor)
1796 {
1797     krb5_fcc_cursor *fcursor;
1798     krb5_error_code kret = KRB5_OK;
1799     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1800
1801     kret = k5_cc_mutex_lock(context, &data->lock);
1802     if (kret)
1803         return kret;
1804
1805     fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor));
1806     if (fcursor == NULL) {
1807         k5_cc_mutex_unlock(context, &data->lock);
1808         return KRB5_CC_NOMEM;
1809     }
1810     if (OPENCLOSE(id)) {
1811         kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY);
1812         if (kret) {
1813             free(fcursor);
1814             k5_cc_mutex_unlock(context, &data->lock);
1815             return kret;
1816         }
1817     }
1818
1819     /* Make sure we start reading right after the primary principal */
1820     kret = krb5_fcc_skip_header(context, id);
1821     if (kret) {
1822         free(fcursor);
1823         goto done;
1824     }
1825     kret = krb5_fcc_skip_principal(context, id);
1826     if (kret) {
1827         free(fcursor);
1828         goto done;
1829     }
1830
1831     fcursor->pos = fcc_lseek(data, (off_t) 0, SEEK_CUR);
1832     *cursor = (krb5_cc_cursor) fcursor;
1833
1834 done:
1835     MAYBE_CLOSE(context, id, kret);
1836     k5_cc_mutex_unlock(context, &data->lock);
1837     return kret;
1838 }
1839
1840
1841 /*
1842  * Requires:
1843  * cursor is a krb5_cc_cursor originally obtained from
1844  * krb5_fcc_start_seq_get.
1845  *
1846  * Modifes:
1847  * cursor, creds
1848  *
1849  * Effects:
1850  * Fills in creds with the "next" credentals structure from the cache
1851  * id.  The actual order the creds are returned in is arbitrary.
1852  * Space is allocated for the variable length fields in the
1853  * credentials structure, so the object returned must be passed to
1854  * krb5_destroy_credential.
1855  *
1856  * The cursor is updated for the next call to krb5_fcc_next_cred.
1857  *
1858  * Errors:
1859  * system errors
1860  */
1861 static krb5_error_code KRB5_CALLCONV
1862 krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
1863                    krb5_creds *creds)
1864 {
1865 #define TCHECK(ret) if (ret != KRB5_OK) goto lose;
1866     krb5_error_code kret;
1867     krb5_fcc_cursor *fcursor;
1868     krb5_int32 int32;
1869     krb5_octet octet;
1870     krb5_fcc_data *d = (krb5_fcc_data *) id->data;
1871
1872     kret = k5_cc_mutex_lock(context, &d->lock);
1873     if (kret)
1874         return kret;
1875
1876     memset(creds, 0, sizeof(*creds));
1877     MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
1878     fcursor = (krb5_fcc_cursor *) *cursor;
1879
1880     kret = (fcc_lseek(d, fcursor->pos, SEEK_SET) == (off_t) -1);
1881     if (kret) {
1882         kret = krb5_fcc_interpret(context, errno);
1883         MAYBE_CLOSE(context, id, kret);
1884         k5_cc_mutex_unlock(context, &d->lock);
1885         return kret;
1886     }
1887
1888     kret = krb5_fcc_read_principal(context, id, &creds->client);
1889     TCHECK(kret);
1890     kret = krb5_fcc_read_principal(context, id, &creds->server);
1891     TCHECK(kret);
1892     kret = krb5_fcc_read_keyblock(context, id, &creds->keyblock);
1893     TCHECK(kret);
1894     kret = krb5_fcc_read_times(context, id, &creds->times);
1895     TCHECK(kret);
1896     kret = krb5_fcc_read_octet(context, id, &octet);
1897     TCHECK(kret);
1898     creds->is_skey = octet;
1899     kret = krb5_fcc_read_int32(context, id, &int32);
1900     TCHECK(kret);
1901     creds->ticket_flags = int32;
1902     kret = krb5_fcc_read_addrs(context, id, &creds->addresses);
1903     TCHECK(kret);
1904     kret = krb5_fcc_read_authdata(context, id, &creds->authdata);
1905     TCHECK(kret);
1906     kret = krb5_fcc_read_data(context, id, &creds->ticket);
1907     TCHECK(kret);
1908     kret = krb5_fcc_read_data(context, id, &creds->second_ticket);
1909     TCHECK(kret);
1910
1911     fcursor->pos = fcc_lseek(d, (off_t) 0, SEEK_CUR);
1912
1913 lose:
1914     MAYBE_CLOSE (context, id, kret);
1915     k5_cc_mutex_unlock(context, &d->lock);
1916     if (kret != KRB5_OK)
1917         krb5_free_cred_contents(context, creds);
1918     return kret;
1919 }
1920
1921 /*
1922  * Requires:
1923  * cursor is a krb5_cc_cursor originally obtained from
1924  * krb5_fcc_start_seq_get.
1925  *
1926  * Modifies:
1927  * id, cursor
1928  *
1929  * Effects:
1930  * Finishes sequential processing of the file credentials ccache id,
1931  * and invalidates the cursor (it must never be used after this call).
1932  */
1933 /* ARGSUSED */
1934 static krb5_error_code KRB5_CALLCONV
1935 krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
1936 {
1937     /* We don't do anything with the file cache itself, so
1938        no need to lock anything.  */
1939
1940     /* don't close; it may be left open by the caller,
1941        and if not, fcc_start_seq_get and/or fcc_next_cred will do the
1942        MAYBE_CLOSE.
1943        MAYBE_CLOSE(context, id, kret); */
1944     free((krb5_fcc_cursor *) *cursor);
1945     return 0;
1946 }
1947
1948
1949 /*
1950  * Effects:
1951  * Creates a new file cred cache whose name is guaranteed to be
1952  * unique.  The name begins with the string TKT_ROOT (from fcc.h).
1953  * The cache is not opened, but the new filename is reserved.
1954  *
1955  * Returns:
1956  * The filled in krb5_ccache id.
1957  *
1958  * Errors:
1959  * KRB5_CC_NOMEM - there was insufficient memory to allocate the
1960  *              krb5_ccache.  id is undefined.
1961  * system errors (from open)
1962  */
1963 static krb5_error_code KRB5_CALLCONV
1964 krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
1965 {
1966     krb5_ccache lid;
1967     int ret;
1968     krb5_error_code    kret = 0;
1969     char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for
1970                                            NUL */
1971     krb5_fcc_data *data;
1972     krb5_int16 fcc_fvno = htons(context->fcc_default_format);
1973     krb5_int16 fcc_flen = 0;
1974     int errsave, cnt;
1975     struct fcc_set *setptr;
1976
1977     /* Set master lock */
1978     kret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
1979     if (kret)
1980         return kret;
1981
1982     (void) snprintf(scratch, sizeof(scratch), "%sXXXXXX", TKT_ROOT);
1983     ret = mkstemp(scratch);
1984     if (ret == -1) {
1985         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1986         return krb5_fcc_interpret(context, errno);
1987     }
1988     set_cloexec_fd(ret);
1989
1990     /* Allocate memory */
1991     data = (krb5_pointer) malloc(sizeof(krb5_fcc_data));
1992     if (data == NULL) {
1993         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1994         close(ret);
1995         unlink(scratch);
1996         return KRB5_CC_NOMEM;
1997     }
1998
1999     data->filename = strdup(scratch);
2000     if (data->filename == NULL) {
2001         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2002         free(data);
2003         close(ret);
2004         unlink(scratch);
2005         return KRB5_CC_NOMEM;
2006     }
2007
2008     kret = k5_cc_mutex_init(&data->lock);
2009     if (kret) {
2010         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2011         free(data->filename);
2012         free(data);
2013         close(ret);
2014         unlink(scratch);
2015         return kret;
2016     }
2017     kret = k5_cc_mutex_lock(context, &data->lock);
2018     if (kret) {
2019         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2020         k5_cc_mutex_destroy(&data->lock);
2021         free(data->filename);
2022         free(data);
2023         close(ret);
2024         unlink(scratch);
2025         return kret;
2026     }
2027
2028     /*
2029      * The file is initially closed at the end of this call...
2030      */
2031     data->flags = 0;
2032     data->file = -1;
2033     data->valid_bytes = 0;
2034     /* data->version,mode filled in for real later */
2035     data->version = data->mode = 0;
2036
2037
2038     /* Ignore user's umask, set mode = 0600 */
2039 #ifndef HAVE_FCHMOD
2040 #ifdef HAVE_CHMOD
2041     chmod(data->filename, S_IRUSR | S_IWUSR);
2042 #endif
2043 #else
2044     fchmod(ret, S_IRUSR | S_IWUSR);
2045 #endif
2046     if ((cnt = write(ret, (char *)&fcc_fvno, sizeof(fcc_fvno)))
2047         != sizeof(fcc_fvno)) {
2048         errsave = errno;
2049         (void) close(ret);
2050         (void) unlink(data->filename);
2051         kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2052         goto err_out;
2053     }
2054     /* For version 4 we save a length for the rest of the header */
2055     if (context->fcc_default_format == KRB5_FCC_FVNO_4) {
2056         if ((cnt = write(ret, (char *)&fcc_flen, sizeof(fcc_flen)))
2057             != sizeof(fcc_flen)) {
2058             errsave = errno;
2059             (void) close(ret);
2060             (void) unlink(data->filename);
2061             kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2062             goto err_out;
2063         }
2064     }
2065     if (close(ret) == -1) {
2066         errsave = errno;
2067         (void) unlink(data->filename);
2068         kret = krb5_fcc_interpret(context, errsave);
2069         goto err_out;
2070     }
2071
2072
2073     setptr = malloc(sizeof(struct fcc_set));
2074     if (setptr == NULL) {
2075         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2076         k5_cc_mutex_unlock(context, &data->lock);
2077         k5_cc_mutex_destroy(&data->lock);
2078         free(data->filename);
2079         free(data);
2080         (void) close(ret);
2081         (void) unlink(scratch);
2082         return KRB5_CC_NOMEM;
2083     }
2084     setptr->refcount = 1;
2085     setptr->data = data;
2086     setptr->next = fccs;
2087     fccs = setptr;
2088     k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2089
2090     k5_cc_mutex_assert_locked(context, &data->lock);
2091     k5_cc_mutex_unlock(context, &data->lock);
2092     lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
2093     if (lid == NULL) {
2094         dereference(context, data);
2095         return KRB5_CC_NOMEM;
2096     }
2097
2098     lid->ops = &krb5_fcc_ops;
2099     lid->data = data;
2100     lid->magic = KV5M_CCACHE;
2101
2102     /* default to open/close on every trn - otherwise destroy
2103        will get as to state confused */
2104     ((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE;
2105
2106     *id = lid;
2107
2108
2109     krb5_change_cache ();
2110     return KRB5_OK;
2111
2112 err_out:
2113     k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2114     k5_cc_mutex_unlock(context, &data->lock);
2115     k5_cc_mutex_destroy(&data->lock);
2116     free(data->filename);
2117     free(data);
2118     return kret;
2119 }
2120
2121 /*
2122  * Requires:
2123  * id is a file credential cache
2124  *
2125  * Returns:
2126  * The name of the file cred cache id.
2127  */
2128 static const char * KRB5_CALLCONV
2129 krb5_fcc_get_name (krb5_context context, krb5_ccache id)
2130 {
2131     return (char *) ((krb5_fcc_data *) id->data)->filename;
2132 }
2133
2134 /*
2135  * Modifies:
2136  * id, princ
2137  *
2138  * Effects:
2139  * Retrieves the primary principal from id, as set with
2140  * krb5_fcc_initialize.  The principal is returned is allocated
2141  * storage that must be freed by the caller via krb5_free_principal.
2142  *
2143  * Errors:
2144  * system errors
2145  * KRB5_CC_NOMEM
2146  */
2147 static krb5_error_code KRB5_CALLCONV
2148 krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
2149 {
2150     krb5_error_code kret = KRB5_OK;
2151
2152     kret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2153     if (kret)
2154         return kret;
2155
2156     MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2157
2158     /* make sure we're beyond the header */
2159     kret = krb5_fcc_skip_header(context, id);
2160     if (kret) goto done;
2161     kret = krb5_fcc_read_principal(context, id, princ);
2162
2163 done:
2164     MAYBE_CLOSE(context, id, kret);
2165     k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2166     return kret;
2167 }
2168
2169
2170 static krb5_error_code KRB5_CALLCONV
2171 krb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
2172 {
2173     return krb5_cc_retrieve_cred_default (context, id, whichfields,
2174                                           mcreds, creds);
2175 }
2176
2177
2178 /*
2179  * Modifies:
2180  * the file cache
2181  *
2182  * Effects:
2183  * stores creds in the file cred cache
2184  *
2185  * Errors:
2186  * system errors
2187  * storage failure errors
2188  */
2189 static krb5_error_code KRB5_CALLCONV
2190 krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
2191 {
2192 #define TCHECK(ret) if (ret != KRB5_OK) goto lose;
2193     krb5_error_code ret;
2194
2195     ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2196     if (ret)
2197         return ret;
2198
2199     /* Make sure we are writing to the end of the file */
2200     MAYBE_OPEN(context, id, FCC_OPEN_RDWR);
2201
2202     /* Make sure we are writing to the end of the file */
2203     ret = fcc_lseek((krb5_fcc_data *) id->data, (off_t) 0, SEEK_END);
2204     if (ret < 0) {
2205         MAYBE_CLOSE_IGNORE(context, id);
2206         k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2207         return krb5_fcc_interpret(context, errno);
2208     }
2209
2210     ret = krb5_fcc_store_principal(context, id, creds->client);
2211     TCHECK(ret);
2212     ret = krb5_fcc_store_principal(context, id, creds->server);
2213     TCHECK(ret);
2214     ret = krb5_fcc_store_keyblock(context, id, &creds->keyblock);
2215     TCHECK(ret);
2216     ret = krb5_fcc_store_times(context, id, &creds->times);
2217     TCHECK(ret);
2218     ret = krb5_fcc_store_octet(context, id, (krb5_int32) creds->is_skey);
2219     TCHECK(ret);
2220     ret = krb5_fcc_store_int32(context, id, creds->ticket_flags);
2221     TCHECK(ret);
2222     ret = krb5_fcc_store_addrs(context, id, creds->addresses);
2223     TCHECK(ret);
2224     ret = krb5_fcc_store_authdata(context, id, creds->authdata);
2225     TCHECK(ret);
2226     ret = krb5_fcc_store_data(context, id, &creds->ticket);
2227     TCHECK(ret);
2228     ret = krb5_fcc_store_data(context, id, &creds->second_ticket);
2229     TCHECK(ret);
2230
2231 lose:
2232     MAYBE_CLOSE(context, id, ret);
2233     k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2234     krb5_change_cache ();
2235     return ret;
2236 #undef TCHECK
2237 }
2238
2239 /*
2240  * Non-functional stub implementation for krb5_fcc_remove
2241  *
2242  * Errors:
2243  *    KRB5_CC_NOSUPP - not implemented
2244  */
2245 static krb5_error_code KRB5_CALLCONV
2246 krb5_fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
2247                      krb5_creds *creds)
2248 {
2249     return KRB5_CC_NOSUPP;
2250 }
2251
2252 /*
2253  * Requires:
2254  * id is a cred cache returned by krb5_fcc_resolve or
2255  * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2256  *
2257  * Modifies:
2258  * id
2259  *
2260  * Effects:
2261  * Sets the operational flags of id to flags.
2262  */
2263 static krb5_error_code KRB5_CALLCONV
2264 krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
2265 {
2266     krb5_error_code ret = KRB5_OK;
2267
2268     ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2269     if (ret)
2270         return ret;
2271
2272     /* XXX This should check for illegal combinations, if any.. */
2273     if (flags & KRB5_TC_OPENCLOSE) {
2274         /* asking to turn on OPENCLOSE mode */
2275         if (!OPENCLOSE(id)
2276             /* XXX Is this test necessary? */
2277             && ((krb5_fcc_data *) id->data)->file != NO_FILE)
2278             (void) krb5_fcc_close_file (context, ((krb5_fcc_data *) id->data));
2279     } else {
2280         /* asking to turn off OPENCLOSE mode, meaning it must be
2281            left open.  We open if it's not yet open */
2282         MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2283     }
2284
2285     ((krb5_fcc_data *) id->data)->flags = flags;
2286     k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2287     return ret;
2288 }
2289
2290 /*
2291  * Requires:
2292  * id is a cred cache returned by krb5_fcc_resolve or
2293  * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2294  *
2295  * Modifies:
2296  * id (mutex only; temporary)
2297  *
2298  * Effects:
2299  * Returns the operational flags of id.
2300  */
2301 static krb5_error_code KRB5_CALLCONV
2302 krb5_fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
2303 {
2304     krb5_error_code ret = KRB5_OK;
2305
2306     ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2307     if (ret)
2308         return ret;
2309     *flags = ((krb5_fcc_data *) id->data)->flags;
2310     k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2311     return ret;
2312 }
2313
2314 static krb5_error_code KRB5_CALLCONV
2315 krb5_fcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor)
2316 {
2317     krb5_error_code ret = 0;
2318     krb5_cc_ptcursor n = NULL;
2319     struct krb5_fcc_ptcursor_data *cdata = NULL;
2320
2321     *cursor = NULL;
2322
2323     n = malloc(sizeof(*n));
2324     if (n == NULL)
2325         return ENOMEM;
2326     n->ops = &krb5_fcc_ops;
2327     cdata = malloc(sizeof(struct krb5_fcc_ptcursor_data));
2328     if (cdata == NULL) {
2329         ret = ENOMEM;
2330         goto errout;
2331     }
2332     n->data = cdata;
2333     ret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2334     if (ret)
2335         goto errout;
2336     cdata->cur = fccs;
2337     ret = k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2338     if (ret)
2339         goto errout;
2340
2341 errout:
2342     if (ret) {
2343         krb5_fcc_ptcursor_free(context, &n);
2344     }
2345     *cursor = n;
2346     return ret;
2347 }
2348
2349 static krb5_error_code KRB5_CALLCONV
2350 krb5_fcc_ptcursor_next(krb5_context context,
2351                        krb5_cc_ptcursor cursor,
2352                        krb5_ccache *ccache)
2353 {
2354     krb5_error_code ret = 0;
2355     struct krb5_fcc_ptcursor_data *cdata = NULL;
2356     krb5_ccache n;
2357
2358     *ccache = NULL;
2359     n = malloc(sizeof(*n));
2360     if (n == NULL)
2361         return ENOMEM;
2362
2363     cdata = cursor->data;
2364
2365     ret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2366     if (ret)
2367         goto errout;
2368
2369     if (cdata->cur == NULL) {
2370         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2371         free(n);
2372         n = NULL;
2373         goto errout;
2374     }
2375
2376     n->ops = &krb5_fcc_ops;
2377     n->data = cdata->cur->data;
2378     cdata->cur->refcount++;
2379
2380     cdata->cur = cdata->cur->next;
2381
2382     ret = k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2383     if (ret)
2384         goto errout;
2385 errout:
2386     if (ret && n != NULL) {
2387         free(n);
2388         n = NULL;
2389     }
2390     *ccache = n;
2391     return ret;
2392 }
2393
2394 static krb5_error_code KRB5_CALLCONV
2395 krb5_fcc_ptcursor_free(krb5_context context,
2396                        krb5_cc_ptcursor *cursor)
2397 {
2398     if (*cursor == NULL)
2399         return 0;
2400     if ((*cursor)->data != NULL)
2401         free((*cursor)->data);
2402     free(*cursor);
2403     *cursor = NULL;
2404     return 0;
2405 }
2406
2407 /*
2408  * Modifies:
2409  * change_time
2410  *
2411  * Effects:
2412  * Returns the timestamp of id's file modification date.
2413  * If an error occurs, change_time is set to 0.
2414  */
2415 static krb5_error_code KRB5_CALLCONV
2416 krb5_fcc_last_change_time(krb5_context context, krb5_ccache id,
2417                           krb5_timestamp *change_time)
2418 {
2419     krb5_error_code kret = KRB5_OK;
2420     krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2421
2422     kret = krb5_fcc_data_last_change_time(context, data, change_time);
2423
2424     return kret;
2425 }
2426
2427 static krb5_error_code KRB5_CALLCONV
2428 krb5_fcc_lock(krb5_context context, krb5_ccache id)
2429 {
2430     krb5_error_code ret = 0;
2431     krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2432     ret = k5_cc_mutex_lock(context, &data->lock);
2433     return ret;
2434 }
2435
2436 static krb5_error_code KRB5_CALLCONV
2437 krb5_fcc_unlock(krb5_context context, krb5_ccache id)
2438 {
2439     krb5_error_code ret = 0;
2440     krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2441     ret = k5_cc_mutex_unlock(context, &data->lock);
2442     return ret;
2443 }
2444
2445 static krb5_error_code
2446 krb5_fcc_data_last_change_time(krb5_context context, krb5_fcc_data *data,
2447                                krb5_timestamp *change_time)
2448 {
2449     krb5_error_code kret = KRB5_OK;
2450     register int ret;
2451
2452     struct stat buf;
2453
2454     *change_time = 0;
2455
2456     kret = k5_cc_mutex_lock(context, &data->lock);
2457     if (kret) {
2458         return kret;
2459     }
2460
2461     ret = stat(data->filename, &buf);
2462     if (ret == -1) {
2463         kret = krb5_fcc_interpret(context, errno);
2464     } else {
2465         *change_time = (krb5_timestamp) buf.st_mtime;
2466     }
2467
2468     k5_cc_mutex_unlock(context, &data->lock);
2469
2470     return kret;
2471 }
2472
2473 static krb5_error_code
2474 krb5_fcc_interpret(krb5_context context, int errnum)
2475 {
2476     register krb5_error_code retval;
2477     switch (errnum) {
2478     case ENOENT:
2479         retval = KRB5_FCC_NOFILE;
2480         break;
2481     case EPERM:
2482     case EACCES:
2483 #ifdef EISDIR
2484     case EISDIR:                        /* Mac doesn't have EISDIR */
2485 #endif
2486     case ENOTDIR:
2487 #ifdef ELOOP
2488     case ELOOP:                         /* Bad symlink is like no file. */
2489 #endif
2490 #ifdef ETXTBSY
2491     case ETXTBSY:
2492 #endif
2493     case EBUSY:
2494     case EROFS:
2495         retval = KRB5_FCC_PERM;
2496         break;
2497     case EINVAL:
2498     case EEXIST:                        /* XXX */
2499     case EFAULT:
2500     case EBADF:
2501 #ifdef ENAMETOOLONG
2502     case ENAMETOOLONG:
2503 #endif
2504 #ifdef EWOULDBLOCK
2505     case EWOULDBLOCK:
2506 #endif
2507         retval = KRB5_FCC_INTERNAL;
2508         break;
2509 #ifdef EDQUOT
2510     case EDQUOT:
2511 #endif
2512     case ENOSPC:
2513     case EIO:
2514     case ENFILE:
2515     case EMFILE:
2516     case ENXIO:
2517     default:
2518         retval = KRB5_CC_IO;            /* XXX */
2519         krb5_set_error_message(context, retval,
2520                                "Credentials cache I/O operation failed (%s)",
2521                                strerror(errnum));
2522     }
2523     return retval;
2524 }
2525
2526 const krb5_cc_ops krb5_fcc_ops = {
2527     0,
2528     "FILE",
2529     krb5_fcc_get_name,
2530     krb5_fcc_resolve,
2531     krb5_fcc_generate_new,
2532     krb5_fcc_initialize,
2533     krb5_fcc_destroy,
2534     krb5_fcc_close,
2535     krb5_fcc_store,
2536     krb5_fcc_retrieve,
2537     krb5_fcc_get_principal,
2538     krb5_fcc_start_seq_get,
2539     krb5_fcc_next_cred,
2540     krb5_fcc_end_seq_get,
2541     krb5_fcc_remove_cred,
2542     krb5_fcc_set_flags,
2543     krb5_fcc_get_flags,
2544     krb5_fcc_ptcursor_new,
2545     krb5_fcc_ptcursor_next,
2546     krb5_fcc_ptcursor_free,
2547     NULL, /* move */
2548     krb5_fcc_last_change_time,
2549     NULL, /* wasdefault */
2550     krb5_fcc_lock,
2551     krb5_fcc_unlock,
2552 };
2553
2554 #if defined(_WIN32)
2555 /*
2556  * krb5_change_cache should be called after the cache changes.
2557  * A notification message is is posted out to all top level
2558  * windows so that they may recheck the cache based on the
2559  * changes made.  We register a unique message type with which
2560  * we'll communicate to all other processes.
2561  */
2562
2563 krb5_error_code
2564 krb5_change_cache (void) {
2565
2566     PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0);
2567
2568     return 0;
2569 }
2570
2571 unsigned int KRB5_CALLCONV
2572 krb5_get_notification_message (void) {
2573     static unsigned int message = 0;
2574
2575     if (message == 0)
2576         message = RegisterWindowMessage(WM_KERBEROS5_CHANGED);
2577
2578     return message;
2579 }
2580 #else /* _WIN32 */
2581
2582 krb5_error_code
2583 krb5_change_cache (void)
2584 {
2585     return 0;
2586 }
2587 unsigned int
2588 krb5_get_notification_message (void)
2589 {
2590     return 0;
2591 }
2592
2593 #endif /* _WIN32 */
2594
2595 const krb5_cc_ops krb5_cc_file_ops = {
2596     0,
2597     "FILE",
2598     krb5_fcc_get_name,
2599     krb5_fcc_resolve,
2600     krb5_fcc_generate_new,
2601     krb5_fcc_initialize,
2602     krb5_fcc_destroy,
2603     krb5_fcc_close,
2604     krb5_fcc_store,
2605     krb5_fcc_retrieve,
2606     krb5_fcc_get_principal,
2607     krb5_fcc_start_seq_get,
2608     krb5_fcc_next_cred,
2609     krb5_fcc_end_seq_get,
2610     krb5_fcc_remove_cred,
2611     krb5_fcc_set_flags,
2612     krb5_fcc_get_flags,
2613     krb5_fcc_ptcursor_new,
2614     krb5_fcc_ptcursor_next,
2615     krb5_fcc_ptcursor_free,
2616     NULL, /* move */
2617     krb5_fcc_last_change_time,
2618     NULL, /* wasdefault */
2619     krb5_fcc_lock,
2620     krb5_fcc_unlock,
2621 };