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