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