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