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