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