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