1 /*
2  * Copyright 2008 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, 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, 0600);
1403      }
1404     if (f == NO_FILE)
1405 	return krb5_fcc_interpret (context, errno);
1406 
1407     data->mode = mode;
1408 
1409     if (data->mode == FCC_OPEN_RDONLY)
1410 	lock_flag = KRB5_LOCKMODE_SHARED;
1411     else
1412 	lock_flag = KRB5_LOCKMODE_EXCLUSIVE;
1413     if ((retval = krb5_lock_file(context, f, lock_flag))) {
1414 	(void) close(f);
1415         if (retval == EAGAIN && retries++ < LOCK_RETRIES) {
1416 	    /* Solaris Kerberos wait some time before retrying */
1417 	    if (poll(NULL, 0, WAIT_LENGTH) == 0)
1418 	        goto fcc_retry;
1419 	}
1420 	syslog(LOG_ERR, "Failed to lock %s [%m]", data->filename);
1421 	return retval;
1422     }
1423 
1424     if (mode == FCC_OPEN_AND_ERASE || mode == FCC_OPEN_AND_ERASE_NOUNLINK) {
1425         int cnt;
1426 
1427 	/*
1428 	 * Solaris Kerberos
1429 	 * If this file was not created, we have to flush existing data.
1430 	 * This will happen only if we are doing an ERASE_NOUNLINK open.
1431 	 */
1432 	if (newfile == 0 && (ftruncate(f, 0) == -1)) {
1433 	    syslog(LOG_ERR, "ftruncate failed for %s [%m]", data->filename);
1434 	    close(f);
1435 	    return (krb5_fcc_interpret(context, errno));
1436 	}
1437 
1438 	/* write the version number */
1439 	fcc_fvno = htons(context->fcc_default_format);
1440 	data->version = context->fcc_default_format;
1441 	if ((cnt = write(f, (char *)&fcc_fvno, sizeof(fcc_fvno))) !=
1442 	    sizeof(fcc_fvno)) {
1443 	    retval = ((cnt == -1) ? krb5_fcc_interpret(context, errno) :
1444 		    KRB5_CC_IO);
1445              goto done;
1446          }
1447          data->file = f;
1448 
1449 	 if (data->version == KRB5_FCC_FVNO_4) {
1450              /* V4 of the credentials cache format allows for header tags */
1451 	     fcc_flen = 0;
1452 
1453 	     if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
1454 		 fcc_flen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32));
1455 
1456 	     /* Write header length */
1457 	     retval = krb5_fcc_store_ui_2(context, id, (krb5_int32)fcc_flen);
1458 	     if (retval) goto done;
1459 
1460 	     if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
1461 		 /* Write time offset tag */
1462 		 fcc_tag = FCC_TAG_DELTATIME;
1463 		 fcc_taglen = 2*sizeof(krb5_int32);
1464 
1465 		 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_tag);
1466 		 if (retval) goto done;
1467 		 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_taglen);
1468 		 if (retval) goto done;
1469 		 retval = krb5_fcc_store_int32(context,id,os_ctx->time_offset);
1470 		 if (retval) goto done;
1471 		 retval = krb5_fcc_store_int32(context,id,os_ctx->usec_offset);
1472 		 if (retval) goto done;
1473 	     }
1474 	 }
1475 	 invalidate_cache(data);
1476 	 goto done;
1477      }
1478 
1479      /* verify a valid version number is there */
1480     invalidate_cache(data);
1481      if (read(f, (char *)&fcc_fvno, sizeof(fcc_fvno)) != sizeof(fcc_fvno)) {
1482 	 retval = KRB5_CC_FORMAT;
1483 	 goto done;
1484      }
1485      data->version = ntohs(fcc_fvno);
1486     if ((data->version != KRB5_FCC_FVNO_4) &&
1487 	(data->version != KRB5_FCC_FVNO_3) &&
1488 	(data->version != KRB5_FCC_FVNO_2) &&
1489 	(data->version != KRB5_FCC_FVNO_1)) {
1490 	retval = KRB5_CCACHE_BADVNO;
1491 	goto done;
1492     }
1493 
1494     data->file = f;
1495 
1496      if (data->version == KRB5_FCC_FVNO_4) {
1497 	 char buf[1024];
1498 
1499 	 if (krb5_fcc_read_ui_2(context, id, &fcc_flen) ||
1500 	     (fcc_flen > sizeof(buf)))
1501 	 {
1502 	     retval = KRB5_CC_FORMAT;
1503 	     goto done;
1504 	 }
1505 
1506 	 while (fcc_flen) {
1507 	     if ((fcc_flen < (2 * sizeof(krb5_ui_2))) ||
1508 		 krb5_fcc_read_ui_2(context, id, &fcc_tag) ||
1509 		 krb5_fcc_read_ui_2(context, id, &fcc_taglen) ||
1510 		 (fcc_taglen > (fcc_flen - 2*sizeof(krb5_ui_2))))
1511 	     {
1512 		 retval = KRB5_CC_FORMAT;
1513 		 goto done;
1514 	     }
1515 
1516 	     switch (fcc_tag) {
1517 	     case FCC_TAG_DELTATIME:
1518 		 if (fcc_taglen != 2*sizeof(krb5_int32)) {
1519 		     retval = KRB5_CC_FORMAT;
1520 		     goto done;
1521 		 }
1522 		 if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
1523 		     (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID))
1524 		 {
1525 		     if (krb5_fcc_read(context, id, buf, fcc_taglen)) {
1526 			 retval = KRB5_CC_FORMAT;
1527 			 goto done;
1528 		     }
1529 		     break;
1530 		 }
1531 		 if (krb5_fcc_read_int32(context, id, &os_ctx->time_offset) ||
1532 		     krb5_fcc_read_int32(context, id, &os_ctx->usec_offset))
1533 		 {
1534 		     retval = KRB5_CC_FORMAT;
1535 		     goto done;
1536 		 }
1537 		 os_ctx->os_flags =
1538 		     ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
1539 		      KRB5_OS_TOFFSET_VALID);
1540 		 break;
1541 	     default:
1542 		 if (fcc_taglen && krb5_fcc_read(context,id,buf,fcc_taglen)) {
1543 		     retval = KRB5_CC_FORMAT;
1544 		     goto done;
1545 		 }
1546 		 break;
1547 	     }
1548 	     fcc_flen -= (2*sizeof(krb5_ui_2) + fcc_taglen);
1549 	 }
1550      }
1551 
1552 done:
1553      if (retval) {
1554          data->file = -1;
1555          (void) krb5_unlock_file(context, f);
1556          (void) close(f);
1557      }
1558      return retval;
1559 }
1560 
1561 static krb5_error_code
1562 krb5_fcc_skip_header(krb5_context context, krb5_ccache id)
1563 {
1564      krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1565      krb5_error_code kret;
1566      krb5_ui_2 fcc_flen;
1567 
1568      k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
1569 
1570      fcc_lseek(data, (off_t) sizeof(krb5_ui_2), SEEK_SET);
1571      if (data->version == KRB5_FCC_FVNO_4) {
1572 	 kret = krb5_fcc_read_ui_2(context, id, &fcc_flen);
1573 	 if (kret) return kret;
1574          if(fcc_lseek(data, (off_t) fcc_flen, SEEK_CUR) < 0)
1575 		return errno;
1576      }
1577      return KRB5_OK;
1578 }
1579 
1580 static krb5_error_code
1581 krb5_fcc_skip_principal(krb5_context context, krb5_ccache id)
1582 {
1583      krb5_error_code kret;
1584      krb5_principal princ;
1585 
1586      k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
1587 
1588      kret = krb5_fcc_read_principal(context, id, &princ);
1589      if (kret != KRB5_OK)
1590 	  return kret;
1591 
1592      krb5_free_principal(context, princ);
1593      return KRB5_OK;
1594 }
1595 
1596 
1597 /*
1598  * Modifies:
1599  * id
1600  *
1601  * Effects:
1602  * Creates/refreshes the file cred cache id.  If the cache exists, its
1603  * contents are destroyed.
1604  *
1605  * Errors:
1606  * system errors
1607  * permission errors
1608  */
1609 static krb5_error_code KRB5_CALLCONV
1610 krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
1611 {
1612      krb5_error_code kret = 0;
1613      int reti = 0;
1614 
1615      kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
1616      if (kret)
1617 	 return kret;
1618 
1619      MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE_NOUNLINK); /* Solaris Kerberos */
1620 
1621      /*
1622       * SUN14resync
1623       * This is not needed and can cause problems with ktkt_warnd(1M)
1624       * because it does tricks with getuid and if we enable this fchmod
1625       * we get EPERM [file_owner] failures on fchmod.
1626       */
1627 #if 0
1628 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
1629      {
1630 #ifdef HAVE_FCHMOD
1631          reti = fchmod(((krb5_fcc_data *) id->data)->file, S_IREAD | S_IWRITE);
1632 #else
1633          reti = chmod(((krb5_fcc_data *) id->data)->filename, S_IREAD | S_IWRITE);
1634 #endif
1635 #endif
1636          if (reti == -1) {
1637              kret = krb5_fcc_interpret(context, errno);
1638              MAYBE_CLOSE(context, id, kret);
1639 	     k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
1640              return kret;
1641          }
1642      }
1643 #endif
1644      kret = krb5_fcc_store_principal(context, id, princ);
1645 
1646      MAYBE_CLOSE(context, id, kret);
1647      k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
1648      krb5_change_cache ();
1649      return kret;
1650 }
1651 
1652 /*
1653  * Drop the ref count; if it hits zero, remove the entry from the
1654  * fcc_set list and free it.
1655  */
1656 static krb5_error_code dereference(krb5_context context, krb5_fcc_data *data)
1657 {
1658     krb5_error_code kerr;
1659     struct fcc_set **fccsp;
1660 
1661     kerr = k5_mutex_lock(&krb5int_cc_file_mutex);
1662     if (kerr)
1663 	return kerr;
1664     for (fccsp = &fccs; *fccsp != NULL; fccsp = &(*fccsp)->next)
1665 	if ((*fccsp)->data == data)
1666 	    break;
1667     assert(*fccsp != NULL);
1668     assert((*fccsp)->data == data);
1669     (*fccsp)->refcount--;
1670     if ((*fccsp)->refcount == 0) {
1671         struct fcc_set *temp;
1672 	data = (*fccsp)->data;
1673 	temp = *fccsp;
1674 	*fccsp = (*fccsp)->next;
1675 	free(temp);
1676 	k5_mutex_unlock(&krb5int_cc_file_mutex);
1677 	k5_mutex_assert_unlocked(&data->lock);
1678 	free(data->filename);
1679 	zap(data->buf, sizeof(data->buf));
1680 	if (data->file >= 0) {
1681 	    k5_mutex_lock(&data->lock);
1682 	    krb5_fcc_close_file(context, data);
1683 	    k5_mutex_unlock(&data->lock);
1684 	}
1685 	k5_mutex_destroy(&data->lock);
1686 	free(data);
1687     } else
1688 	k5_mutex_unlock(&krb5int_cc_file_mutex);
1689     return 0;
1690 }
1691 
1692 /*
1693  * Modifies:
1694  * id
1695  *
1696  * Effects:
1697  * Closes the file cache, invalidates the id, and frees any resources
1698  * associated with the cache.
1699  */
1700 static krb5_error_code KRB5_CALLCONV
1701 krb5_fcc_close(krb5_context context, krb5_ccache id)
1702 {
1703      dereference(context, (krb5_fcc_data *) id->data);
1704      krb5_xfree(id);
1705      return KRB5_OK;
1706 }
1707 
1708 /*
1709  * Effects:
1710  * Destroys the contents of id.
1711  *
1712  * Errors:
1713  * system errors
1714  */
1715 static krb5_error_code KRB5_CALLCONV
1716 krb5_fcc_destroy(krb5_context context, krb5_ccache id)
1717 {
1718      krb5_error_code kret = 0;
1719      krb5_fcc_data *data = (krb5_fcc_data *) id->data;
1720      register int ret;
1721 
1722      struct stat buf;
1723      unsigned long i, size;
1724      unsigned int wlen;
1725      char zeros[BUFSIZ];
1726 
1727      kret = k5_mutex_lock(&data->lock);
1728      if (kret)
1729 	 return kret;
1730 
1731      if (OPENCLOSE(id)) {
1732 	 invalidate_cache(data);
1733 	  ret = THREEPARAMOPEN(data->filename,
1734 			       O_RDWR | O_BINARY, 0);
1735 	  if (ret < 0) {
1736 	      kret = krb5_fcc_interpret(context, errno);
1737 	      goto cleanup;
1738 	  }
1739 	  data->file = ret;
1740      }
1741      else
1742 	  fcc_lseek(data, (off_t) 0, SEEK_SET);
1743 
1744 #ifdef MSDOS_FILESYSTEM
1745 /* "disgusting bit of UNIX trivia" - that's how the writers of NFS describe
1746 ** the ability of UNIX to still write to a file which has been unlinked.
1747 ** Naturally, the PC can't do this. As a result, we have to delete the file
1748 ** after we wipe it clean but that throws off all the error handling code.
1749 ** So we have do the work ourselves.
1750 */
1751     ret = fstat(data->file, &buf);
1752     if (ret == -1) {
1753         kret = krb5_fcc_interpret(context, errno);
1754         size = 0;                               /* Nothing to wipe clean */
1755     } else
1756         size = (unsigned long) buf.st_size;
1757 
1758     memset(zeros, 0, BUFSIZ);
1759     while (size > 0) {
1760         wlen = (int) ((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */
1761         i = write(data->file, zeros, wlen);
1762         if (i < 0) {
1763             kret = krb5_fcc_interpret(context, errno);
1764             /* Don't jump to cleanup--we still want to delete the file. */
1765             break;
1766         }
1767         size -= i;                              /* We've read this much */
1768     }
1769 
1770     if (OPENCLOSE(id)) {
1771         (void) close(((krb5_fcc_data *)id->data)->file);
1772         data->file = -1;
1773     }
1774 
1775     ret = unlink(data->filename);
1776     if (ret < 0) {
1777         kret = krb5_fcc_interpret(context, errno);
1778         goto cleanup;
1779     }
1780 
1781 #else /* MSDOS_FILESYSTEM */
1782 
1783      ret = unlink(data->filename);
1784      if (ret < 0) {
1785 	 kret = krb5_fcc_interpret(context, errno);
1786 	 if (OPENCLOSE(id)) {
1787 	     (void) close(((krb5_fcc_data *)id->data)->file);
1788 	     data->file = -1;
1789              kret = ret;
1790 	 }
1791 	 goto cleanup;
1792      }
1793 
1794      ret = fstat(data->file, &buf);
1795      if (ret < 0) {
1796 	 kret = krb5_fcc_interpret(context, errno);
1797 	 if (OPENCLOSE(id)) {
1798 	     (void) close(((krb5_fcc_data *)id->data)->file);
1799 	     data->file = -1;
1800 	 }
1801 	 goto cleanup;
1802      }
1803 
1804      /* XXX This may not be legal XXX */
1805      size = (unsigned long) buf.st_size;
1806      memset(zeros, 0, BUFSIZ);
1807      for (i=0; i < size / BUFSIZ; i++)
1808 	  if (write(data->file, zeros, BUFSIZ) < 0) {
1809 	      kret = krb5_fcc_interpret(context, errno);
1810 	      if (OPENCLOSE(id)) {
1811 		  (void) close(((krb5_fcc_data *)id->data)->file);
1812 		  data->file = -1;
1813 	      }
1814 	      goto cleanup;
1815 	  }
1816 
1817      wlen = (unsigned int) (size % BUFSIZ);
1818      if (write(data->file, zeros, wlen) < 0) {
1819 	 kret = krb5_fcc_interpret(context, errno);
1820 	 if (OPENCLOSE(id)) {
1821 	     (void) close(((krb5_fcc_data *)id->data)->file);
1822 	     data->file = -1;
1823 	 }
1824 	 goto cleanup;
1825      }
1826 
1827      ret = close(data->file);
1828      data->file = -1;
1829 
1830      if (ret)
1831 	 kret = krb5_fcc_interpret(context, errno);
1832 
1833 #endif /* MSDOS_FILESYSTEM */
1834 
1835   cleanup:
1836      k5_mutex_unlock(&data->lock);
1837      dereference(context, data);
1838      krb5_xfree(id);
1839 
1840      krb5_change_cache ();
1841      return kret;
1842 }
1843 
1844 extern const krb5_cc_ops krb5_fcc_ops;
1845 
1846 /*
1847  * Requires:
1848  * residual is a legal path name, and a null-terminated string
1849  *
1850  * Modifies:
1851  * id
1852  *
1853  * Effects:
1854  * creates a file-based cred cache that will reside in the file
1855  * residual.  The cache is not opened, but the filename is reserved.
1856  *
1857  * Returns:
1858  * A filled in krb5_ccache structure "id".
1859  *
1860  * Errors:
1861  * KRB5_CC_NOMEM - there was insufficient memory to allocate the
1862  * 		krb5_ccache.  id is undefined.
1863  * permission errors
1864  */
1865 static krb5_error_code KRB5_CALLCONV
1866 krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
1867 {
1868      krb5_ccache lid;
1869      krb5_error_code kret;
1870      krb5_fcc_data *data;
1871      struct fcc_set *setptr;
1872 
1873      kret = k5_mutex_lock(&krb5int_cc_file_mutex);
1874      if (kret)
1875 	 return kret;
1876      for (setptr = fccs; setptr; setptr = setptr->next) {
1877 	 if (!strcmp(setptr->data->filename, residual))
1878 	     break;
1879      }
1880      if (setptr) {
1881 	 data = setptr->data;
1882 	 assert(setptr->refcount != 0);
1883 	 setptr->refcount++;
1884 	 assert(setptr->refcount != 0);
1885 	 kret = k5_mutex_lock(&data->lock);
1886 	 if (kret) {
1887 	     k5_mutex_unlock(&krb5int_cc_file_mutex);
1888 	     return kret;
1889 	 }
1890 	 k5_mutex_unlock(&krb5int_cc_file_mutex);
1891      } else {
1892 	 data = malloc(sizeof(krb5_fcc_data));
1893 	 if (data == NULL) {
1894 	     k5_mutex_unlock(&krb5int_cc_file_mutex);
1895 	     return KRB5_CC_NOMEM;
1896 	 }
1897 	 data->filename = strdup(residual);
1898 	 if (data->filename == NULL) {
1899 	     k5_mutex_unlock(&krb5int_cc_file_mutex);
1900 	     free(data);
1901 	     return KRB5_CC_NOMEM;
1902 	 }
1903 	 kret = k5_mutex_init(&data->lock);
1904 	 if (kret) {
1905 	     k5_mutex_unlock(&krb5int_cc_file_mutex);
1906 	     free(data->filename);
1907 	     free(data);
1908 	     return kret;
1909 	 }
1910 	 kret = k5_mutex_lock(&data->lock);
1911 	 if (kret) {
1912 	     k5_mutex_unlock(&krb5int_cc_file_mutex);
1913 	     k5_mutex_destroy(&data->lock);
1914 	     free(data->filename);
1915 	     free(data);
1916 	     return kret;
1917 	 }
1918 	 /* data->version,mode filled in for real later */
1919 	 data->version = data->mode = 0;
1920 	 data->flags = KRB5_TC_OPENCLOSE;
1921 	 data->file = -1;
1922 	 data->valid_bytes = 0;
1923 	 setptr = malloc(sizeof(struct fcc_set));
1924 	 if (setptr == NULL) {
1925 	     k5_mutex_unlock(&krb5int_cc_file_mutex);
1926 	     k5_mutex_destroy(&data->lock);
1927 	     free(data->filename);
1928 	     free(data);
1929 	     return KRB5_CC_NOMEM;
1930 	 }
1931 	 setptr->refcount = 1;
1932 	 setptr->data = data;
1933 	 setptr->next = fccs;
1934 	 fccs = setptr;
1935 	 k5_mutex_unlock(&krb5int_cc_file_mutex);
1936      }
1937 
1938      k5_mutex_assert_locked(&data->lock);
1939      k5_mutex_unlock(&data->lock);
1940      lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
1941      if (lid == NULL) {
1942 	 dereference(context, data);
1943 	 return KRB5_CC_NOMEM;
1944      }
1945 
1946      lid->ops = &krb5_fcc_ops;
1947      lid->data = data;
1948      lid->magic = KV5M_CCACHE;
1949 
1950      /* other routines will get errors on open, and callers must expect them,
1951 	if cache is non-existent/unusable */
1952      *id = lid;
1953      return KRB5_OK;
1954 }
1955 
1956 /*
1957  * Effects:
1958  * Prepares for a sequential search of the credentials cache.
1959  * Returns and krb5_cc_cursor to be used with krb5_fcc_next_cred and
1960  * krb5_fcc_end_seq_get.
1961  *
1962  * If the cache is modified between the time of this call and the time
1963  * of the final krb5_fcc_end_seq_get, the results are undefined.
1964  *
1965  * Errors:
1966  * KRB5_CC_NOMEM
1967  * system errors
1968  */
1969 static krb5_error_code KRB5_CALLCONV
1970 krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id,
1971 		       krb5_cc_cursor *cursor)
1972 {
1973      krb5_fcc_cursor *fcursor;
1974      krb5_error_code kret = KRB5_OK;
1975      krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1976 
1977      kret = k5_mutex_lock(&data->lock);
1978      if (kret)
1979 	 return kret;
1980 
1981      fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor));
1982      if (fcursor == NULL) {
1983 	 k5_mutex_unlock(&data->lock);
1984 	 return KRB5_CC_NOMEM;
1985      }
1986      if (OPENCLOSE(id)) {
1987           kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY);
1988           if (kret) {
1989               krb5_xfree(fcursor);
1990 	      k5_mutex_unlock(&data->lock);
1991               return kret;
1992           }
1993      }
1994 
1995      /* Make sure we start reading right after the primary principal */
1996      kret = krb5_fcc_skip_header(context, id);
1997      if (kret) {
1998 	    /* Solaris Kerberos - fix mem leak */
1999 	    krb5_xfree(fcursor);
2000 	    goto done;
2001      }
2002      kret = krb5_fcc_skip_principal(context, id);
2003      if (kret) {
2004 	    /* Solaris Kerberos - fix mem leak */
2005 	    krb5_xfree(fcursor);
2006 	    goto done;
2007      }
2008 
2009      fcursor->pos = fcc_lseek(data, (off_t) 0, SEEK_CUR);
2010      *cursor = (krb5_cc_cursor) fcursor;
2011 
2012 done:
2013      MAYBE_CLOSE(context, id, kret);
2014      k5_mutex_unlock(&data->lock);
2015      return kret;
2016 }
2017 
2018 
2019 /*
2020  * Requires:
2021  * cursor is a krb5_cc_cursor originally obtained from
2022  * krb5_fcc_start_seq_get.
2023  *
2024  * Modifes:
2025  * cursor, creds
2026  *
2027  * Effects:
2028  * Fills in creds with the "next" credentals structure from the cache
2029  * id.  The actual order the creds are returned in is arbitrary.
2030  * Space is allocated for the variable length fields in the
2031  * credentials structure, so the object returned must be passed to
2032  * krb5_destroy_credential.
2033  *
2034  * The cursor is updated for the next call to krb5_fcc_next_cred.
2035  *
2036  * Errors:
2037  * system errors
2038  */
2039 static krb5_error_code KRB5_CALLCONV
2040 krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
2041 		   krb5_creds *creds)
2042 {
2043 #define TCHECK(ret) if (ret != KRB5_OK) goto lose;
2044      krb5_error_code kret;
2045      krb5_fcc_cursor *fcursor;
2046      krb5_int32 int32;
2047      krb5_octet octet;
2048      krb5_fcc_data *d = (krb5_fcc_data *) id->data;
2049 
2050      kret = k5_mutex_lock(&d->lock);
2051      if (kret)
2052 	 return kret;
2053 
2054      memset((char *)creds, 0, sizeof(*creds));
2055      MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2056      fcursor = (krb5_fcc_cursor *) *cursor;
2057 
2058      kret = (fcc_lseek(d, fcursor->pos, SEEK_SET) == (off_t) -1);
2059      if (kret) {
2060 	 kret = krb5_fcc_interpret(context, errno);
2061 	 MAYBE_CLOSE(context, id, kret);
2062 	 k5_mutex_unlock(&d->lock);
2063 	 return kret;
2064      }
2065 
2066      kret = krb5_fcc_read_principal(context, id, &creds->client);
2067      TCHECK(kret);
2068      kret = krb5_fcc_read_principal(context, id, &creds->server);
2069      TCHECK(kret);
2070      kret = krb5_fcc_read_keyblock(context, id, &creds->keyblock);
2071      TCHECK(kret);
2072      kret = krb5_fcc_read_times(context, id, &creds->times);
2073      TCHECK(kret);
2074      kret = krb5_fcc_read_octet(context, id, &octet);
2075      TCHECK(kret);
2076      creds->is_skey = octet;
2077      kret = krb5_fcc_read_int32(context, id, &int32);
2078      TCHECK(kret);
2079      creds->ticket_flags = int32;
2080      kret = krb5_fcc_read_addrs(context, id, &creds->addresses);
2081      TCHECK(kret);
2082      kret = krb5_fcc_read_authdata(context, id, &creds->authdata);
2083      TCHECK(kret);
2084      kret = krb5_fcc_read_data(context, id, &creds->ticket);
2085      TCHECK(kret);
2086      kret = krb5_fcc_read_data(context, id, &creds->second_ticket);
2087      TCHECK(kret);
2088 
2089      fcursor->pos = fcc_lseek(d, (off_t) 0, SEEK_CUR);
2090      cursor = (krb5_cc_cursor *) fcursor;
2091 
2092 lose:
2093      MAYBE_CLOSE (context, id, kret);
2094      k5_mutex_unlock(&d->lock);
2095      if (kret != KRB5_OK)
2096 	 krb5_free_cred_contents(context, creds);
2097      return kret;
2098 }
2099 
2100 /*
2101  * Requires:
2102  * cursor is a krb5_cc_cursor originally obtained from
2103  * krb5_fcc_start_seq_get.
2104  *
2105  * Modifies:
2106  * id, cursor
2107  *
2108  * Effects:
2109  * Finishes sequential processing of the file credentials ccache id,
2110  * and invalidates the cursor (it must never be used after this call).
2111  */
2112 /* ARGSUSED */
2113 static krb5_error_code KRB5_CALLCONV
2114 krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
2115 {
2116      /* We don't do anything with the file cache itself, so
2117 	no need to lock anything.  */
2118 
2119      /* don't close; it may be left open by the caller,
2120         and if not, fcc_start_seq_get and/or fcc_next_cred will do the
2121         MAYBE_CLOSE.
2122      MAYBE_CLOSE(context, id, kret); */
2123      krb5_xfree((krb5_fcc_cursor *) *cursor);
2124      return 0;
2125 }
2126 
2127 
2128 /*
2129  * Effects:
2130  * Creates a new file cred cache whose name is guaranteed to be
2131  * unique.  The name begins with the string TKT_ROOT (from fcc.h).
2132  * The cache is not opened, but the new filename is reserved.
2133  *
2134  * Returns:
2135  * The filled in krb5_ccache id.
2136  *
2137  * Errors:
2138  * KRB5_CC_NOMEM - there was insufficient memory to allocate the
2139  * 		krb5_ccache.  id is undefined.
2140  * system errors (from open)
2141  */
2142 static krb5_error_code KRB5_CALLCONV
2143 krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
2144 {
2145      krb5_ccache lid;
2146      int ret;
2147      krb5_error_code    kret = 0;
2148      char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for
2149 					    NUL */
2150      krb5_fcc_data *data;
2151      krb5_int16 fcc_fvno = htons(context->fcc_default_format);
2152      krb5_int16 fcc_flen = 0;
2153      int errsave, cnt;
2154      struct fcc_set *setptr;
2155 
2156      /* Set master lock */
2157      kret = k5_mutex_lock(&krb5int_cc_file_mutex);
2158      if (kret)
2159 	 return kret;
2160 
2161      (void) strcpy(scratch, TKT_ROOT);
2162      (void) strcat(scratch, "XXXXXX");
2163 #ifdef HAVE_MKSTEMP
2164      ret = mkstemp(scratch);
2165      if (ret == -1) {
2166          k5_mutex_unlock(&krb5int_cc_file_mutex);
2167 	 return krb5_fcc_interpret(context, errno);
2168      }
2169 #else /*HAVE_MKSTEMP*/
2170      mktemp(scratch);
2171      /* Make sure the file name is reserved */
2172      ret = THREEPARAMOPEN(scratch, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0);
2173      if (ret == -1) {
2174 	  return krb5_fcc_interpret(context, errno);
2175      }
2176 #endif
2177 
2178      /* Allocate memory */
2179      data = (krb5_pointer) malloc(sizeof(krb5_fcc_data));
2180      if (data == NULL) {
2181 	  close(ret);
2182 	  unlink(scratch);
2183 	  k5_mutex_unlock(&krb5int_cc_file_mutex);
2184 	  return KRB5_CC_NOMEM;
2185      }
2186 
2187      data->filename = strdup(scratch);
2188      if (data->filename == NULL) {
2189           k5_mutex_unlock(&krb5int_cc_file_mutex);
2190 	  free(data);
2191 	  close(ret);
2192 	  unlink(scratch);
2193 	  k5_mutex_unlock(&krb5int_cc_file_mutex);
2194 	  return KRB5_CC_NOMEM;
2195      }
2196 
2197      kret = k5_mutex_init(&data->lock);
2198      if (kret) {
2199        k5_mutex_unlock(&krb5int_cc_file_mutex);
2200        free(data->filename);
2201        free(data);
2202        close(ret);
2203        unlink(scratch);
2204        return kret;
2205      }
2206      kret = k5_mutex_lock(&data->lock);
2207      if (kret) {
2208        k5_mutex_unlock(&krb5int_cc_file_mutex);
2209        k5_mutex_destroy(&data->lock);
2210        free(data->filename);
2211        free(data);
2212        close(ret);
2213        unlink(scratch);
2214        return kret;
2215      }
2216 
2217      /*
2218       * The file is initially closed at the end of this call...
2219       */
2220      data->flags = 0;
2221      data->file = -1;
2222      data->valid_bytes = 0;
2223      /* data->version,mode filled in for real later */
2224      data->version = data->mode = 0;
2225 
2226 
2227      /* Ignore user's umask, set mode = 0600 */
2228 #ifndef HAVE_FCHMOD
2229 #ifdef HAVE_CHMOD
2230      chmod(data->filename, S_IRUSR | S_IWUSR);
2231 #endif
2232 #else
2233      fchmod(ret, S_IRUSR | S_IWUSR);
2234 #endif
2235      if ((cnt = write(ret, (char *)&fcc_fvno, sizeof(fcc_fvno)))
2236 	 != sizeof(fcc_fvno)) {
2237 	  errsave = errno;
2238 	  (void) close(ret);
2239 	  (void) unlink(data->filename);
2240 	  kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2241 	  goto err_out;
2242      }
2243      /* For version 4 we save a length for the rest of the header */
2244      if (context->fcc_default_format == KRB5_FCC_FVNO_4) {
2245 	  if ((cnt = write(ret, (char *)&fcc_flen, sizeof(fcc_flen)))
2246 	      != sizeof(fcc_flen)) {
2247 	       errsave = errno;
2248 	       (void) close(ret);
2249 	       (void) unlink(data->filename);
2250 	       kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2251 	       goto err_out;
2252 	  }
2253      }
2254      if (close(ret) == -1) {
2255 	  errsave = errno;
2256 	  (void) unlink(data->filename);
2257 	  kret = krb5_fcc_interpret(context, errsave);
2258 	  goto err_out;
2259      }
2260 
2261 
2262      setptr = malloc(sizeof(struct fcc_set));
2263      if (setptr == NULL) {
2264        k5_mutex_unlock(&krb5int_cc_file_mutex);
2265        k5_mutex_destroy(&data->lock);
2266        free(data->filename);
2267        free(data);
2268        (void) close(ret);
2269        (void) unlink(scratch);
2270        return KRB5_CC_NOMEM;
2271      }
2272      setptr->refcount = 1;
2273      setptr->data = data;
2274      setptr->next = fccs;
2275      fccs = setptr;
2276      k5_mutex_unlock(&krb5int_cc_file_mutex);
2277 
2278      k5_mutex_assert_locked(&data->lock);
2279      k5_mutex_unlock(&data->lock);
2280      lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
2281      if (lid == NULL) {
2282 	 dereference(context, data);
2283 	 return KRB5_CC_NOMEM;
2284      }
2285 
2286      lid->ops = &krb5_fcc_ops;
2287      lid->data = data;
2288      lid->magic = KV5M_CCACHE;
2289 
2290      /* default to open/close on every trn - otherwise destroy
2291 	will get as to state confused */
2292      ((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE;
2293 
2294      *id = lid;
2295 
2296 
2297      krb5_change_cache ();
2298      return KRB5_OK;
2299 
2300 err_out:
2301      k5_mutex_unlock(&krb5int_cc_file_mutex);
2302      k5_mutex_destroy(&data->lock);
2303      free(data->filename);
2304      free(data);
2305      return kret;
2306 }
2307 
2308 /*
2309  * Requires:
2310  * id is a file credential cache
2311  *
2312  * Returns:
2313  * The name of the file cred cache id.
2314  */
2315 static const char * KRB5_CALLCONV
2316 krb5_fcc_get_name (krb5_context context, krb5_ccache id)
2317 {
2318      return (char *) ((krb5_fcc_data *) id->data)->filename;
2319 }
2320 
2321 /*
2322  * Modifies:
2323  * id, princ
2324  *
2325  * Effects:
2326  * Retrieves the primary principal from id, as set with
2327  * krb5_fcc_initialize.  The principal is returned is allocated
2328  * storage that must be freed by the caller via krb5_free_principal.
2329  *
2330  * Errors:
2331  * system errors
2332  * KRB5_CC_NOMEM
2333  */
2334 static krb5_error_code KRB5_CALLCONV
2335 krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
2336 {
2337      krb5_error_code kret = KRB5_OK;
2338 
2339      kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
2340      if (kret)
2341 	 return kret;
2342 
2343      MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2344 
2345      /* make sure we're beyond the header */
2346      kret = krb5_fcc_skip_header(context, id);
2347      if (kret) goto done;
2348      kret = krb5_fcc_read_principal(context, id, princ);
2349 
2350 done:
2351      MAYBE_CLOSE(context, id, kret);
2352      k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
2353      return kret;
2354 }
2355 
2356 
2357 static krb5_error_code KRB5_CALLCONV
2358 krb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
2359 {
2360     return krb5_cc_retrieve_cred_default (context, id, whichfields,
2361 					  mcreds, creds);
2362 }
2363 
2364 
2365 /*
2366  * Modifies:
2367  * the file cache
2368  *
2369  * Effects:
2370  * stores creds in the file cred cache
2371  *
2372  * Errors:
2373  * system errors
2374  * storage failure errors
2375  */
2376 static krb5_error_code KRB5_CALLCONV
2377 krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
2378 {
2379 #define TCHECK(ret) if (ret != KRB5_OK) goto lose;
2380      krb5_error_code ret;
2381 
2382      ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
2383      if (ret)
2384 	 return ret;
2385 
2386      /* Make sure we are writing to the end of the file */
2387      MAYBE_OPEN(context, id, FCC_OPEN_RDWR);
2388 
2389      /* Make sure we are writing to the end of the file */
2390      ret = fcc_lseek((krb5_fcc_data *) id->data, (off_t) 0, SEEK_END);
2391      if (ret < 0) {
2392           MAYBE_CLOSE_IGNORE(context, id);
2393 	  k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
2394 	  return krb5_fcc_interpret(context, errno);
2395      }
2396 
2397      ret = krb5_fcc_store_principal(context, id, creds->client);
2398      TCHECK(ret);
2399      ret = krb5_fcc_store_principal(context, id, creds->server);
2400      TCHECK(ret);
2401      ret = krb5_fcc_store_keyblock(context, id, &creds->keyblock);
2402      TCHECK(ret);
2403      ret = krb5_fcc_store_times(context, id, &creds->times);
2404      TCHECK(ret);
2405      ret = krb5_fcc_store_octet(context, id, (krb5_int32) creds->is_skey);
2406      TCHECK(ret);
2407      ret = krb5_fcc_store_int32(context, id, creds->ticket_flags);
2408      TCHECK(ret);
2409      ret = krb5_fcc_store_addrs(context, id, creds->addresses);
2410      TCHECK(ret);
2411      ret = krb5_fcc_store_authdata(context, id, creds->authdata);
2412      TCHECK(ret);
2413      ret = krb5_fcc_store_data(context, id, &creds->ticket);
2414      TCHECK(ret);
2415      ret = krb5_fcc_store_data(context, id, &creds->second_ticket);
2416      TCHECK(ret);
2417 
2418 lose:
2419      MAYBE_CLOSE(context, id, ret);
2420      k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
2421      krb5_change_cache ();
2422      return ret;
2423 #undef TCHECK
2424 }
2425 
2426 /*
2427  * Non-functional stub implementation for krb5_fcc_remove
2428  *
2429  * Errors:
2430  *    KRB5_CC_NOSUPP - not implemented
2431  */
2432 static krb5_error_code KRB5_CALLCONV
2433 krb5_fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
2434                      krb5_creds *creds)
2435 {
2436     return KRB5_CC_NOSUPP;
2437 }
2438 
2439 /*
2440  * Requires:
2441  * id is a cred cache returned by krb5_fcc_resolve or
2442  * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2443  *
2444  * Modifies:
2445  * id
2446  *
2447  * Effects:
2448  * Sets the operational flags of id to flags.
2449  */
2450 static krb5_error_code KRB5_CALLCONV
2451 krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
2452 {
2453     krb5_error_code ret = KRB5_OK;
2454 
2455     ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
2456     if (ret)
2457 	return ret;
2458 
2459     /* XXX This should check for illegal combinations, if any.. */
2460     if (flags & KRB5_TC_OPENCLOSE) {
2461 	/* asking to turn on OPENCLOSE mode */
2462 	if (!OPENCLOSE(id)
2463 	    /* XXX Is this test necessary? */
2464 	    && ((krb5_fcc_data *) id->data)->file != NO_FILE)
2465             (void) krb5_fcc_close_file (context, ((krb5_fcc_data *) id->data));
2466     } else {
2467 	/* asking to turn off OPENCLOSE mode, meaning it must be
2468 	   left open.  We open if it's not yet open */
2469         MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2470     }
2471 
2472     ((krb5_fcc_data *) id->data)->flags = flags;
2473     k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
2474     return ret;
2475 }
2476 
2477 /*
2478  * Requires:
2479  * id is a cred cache returned by krb5_fcc_resolve or
2480  * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2481  *
2482  * Modifies:
2483  * id (mutex only; temporary)
2484  *
2485  * Effects:
2486  * Returns the operational flags of id.
2487  */
2488 static krb5_error_code KRB5_CALLCONV
2489 krb5_fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
2490 {
2491     krb5_error_code ret = KRB5_OK;
2492 
2493     ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
2494     if (ret)
2495 	return ret;
2496     *flags = ((krb5_fcc_data *) id->data)->flags;
2497     k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
2498     return ret;
2499 }
2500 
2501 
2502 static krb5_error_code
2503 krb5_fcc_interpret(krb5_context context, int errnum)
2504 {
2505     register krb5_error_code retval;
2506     switch (errnum) {
2507     case ENOENT:
2508 	retval = KRB5_FCC_NOFILE;
2509 	break;
2510     case EPERM:
2511     case EACCES:
2512 #ifdef EISDIR
2513     case EISDIR:                        /* Mac doesn't have EISDIR */
2514 #endif
2515     case ENOTDIR:
2516 #ifdef ELOOP
2517     case ELOOP:                         /* Bad symlink is like no file. */
2518 #endif
2519 #ifdef ETXTBSY
2520     case ETXTBSY:
2521 #endif
2522     case EBUSY:
2523     case EROFS:
2524 	retval = KRB5_FCC_PERM;
2525 	break;
2526     case EINVAL:
2527     case EEXIST:			/* XXX */
2528     case EFAULT:
2529     case EBADF:
2530 #ifdef ENAMETOOLONG
2531     case ENAMETOOLONG:
2532 #endif
2533 #ifdef EWOULDBLOCK
2534     case EWOULDBLOCK:
2535 #endif
2536 	retval = KRB5_FCC_INTERNAL;
2537 	break;
2538 #ifdef EDQUOT
2539     case EDQUOT:
2540 #endif
2541     case ENOSPC:
2542     case EIO:
2543     case ENFILE:
2544     case EMFILE:
2545     case ENXIO:
2546     default:
2547 	retval = KRB5_CC_IO;		/* XXX */
2548 	krb5_set_error_message(context, retval,
2549 			       "Credentials cache I/O operation failed (%s)",
2550 			       strerror(errnum));
2551     }
2552     return retval;
2553 }
2554 
2555 const krb5_cc_ops krb5_fcc_ops = {
2556      0,
2557      "FILE",
2558      krb5_fcc_get_name,
2559      krb5_fcc_resolve,
2560      krb5_fcc_generate_new,
2561      krb5_fcc_initialize,
2562      krb5_fcc_destroy,
2563      krb5_fcc_close,
2564      krb5_fcc_store,
2565      krb5_fcc_retrieve,
2566      krb5_fcc_get_principal,
2567      krb5_fcc_start_seq_get,
2568      krb5_fcc_next_cred,
2569      krb5_fcc_end_seq_get,
2570      krb5_fcc_remove_cred,
2571      krb5_fcc_set_flags,
2572      krb5_fcc_get_flags,
2573 };
2574 
2575 #if defined(_WIN32)
2576 /*
2577  * krb5_change_cache should be called after the cache changes.
2578  * A notification message is is posted out to all top level
2579  * windows so that they may recheck the cache based on the
2580  * changes made.  We register a unique message type with which
2581  * we'll communicate to all other processes.
2582  */
2583 
2584 krb5_error_code
2585 krb5_change_cache (void) {
2586 
2587     PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0);
2588 
2589     return 0;
2590 }
2591 
2592 unsigned int KRB5_CALLCONV
2593 krb5_get_notification_message (void) {
2594     static unsigned int message = 0;
2595 
2596     if (message == 0)
2597         message = RegisterWindowMessage(WM_KERBEROS5_CHANGED);
2598 
2599     return message;
2600 }
2601 #else /* _WIN32 */
2602 
2603 krb5_error_code
2604 krb5_change_cache (void)
2605 {
2606     return 0;
2607 }
2608 unsigned int
2609 krb5_get_notification_message (void)
2610 {
2611     return 0;
2612 }
2613 
2614 #endif /* _WIN32 */
2615 
2616 const krb5_cc_ops krb5_cc_file_ops = {
2617      0,
2618      "FILE",
2619      krb5_fcc_get_name,
2620      krb5_fcc_resolve,
2621      krb5_fcc_generate_new,
2622      krb5_fcc_initialize,
2623      krb5_fcc_destroy,
2624      krb5_fcc_close,
2625      krb5_fcc_store,
2626      krb5_fcc_retrieve,
2627      krb5_fcc_get_principal,
2628      krb5_fcc_start_seq_get,
2629      krb5_fcc_next_cred,
2630      krb5_fcc_end_seq_get,
2631      krb5_fcc_remove_cred,
2632      krb5_fcc_set_flags,
2633      krb5_fcc_get_flags,
2634      NULL,
2635      NULL,
2636      NULL,
2637      NULL,
2638      NULL,
2639      NULL,
2640 };
2641