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