1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_file.c - File-based credential cache */
4 * Copyright 1990,1991,1992,1993,1994,2000,2004,2007 Massachusetts Institute of Technology.
7 * Original stdio support copyright 1995 by Cygnus Support.
9 * Export of this software from the United States of America may
10 * require a specific license from the United States Government.
11 * It is the responsibility of any person or organization contemplating
12 * export to obtain such a license before exporting.
14 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
15 * distribute this software and its documentation for any purpose and
16 * without fee is hereby granted, provided that the above copyright
17 * notice appear in all copies and that both that copyright notice and
18 * this permission notice appear in supporting documentation, and that
19 * the name of M.I.T. not be used in advertising or publicity pertaining
20 * to distribution of the software without specific, written prior
21 * permission. Furthermore if you modify this software you must label
22 * your software as modified software and not distribute it in such a
23 * fashion that it might be confused with the original M.I.T. software.
24 * M.I.T. makes no representations about the suitability of
25 * this software for any purpose. It is provided "as is" without express
26 * or implied warranty.
30 If OPENCLOSE is defined, each of the functions opens and closes the
31 file whenever it needs to access it. Otherwise, the file is opened
32 once in initialize and closed once is close.
34 This library depends on UNIX-like file descriptors, and UNIX-like
35 behavior from the functions: open, close, read, write, lseek.
37 The quasi-BNF grammar for a credentials cache:
40 principal list-of-credentials
53 number of components (int32)
60 string of length bytes
65 Make sure that each time a function returns KRB5_NOMEM, everything
66 allocated earlier in the function and stack tree is freed.
70 Use pread/pwrite if available, so multiple threads can read
71 simultaneously. (That may require reader/writer locks.)
73 fcc_nseq.c and fcc_read don't check return values a lot.
85 #ifdef HAVE_NETINET_IN_H
87 #include <netinet/in.h>
89 #include "port-sockets.h"
92 # error find some way to use net-byte-order file version numbers.
95 static krb5_error_code KRB5_CALLCONV krb5_fcc_close
96 (krb5_context, krb5_ccache id);
98 static krb5_error_code KRB5_CALLCONV krb5_fcc_destroy
99 (krb5_context, krb5_ccache id);
101 static krb5_error_code KRB5_CALLCONV krb5_fcc_end_seq_get
102 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
104 static krb5_error_code KRB5_CALLCONV krb5_fcc_generate_new
105 (krb5_context, krb5_ccache *id);
107 static const char * KRB5_CALLCONV krb5_fcc_get_name
108 (krb5_context, krb5_ccache id);
110 static krb5_error_code KRB5_CALLCONV krb5_fcc_get_principal
111 (krb5_context, krb5_ccache id, krb5_principal *princ);
113 static krb5_error_code KRB5_CALLCONV krb5_fcc_initialize
114 (krb5_context, krb5_ccache id, krb5_principal princ);
116 static krb5_error_code KRB5_CALLCONV krb5_fcc_next_cred
117 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor,
120 static krb5_error_code krb5_fcc_read
121 (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
122 static krb5_error_code krb5_fcc_read_principal
123 (krb5_context, krb5_ccache id, krb5_principal *princ);
124 static krb5_error_code krb5_fcc_read_keyblock
125 (krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
126 static krb5_error_code krb5_fcc_read_data
127 (krb5_context, krb5_ccache id, krb5_data *data);
128 static krb5_error_code krb5_fcc_read_int32
129 (krb5_context, krb5_ccache id, krb5_int32 *i);
130 static krb5_error_code krb5_fcc_read_ui_2
131 (krb5_context, krb5_ccache id, krb5_ui_2 *i);
132 static krb5_error_code krb5_fcc_read_octet
133 (krb5_context, krb5_ccache id, krb5_octet *i);
134 static krb5_error_code krb5_fcc_read_times
135 (krb5_context, krb5_ccache id, krb5_ticket_times *t);
136 static krb5_error_code krb5_fcc_read_addrs
137 (krb5_context, krb5_ccache, krb5_address ***);
138 static krb5_error_code krb5_fcc_read_addr
139 (krb5_context, krb5_ccache, krb5_address *);
140 static krb5_error_code krb5_fcc_read_authdata
141 (krb5_context, krb5_ccache, krb5_authdata ***);
142 static krb5_error_code krb5_fcc_read_authdatum
143 (krb5_context, krb5_ccache, krb5_authdata *);
145 static krb5_error_code KRB5_CALLCONV krb5_fcc_resolve
146 (krb5_context, krb5_ccache *id, const char *residual);
148 static krb5_error_code KRB5_CALLCONV krb5_fcc_retrieve
149 (krb5_context, krb5_ccache id, krb5_flags whichfields,
150 krb5_creds *mcreds, krb5_creds *creds);
152 static krb5_error_code KRB5_CALLCONV krb5_fcc_start_seq_get
153 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
155 static krb5_error_code KRB5_CALLCONV krb5_fcc_store
156 (krb5_context, krb5_ccache id, krb5_creds *creds);
158 static krb5_error_code krb5_fcc_skip_header
159 (krb5_context, krb5_ccache);
160 static krb5_error_code krb5_fcc_skip_principal
161 (krb5_context, krb5_ccache id);
163 static krb5_error_code KRB5_CALLCONV krb5_fcc_set_flags
164 (krb5_context, krb5_ccache id, krb5_flags flags);
166 static krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_new
167 (krb5_context context, krb5_cc_ptcursor *cursor);
169 static krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_next
170 (krb5_context context, krb5_cc_ptcursor cursor, krb5_ccache *ccache);
172 static krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_free
173 (krb5_context context, krb5_cc_ptcursor *cursor);
175 static krb5_error_code KRB5_CALLCONV krb5_fcc_last_change_time
176 (krb5_context context, krb5_ccache id, krb5_timestamp *change_time);
178 static krb5_error_code KRB5_CALLCONV krb5_fcc_lock
179 (krb5_context context, krb5_ccache id);
181 static krb5_error_code KRB5_CALLCONV krb5_fcc_unlock
182 (krb5_context context, krb5_ccache id);
185 extern const krb5_cc_ops krb5_cc_file_ops;
187 krb5_error_code krb5_change_cache (void);
189 static krb5_error_code krb5_fcc_write
190 (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
191 static krb5_error_code krb5_fcc_store_principal
192 (krb5_context, krb5_ccache id, krb5_principal princ);
193 static krb5_error_code krb5_fcc_store_keyblock
194 (krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
195 static krb5_error_code krb5_fcc_store_data
196 (krb5_context, krb5_ccache id, krb5_data *data);
197 static krb5_error_code krb5_fcc_store_int32
198 (krb5_context, krb5_ccache id, krb5_int32 i);
199 static krb5_error_code krb5_fcc_store_ui_4
200 (krb5_context, krb5_ccache id, krb5_ui_4 i);
201 static krb5_error_code krb5_fcc_store_ui_2
202 (krb5_context, krb5_ccache id, krb5_int32 i);
203 static krb5_error_code krb5_fcc_store_octet
204 (krb5_context, krb5_ccache id, krb5_int32 i);
205 static krb5_error_code krb5_fcc_store_times
206 (krb5_context, krb5_ccache id, krb5_ticket_times *t);
207 static krb5_error_code krb5_fcc_store_addrs
208 (krb5_context, krb5_ccache, krb5_address **);
209 static krb5_error_code krb5_fcc_store_addr
210 (krb5_context, krb5_ccache, krb5_address *);
211 static krb5_error_code krb5_fcc_store_authdata
212 (krb5_context, krb5_ccache, krb5_authdata **);
213 static krb5_error_code krb5_fcc_store_authdatum
214 (krb5_context, krb5_ccache, krb5_authdata *);
216 static krb5_error_code krb5_fcc_interpret
219 struct _krb5_fcc_data;
220 static krb5_error_code krb5_fcc_close_file
221 (krb5_context, struct _krb5_fcc_data *data);
222 static krb5_error_code krb5_fcc_open_file
223 (krb5_context, krb5_ccache, int);
224 static krb5_error_code krb5_fcc_data_last_change_time
225 (krb5_context context, struct _krb5_fcc_data *data,
226 krb5_timestamp *change_time);
231 #define KRB5_FCC_MAXLEN 100
234 * FCC version 2 contains type information for principals. FCC
235 * version 1 does not.
237 * FCC version 3 contains keyblock encryption type information, and is
238 * architecture independent. Previous versions are not.
240 * The code will accept version 1, 2, and 3 ccaches, and depending
241 * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2,
244 * The default credentials cache should be type 3 for now (see
248 #define KRB5_FCC_FVNO_1 0x0501 /* krb v5, fcc v1 */
249 #define KRB5_FCC_FVNO_2 0x0502 /* krb v5, fcc v2 */
250 #define KRB5_FCC_FVNO_3 0x0503 /* krb v5, fcc v3 */
251 #define KRB5_FCC_FVNO_4 0x0504 /* krb v5, fcc v4 */
253 #define FCC_OPEN_AND_ERASE 1
254 #define FCC_OPEN_RDWR 2
255 #define FCC_OPEN_RDONLY 3
257 /* Credential file header tags.
258 * The header tags are constructed as:
261 * krb5_octet data[len]
262 * This format allows for older versions of the fcc processing code to skip
263 * past unrecognized tag formats.
265 #define FCC_TAG_DELTATIME 1
268 #ifdef MSDOS_FILESYSTEM
269 #define TKT_ROOT "\\tkt"
271 #define TKT_ROOT "/tmp/tkt"
275 /* macros to make checking flags easier */
276 #define OPENCLOSE(id) (((krb5_fcc_data *)id->data)->flags & KRB5_TC_OPENCLOSE)
278 typedef struct _krb5_fcc_data {
280 /* Lock this one before reading or modifying the data stored here
281 that can be changed. (Filename is fixed after
286 int mode; /* needed for locking code */
287 int version; /* version number of the file */
289 /* Buffer data on reading, for performance.
290 We used to have a stdio option, but we get more precise control
291 by using the POSIX I/O functions. */
292 #define FCC_BUFSIZ 1024
295 char buf[FCC_BUFSIZ];
298 static inline void invalidate_cache(krb5_fcc_data *data)
300 data->valid_bytes = 0;
303 static off_t fcc_lseek(krb5_fcc_data *data, off_t offset, int whence)
305 /* If we read some extra data in advance, and then want to know or
306 use our "current" position, we need to back up a little. */
307 if (whence == SEEK_CUR && data->valid_bytes) {
308 assert(data->cur_offset > 0);
309 assert(data->cur_offset <= data->valid_bytes);
310 offset -= (data->valid_bytes - data->cur_offset);
312 invalidate_cache(data);
313 return lseek(data->file, offset, whence);
317 struct fcc_set *next;
319 unsigned int refcount;
322 k5_cc_mutex krb5int_cc_file_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
323 static struct fcc_set *fccs = NULL;
325 /* Iterator over file caches. */
326 struct krb5_fcc_ptcursor_data {
330 /* An off_t can be arbitrarily complex */
331 typedef struct _krb5_fcc_cursor {
335 #define MAYBE_OPEN(CONTEXT, ID, MODE) \
337 k5_cc_mutex_assert_locked(CONTEXT, &((krb5_fcc_data *)(ID)->data)->lock); \
338 if (OPENCLOSE (ID)) { \
339 krb5_error_code maybe_open_ret; \
340 maybe_open_ret = krb5_fcc_open_file (CONTEXT,ID,MODE); \
341 if (maybe_open_ret) { \
342 k5_cc_mutex_unlock(CONTEXT, &((krb5_fcc_data *)(ID)->data)->lock); \
343 return maybe_open_ret; \
348 #define MAYBE_CLOSE(CONTEXT, ID, RET) \
350 if (OPENCLOSE (ID)) { \
351 krb5_error_code maybe_close_ret; \
352 maybe_close_ret = krb5_fcc_close_file (CONTEXT, \
353 (krb5_fcc_data *)(ID)->data); \
354 if (!(RET)) RET = maybe_close_ret; } }
356 #define MAYBE_CLOSE_IGNORE(CONTEXT, ID) \
358 if (OPENCLOSE (ID)) { \
359 (void) krb5_fcc_close_file (CONTEXT,(krb5_fcc_data *)(ID)->data); } }
361 #define CHECK(ret) if (ret != KRB5_OK) goto errout;
367 * Reads len bytes from the cache id, storing them in buf.
370 * Must be called with mutex locked.
373 * KRB5_CC_END - there were not len bytes available
374 * system errors (read)
376 static krb5_error_code
377 krb5_fcc_read(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
382 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
384 ret = read(((krb5_fcc_data *) id->data)->file, (char *) buf, len);
386 return krb5_fcc_interpret(context, errno);
392 krb5_fcc_data *data = (krb5_fcc_data *) id->data;
394 k5_cc_mutex_assert_locked(context, &data->lock);
400 if (data->valid_bytes > 0)
401 assert(data->cur_offset <= data->valid_bytes);
402 if (data->valid_bytes == 0
403 || data->cur_offset == data->valid_bytes) {
404 /* Fill buffer from current file position. */
405 nread = read(data->file, data->buf, sizeof(data->buf));
408 return krb5_fcc_interpret(context, e);
412 data->valid_bytes = nread;
413 data->cur_offset = 0;
415 assert(data->cur_offset < data->valid_bytes);
417 assert(ncopied == len);
418 if (data->valid_bytes - data->cur_offset < ncopied)
419 ncopied = data->valid_bytes - data->cur_offset;
420 memcpy(buf, data->buf + data->cur_offset, ncopied);
421 data->cur_offset += ncopied;
422 assert(data->cur_offset > 0);
423 assert(data->cur_offset <= data->valid_bytes);
425 /* Don't do arithmetic on void pointers. */
426 buf = (char*)buf + ncopied;
433 * FOR ALL OF THE FOLLOWING FUNCTIONS:
436 * id is open and set to read at the appropriate place in the file
441 * Fills in the second argument with data of the appropriate type from
442 * the file. In some cases, the functions have to allocate space for
443 * variable length fields; therefore, krb5_destroy_<type> must be
444 * called for each filled in structure.
447 * system errors (read errors)
451 #define ALLOC(NUM,TYPE) \
452 (((NUM) <= (((size_t)0-1)/ sizeof(TYPE))) \
453 ? (TYPE *) calloc((NUM), sizeof(TYPE)) \
454 : (errno = ENOMEM,(TYPE *) 0))
456 static krb5_error_code
457 krb5_fcc_read_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
459 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
460 krb5_error_code kret;
461 register krb5_principal tmpprinc;
462 krb5_int32 length, type;
465 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
469 if (data->version == KRB5_FCC_FVNO_1) {
470 type = KRB5_NT_UNKNOWN;
472 /* Read principal type */
473 kret = krb5_fcc_read_int32(context, id, &type);
478 /* Read the number of components */
479 kret = krb5_fcc_read_int32(context, id, &length);
484 * DCE includes the principal's realm in the count; the new format
487 if (data->version == KRB5_FCC_FVNO_1)
490 return KRB5_CC_NOMEM;
492 tmpprinc = (krb5_principal) malloc(sizeof(krb5_principal_data));
493 if (tmpprinc == NULL)
494 return KRB5_CC_NOMEM;
496 size_t msize = length;
497 if (msize != (krb5_ui_4) length) {
499 return KRB5_CC_NOMEM;
501 tmpprinc->data = ALLOC (msize, krb5_data);
502 if (tmpprinc->data == 0) {
504 return KRB5_CC_NOMEM;
508 tmpprinc->magic = KV5M_PRINCIPAL;
509 tmpprinc->length = length;
510 tmpprinc->type = type;
512 kret = krb5_fcc_read_data(context, id, krb5_princ_realm(context, tmpprinc));
517 for (i=0; i < length; i++) {
518 kret = krb5_fcc_read_data(context, id, krb5_princ_component(context, tmpprinc, i));
526 free(krb5_princ_component(context, tmpprinc, i)->data);
527 free(krb5_princ_realm(context, tmpprinc)->data);
528 free(tmpprinc->data);
533 static krb5_error_code
534 krb5_fcc_read_addrs(krb5_context context, krb5_ccache id, krb5_address ***addrs)
536 krb5_error_code kret;
541 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
545 /* Read the number of components */
546 kret = krb5_fcc_read_int32(context, id, &length);
549 /* Make *addrs able to hold length pointers to krb5_address structs
550 * Add one extra for a null-terminated list
554 if (msize == 0 || msize - 1 != (krb5_ui_4) length || length < 0)
555 return KRB5_CC_NOMEM;
556 *addrs = ALLOC (msize, krb5_address *);
558 return KRB5_CC_NOMEM;
560 for (i=0; i < length; i++) {
561 (*addrs)[i] = (krb5_address *) malloc(sizeof(krb5_address));
562 if ((*addrs)[i] == NULL) {
563 krb5_free_addresses(context, *addrs);
565 return KRB5_CC_NOMEM;
567 (*addrs)[i]->contents = NULL;
568 kret = krb5_fcc_read_addr(context, id, (*addrs)[i]);
575 krb5_free_addresses(context, *addrs);
581 static krb5_error_code
582 krb5_fcc_read_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
584 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
585 krb5_error_code kret;
589 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
591 keyblock->magic = KV5M_KEYBLOCK;
592 keyblock->contents = 0;
594 /* Enctype is signed, so sign-extend the 16-bit value we read. */
595 kret = krb5_fcc_read_ui_2(context, id, &ui2);
596 keyblock->enctype = (krb5_int16) ui2;
598 if (data->version == KRB5_FCC_FVNO_3) {
599 /* This works because the old etype is the same as the new enctype. */
600 kret = krb5_fcc_read_ui_2(context, id, &ui2);
601 /* keyblock->enctype = ui2; */
605 kret = krb5_fcc_read_int32(context, id, &int32);
608 return KRB5_CC_NOMEM;
609 keyblock->length = int32;
610 /* Overflow check. */
611 if (keyblock->length != (krb5_ui_4) int32)
612 return KRB5_CC_NOMEM;
613 if ( keyblock->length == 0 )
615 keyblock->contents = malloc(keyblock->length);
616 if (keyblock->contents == NULL)
617 return KRB5_CC_NOMEM;
619 kret = krb5_fcc_read(context, id, keyblock->contents, keyblock->length);
625 if (keyblock->contents) {
626 free(keyblock->contents);
627 keyblock->contents = NULL;
632 static krb5_error_code
633 krb5_fcc_read_data(krb5_context context, krb5_ccache id, krb5_data *data)
635 krb5_error_code kret;
638 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
640 data->magic = KV5M_DATA;
643 kret = krb5_fcc_read_int32(context, id, &len);
646 return KRB5_CC_NOMEM;
648 if (data->length != (krb5_ui_4) len || data->length + 1 == 0)
649 return KRB5_CC_NOMEM;
651 if (data->length == 0) {
656 data->data = (char *) malloc(data->length+1);
657 if (data->data == NULL)
658 return KRB5_CC_NOMEM;
660 kret = krb5_fcc_read(context, id, data->data, (unsigned) data->length);
663 data->data[data->length] = 0; /* Null terminate, just in case.... */
673 static krb5_error_code
674 krb5_fcc_read_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
676 krb5_error_code kret;
680 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
682 addr->magic = KV5M_ADDRESS;
685 kret = krb5_fcc_read_ui_2(context, id, &ui2);
687 addr->addrtype = ui2;
689 kret = krb5_fcc_read_int32(context, id, &int32);
691 if ((int32 & VALID_INT_BITS) != int32) /* Overflow int??? */
692 return KRB5_CC_NOMEM;
693 addr->length = int32;
694 /* Length field is "unsigned int", which may be smaller than 32
696 if (addr->length != (krb5_ui_4) int32)
697 return KRB5_CC_NOMEM; /* XXX */
699 if (addr->length == 0)
702 addr->contents = (krb5_octet *) malloc(addr->length);
703 if (addr->contents == NULL)
704 return KRB5_CC_NOMEM;
706 kret = krb5_fcc_read(context, id, addr->contents, addr->length);
711 if (addr->contents) {
712 free(addr->contents);
713 addr->contents = NULL;
718 static krb5_error_code
719 krb5_fcc_read_int32(krb5_context context, krb5_ccache id, krb5_int32 *i)
721 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
722 krb5_error_code retval;
723 unsigned char buf[4];
725 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
727 if ((data->version == KRB5_FCC_FVNO_1) ||
728 (data->version == KRB5_FCC_FVNO_2))
729 return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_int32));
731 retval = krb5_fcc_read(context, id, buf, 4);
734 *i = load_32_be (buf);
739 static krb5_error_code
740 krb5_fcc_read_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 *i)
742 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
743 krb5_error_code retval;
744 unsigned char buf[2];
746 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
748 if ((data->version == KRB5_FCC_FVNO_1) ||
749 (data->version == KRB5_FCC_FVNO_2))
750 return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_ui_2));
752 retval = krb5_fcc_read(context, id, buf, 2);
755 *i = load_16_be (buf);
760 static krb5_error_code
761 krb5_fcc_read_octet(krb5_context context, krb5_ccache id, krb5_octet *i)
763 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
764 return krb5_fcc_read(context, id, (krb5_pointer) i, 1);
768 static krb5_error_code
769 krb5_fcc_read_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
771 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
772 krb5_error_code retval;
775 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
777 if ((data->version == KRB5_FCC_FVNO_1) ||
778 (data->version == KRB5_FCC_FVNO_2))
779 return krb5_fcc_read(context, id, (krb5_pointer) t, sizeof(krb5_ticket_times));
781 retval = krb5_fcc_read_int32(context, id, &i);
785 retval = krb5_fcc_read_int32(context, id, &i);
789 retval = krb5_fcc_read_int32(context, id, &i);
793 retval = krb5_fcc_read_int32(context, id, &i);
802 static krb5_error_code
803 krb5_fcc_read_authdata(krb5_context context, krb5_ccache id, krb5_authdata ***a)
805 krb5_error_code kret;
810 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
814 /* Read the number of components */
815 kret = krb5_fcc_read_int32(context, id, &length);
821 /* Make *a able to hold length pointers to krb5_authdata structs
822 * Add one extra for a null-terminated list
826 if (msize == 0 || msize - 1 != (krb5_ui_4) length || length < 0)
827 return KRB5_CC_NOMEM;
828 *a = ALLOC (msize, krb5_authdata *);
830 return KRB5_CC_NOMEM;
832 for (i=0; i < length; i++) {
833 (*a)[i] = (krb5_authdata *) malloc(sizeof(krb5_authdata));
834 if ((*a)[i] == NULL) {
835 krb5_free_authdata(context, *a);
837 return KRB5_CC_NOMEM;
839 (*a)[i]->contents = NULL;
840 kret = krb5_fcc_read_authdatum(context, id, (*a)[i]);
847 krb5_free_authdata(context, *a);
853 static krb5_error_code
854 krb5_fcc_read_authdatum(krb5_context context, krb5_ccache id, krb5_authdata *a)
856 krb5_error_code kret;
858 krb5_int16 ui2; /* negative authorization data types are allowed */
860 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
862 a->magic = KV5M_AUTHDATA;
865 kret = krb5_fcc_read_ui_2(context, id, (krb5_ui_2 *)&ui2);
867 a->ad_type = (krb5_authdatatype)ui2;
868 kret = krb5_fcc_read_int32(context, id, &int32);
870 if ((int32 & VALID_INT_BITS) != int32) /* Overflow int??? */
871 return KRB5_CC_NOMEM;
873 /* Value could have gotten truncated if int is smaller than 32
875 if (a->length != (krb5_ui_4) int32)
876 return KRB5_CC_NOMEM; /* XXX */
881 a->contents = (krb5_octet *) malloc(a->length);
882 if (a->contents == NULL)
883 return KRB5_CC_NOMEM;
885 kret = krb5_fcc_read(context, id, a->contents, a->length);
899 #define CHECK(ret) if (ret != KRB5_OK) return ret;
906 * Writes len bytes from buf into the file cred cache id.
911 static krb5_error_code
912 krb5_fcc_write(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
916 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
917 invalidate_cache((krb5_fcc_data *) id->data);
919 ret = write(((krb5_fcc_data *)id->data)->file, (char *) buf, len);
921 return krb5_fcc_interpret(context, errno);
922 if ((unsigned int) ret != len)
923 return KRB5_CC_WRITE;
928 * FOR ALL OF THE FOLLOWING FUNCTIONS:
931 * ((krb5_fcc_data *) id->data)->file is open and at the right position.
936 * Stores an encoded version of the second argument in the
943 static krb5_error_code
944 krb5_fcc_store_principal(krb5_context context, krb5_ccache id, krb5_principal princ)
946 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
948 krb5_int32 i, length, tmp, type;
950 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
952 type = krb5_princ_type(context, princ);
953 tmp = length = krb5_princ_size(context, princ);
955 if (data->version == KRB5_FCC_FVNO_1) {
957 * DCE-compatible format means that the length count
958 * includes the realm. (It also doesn't include the
959 * principal type information.)
963 ret = krb5_fcc_store_int32(context, id, type);
967 ret = krb5_fcc_store_int32(context, id, tmp);
970 ret = krb5_fcc_store_data(context, id, krb5_princ_realm(context, princ));
973 for (i=0; i < length; i++) {
974 ret = krb5_fcc_store_data(context, id, krb5_princ_component(context, princ, i));
981 static krb5_error_code
982 krb5_fcc_store_addrs(krb5_context context, krb5_ccache id, krb5_address **addrs)
986 krb5_int32 i, length = 0;
988 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
990 /* Count the number of components */
997 ret = krb5_fcc_store_int32(context, id, length);
999 for (i=0; i < length; i++) {
1000 ret = krb5_fcc_store_addr(context, id, addrs[i]);
1007 static krb5_error_code
1008 krb5_fcc_store_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
1010 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1011 krb5_error_code ret;
1013 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1015 ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
1017 if (data->version == KRB5_FCC_FVNO_3) {
1018 ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
1021 ret = krb5_fcc_store_ui_4(context, id, keyblock->length);
1023 return krb5_fcc_write(context, id, (char *) keyblock->contents, keyblock->length);
1026 static krb5_error_code
1027 krb5_fcc_store_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
1029 krb5_error_code ret;
1031 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1033 ret = krb5_fcc_store_ui_2(context, id, addr->addrtype);
1035 ret = krb5_fcc_store_ui_4(context, id, addr->length);
1037 return krb5_fcc_write(context, id, (char *) addr->contents, addr->length);
1041 static krb5_error_code
1042 krb5_fcc_store_data(krb5_context context, krb5_ccache id, krb5_data *data)
1044 krb5_error_code ret;
1046 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1048 ret = krb5_fcc_store_ui_4(context, id, data->length);
1050 return krb5_fcc_write(context, id, data->data, data->length);
1053 static krb5_error_code
1054 krb5_fcc_store_int32(krb5_context context, krb5_ccache id, krb5_int32 i)
1056 return krb5_fcc_store_ui_4(context, id, (krb5_ui_4) i);
1059 static krb5_error_code
1060 krb5_fcc_store_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i)
1062 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1063 unsigned char buf[4];
1065 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1067 if ((data->version == KRB5_FCC_FVNO_1) ||
1068 (data->version == KRB5_FCC_FVNO_2))
1069 return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32));
1071 store_32_be (i, buf);
1072 return krb5_fcc_write(context, id, buf, 4);
1076 static krb5_error_code
1077 krb5_fcc_store_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i)
1079 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1081 unsigned char buf[2];
1083 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1085 if ((data->version == KRB5_FCC_FVNO_1) ||
1086 (data->version == KRB5_FCC_FVNO_2)) {
1087 ibuf = (krb5_ui_2) i;
1088 return krb5_fcc_write(context, id, (char *) &ibuf, sizeof(krb5_ui_2));
1090 store_16_be (i, buf);
1091 return krb5_fcc_write(context, id, buf, 2);
1095 static krb5_error_code
1096 krb5_fcc_store_octet(krb5_context context, krb5_ccache id, krb5_int32 i)
1100 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1102 ibuf = (krb5_octet) i;
1103 return krb5_fcc_write(context, id, (char *) &ibuf, 1);
1106 static krb5_error_code
1107 krb5_fcc_store_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
1109 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1110 krb5_error_code retval;
1112 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1114 if ((data->version == KRB5_FCC_FVNO_1) ||
1115 (data->version == KRB5_FCC_FVNO_2))
1116 return krb5_fcc_write(context, id, (char *) t, sizeof(krb5_ticket_times));
1118 retval = krb5_fcc_store_int32(context, id, t->authtime);
1120 retval = krb5_fcc_store_int32(context, id, t->starttime);
1122 retval = krb5_fcc_store_int32(context, id, t->endtime);
1124 retval = krb5_fcc_store_int32(context, id, t->renew_till);
1130 static krb5_error_code
1131 krb5_fcc_store_authdata(krb5_context context, krb5_ccache id, krb5_authdata **a)
1133 krb5_error_code ret;
1134 krb5_authdata **temp;
1135 krb5_int32 i, length=0;
1137 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1140 for (temp=a; *temp; temp++)
1144 ret = krb5_fcc_store_int32(context, id, length);
1146 for (i=0; i<length; i++) {
1147 ret = krb5_fcc_store_authdatum (context, id, a[i]);
1153 static krb5_error_code
1154 krb5_fcc_store_authdatum (krb5_context context, krb5_ccache id, krb5_authdata *a)
1156 krb5_error_code ret;
1158 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1160 ret = krb5_fcc_store_ui_2(context, id, a->ad_type);
1162 ret = krb5_fcc_store_ui_4(context, id, a->length);
1164 return krb5_fcc_write(context, id, (krb5_pointer) a->contents, a->length);
1168 static krb5_error_code
1169 krb5_fcc_close_file (krb5_context context, krb5_fcc_data *data)
1172 krb5_error_code retval;
1174 k5_cc_mutex_assert_locked(context, &data->lock);
1176 if (data->file == NO_FILE)
1177 return KRB5_FCC_INTERNAL;
1179 retval = krb5_unlock_file(context, data->file);
1180 ret = close (data->file);
1181 data->file = NO_FILE;
1185 return ret ? krb5_fcc_interpret (context, errno) : 0;
1188 #if defined(ANSI_STDIO) || defined(_WIN32)
1189 #define BINARY_MODE "b"
1191 #define BINARY_MODE ""
1194 #ifndef HAVE_SETVBUF
1196 #define setvbuf(FILE,BUF,MODE,SIZE) \
1197 ((SIZE) < BUFSIZE ? (abort(),0) : setbuf(FILE, BUF))
1200 static krb5_error_code
1201 krb5_fcc_open_file (krb5_context context, krb5_ccache id, int mode)
1203 krb5_os_context os_ctx = &context->os_context;
1204 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1208 krb5_ui_2 fcc_taglen;
1211 krb5_error_code retval = 0;
1213 k5_cc_mutex_assert_locked(context, &data->lock);
1214 invalidate_cache(data);
1216 if (data->file != NO_FILE) {
1217 /* Don't know what state it's in; shut down and start anew. */
1218 (void) krb5_unlock_file(context, data->file);
1219 (void) close (data->file);
1220 data->file = NO_FILE;
1224 case FCC_OPEN_AND_ERASE:
1225 unlink(data->filename);
1226 open_flag = O_CREAT|O_EXCL|O_TRUNC|O_RDWR;
1231 case FCC_OPEN_RDONLY:
1233 open_flag = O_RDONLY;
1237 f = THREEPARAMOPEN (data->filename, open_flag | O_BINARY, 0600);
1241 retval = KRB5_FCC_NOFILE;
1242 krb5_set_error_message(context, retval,
1243 "Credentials cache file '%s' not found",
1247 return krb5_fcc_interpret (context, errno);
1254 if (data->mode == FCC_OPEN_RDONLY)
1255 lock_flag = KRB5_LOCKMODE_SHARED;
1257 lock_flag = KRB5_LOCKMODE_EXCLUSIVE;
1258 if ((retval = krb5_lock_file(context, f, lock_flag))) {
1263 if (mode == FCC_OPEN_AND_ERASE) {
1264 /* write the version number */
1267 fcc_fvno = htons(context->fcc_default_format);
1268 data->version = context->fcc_default_format;
1269 if ((cnt = write(f, (char *)&fcc_fvno, sizeof(fcc_fvno))) !=
1271 retval = ((cnt == -1) ? krb5_fcc_interpret(context, errno) :
1277 if (data->version == KRB5_FCC_FVNO_4) {
1278 /* V4 of the credentials cache format allows for header tags */
1281 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
1282 fcc_flen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32));
1284 /* Write header length */
1285 retval = krb5_fcc_store_ui_2(context, id, (krb5_int32)fcc_flen);
1286 if (retval) goto done;
1288 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
1289 /* Write time offset tag */
1290 fcc_tag = FCC_TAG_DELTATIME;
1291 fcc_taglen = 2*sizeof(krb5_int32);
1293 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_tag);
1294 if (retval) goto done;
1295 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_taglen);
1296 if (retval) goto done;
1297 retval = krb5_fcc_store_int32(context,id,os_ctx->time_offset);
1298 if (retval) goto done;
1299 retval = krb5_fcc_store_int32(context,id,os_ctx->usec_offset);
1300 if (retval) goto done;
1303 invalidate_cache(data);
1307 /* verify a valid version number is there */
1308 invalidate_cache(data);
1309 if (read(f, (char *)&fcc_fvno, sizeof(fcc_fvno)) != sizeof(fcc_fvno)) {
1310 retval = KRB5_CC_FORMAT;
1313 data->version = ntohs(fcc_fvno);
1314 if ((data->version != KRB5_FCC_FVNO_4) &&
1315 (data->version != KRB5_FCC_FVNO_3) &&
1316 (data->version != KRB5_FCC_FVNO_2) &&
1317 (data->version != KRB5_FCC_FVNO_1)) {
1318 retval = KRB5_CCACHE_BADVNO;
1324 if (data->version == KRB5_FCC_FVNO_4) {
1327 if (krb5_fcc_read_ui_2(context, id, &fcc_flen) ||
1328 (fcc_flen > sizeof(buf)))
1330 retval = KRB5_CC_FORMAT;
1335 if ((fcc_flen < (2 * sizeof(krb5_ui_2))) ||
1336 krb5_fcc_read_ui_2(context, id, &fcc_tag) ||
1337 krb5_fcc_read_ui_2(context, id, &fcc_taglen) ||
1338 (fcc_taglen > (fcc_flen - 2*sizeof(krb5_ui_2))))
1340 retval = KRB5_CC_FORMAT;
1345 case FCC_TAG_DELTATIME:
1346 if (fcc_taglen != 2*sizeof(krb5_int32)) {
1347 retval = KRB5_CC_FORMAT;
1350 if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
1351 (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID))
1353 if (krb5_fcc_read(context, id, buf, fcc_taglen)) {
1354 retval = KRB5_CC_FORMAT;
1359 if (krb5_fcc_read_int32(context, id, &os_ctx->time_offset) ||
1360 krb5_fcc_read_int32(context, id, &os_ctx->usec_offset))
1362 retval = KRB5_CC_FORMAT;
1366 ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
1367 KRB5_OS_TOFFSET_VALID);
1370 if (fcc_taglen && krb5_fcc_read(context,id,buf,fcc_taglen)) {
1371 retval = KRB5_CC_FORMAT;
1376 fcc_flen -= (2*sizeof(krb5_ui_2) + fcc_taglen);
1383 (void) krb5_unlock_file(context, f);
1389 static krb5_error_code
1390 krb5_fcc_skip_header(krb5_context context, krb5_ccache id)
1392 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1393 krb5_error_code kret;
1396 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1398 fcc_lseek(data, (off_t) sizeof(krb5_ui_2), SEEK_SET);
1399 if (data->version == KRB5_FCC_FVNO_4) {
1400 kret = krb5_fcc_read_ui_2(context, id, &fcc_flen);
1401 if (kret) return kret;
1402 if(fcc_lseek(data, (off_t) fcc_flen, SEEK_CUR) < 0)
1408 static krb5_error_code
1409 krb5_fcc_skip_principal(krb5_context context, krb5_ccache id)
1411 krb5_error_code kret;
1412 krb5_principal princ;
1414 k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
1416 kret = krb5_fcc_read_principal(context, id, &princ);
1417 if (kret != KRB5_OK)
1420 krb5_free_principal(context, princ);
1430 * Creates/refreshes the file cred cache id. If the cache exists, its
1431 * contents are destroyed.
1437 static krb5_error_code KRB5_CALLCONV
1438 krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
1440 krb5_error_code kret = 0;
1443 kret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
1447 MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE);
1449 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
1452 reti = fchmod(((krb5_fcc_data *) id->data)->file, S_IREAD | S_IWRITE);
1454 reti = chmod(((krb5_fcc_data *) id->data)->filename, S_IREAD | S_IWRITE);
1457 kret = krb5_fcc_interpret(context, errno);
1458 MAYBE_CLOSE(context, id, kret);
1459 k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
1464 kret = krb5_fcc_store_principal(context, id, princ);
1466 MAYBE_CLOSE(context, id, kret);
1467 k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
1468 krb5_change_cache ();
1473 * Drop the ref count; if it hits zero, remove the entry from the
1474 * fcc_set list and free it.
1476 static krb5_error_code dereference(krb5_context context, krb5_fcc_data *data)
1478 krb5_error_code kerr;
1479 struct fcc_set **fccsp;
1481 kerr = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
1484 for (fccsp = &fccs; *fccsp != NULL; fccsp = &(*fccsp)->next)
1485 if ((*fccsp)->data == data)
1487 assert(*fccsp != NULL);
1488 assert((*fccsp)->data == data);
1489 (*fccsp)->refcount--;
1490 if ((*fccsp)->refcount == 0) {
1491 struct fcc_set *temp;
1492 data = (*fccsp)->data;
1494 *fccsp = (*fccsp)->next;
1496 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1497 k5_cc_mutex_assert_unlocked(context, &data->lock);
1498 free(data->filename);
1499 zap(data->buf, sizeof(data->buf));
1500 if (data->file >= 0) {
1501 kerr = k5_cc_mutex_lock(context, &data->lock);
1504 krb5_fcc_close_file(context, data);
1505 k5_cc_mutex_unlock(context, &data->lock);
1507 k5_cc_mutex_destroy(&data->lock);
1510 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1519 * Closes the file cache, invalidates the id, and frees any resources
1520 * associated with the cache.
1522 static krb5_error_code KRB5_CALLCONV
1523 krb5_fcc_close(krb5_context context, krb5_ccache id)
1525 dereference(context, (krb5_fcc_data *) id->data);
1532 * Destroys the contents of id.
1537 static krb5_error_code KRB5_CALLCONV
1538 krb5_fcc_destroy(krb5_context context, krb5_ccache id)
1540 krb5_error_code kret = 0;
1541 krb5_fcc_data *data = (krb5_fcc_data *) id->data;
1545 unsigned long i, size;
1549 kret = k5_cc_mutex_lock(context, &data->lock);
1553 if (OPENCLOSE(id)) {
1554 invalidate_cache(data);
1555 ret = THREEPARAMOPEN(data->filename,
1556 O_RDWR | O_BINARY, 0);
1558 kret = krb5_fcc_interpret(context, errno);
1561 set_cloexec_fd(ret);
1565 fcc_lseek(data, (off_t) 0, SEEK_SET);
1567 #ifdef MSDOS_FILESYSTEM
1568 /* "disgusting bit of UNIX trivia" - that's how the writers of NFS describe
1569 ** the ability of UNIX to still write to a file which has been unlinked.
1570 ** Naturally, the PC can't do this. As a result, we have to delete the file
1571 ** after we wipe it clean but that throws off all the error handling code.
1572 ** So we have do the work ourselves.
1574 ret = fstat(data->file, &buf);
1576 kret = krb5_fcc_interpret(context, errno);
1577 size = 0; /* Nothing to wipe clean */
1579 size = (unsigned long) buf.st_size;
1581 memset(zeros, 0, BUFSIZ);
1583 wlen = (int) ((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */
1584 i = write(data->file, zeros, wlen);
1586 kret = krb5_fcc_interpret(context, errno);
1587 /* Don't jump to cleanup--we still want to delete the file. */
1590 size -= i; /* We've read this much */
1593 if (OPENCLOSE(id)) {
1594 (void) close(((krb5_fcc_data *)id->data)->file);
1598 ret = unlink(data->filename);
1600 kret = krb5_fcc_interpret(context, errno);
1604 #else /* MSDOS_FILESYSTEM */
1606 ret = unlink(data->filename);
1608 kret = krb5_fcc_interpret(context, errno);
1609 if (OPENCLOSE(id)) {
1610 (void) close(((krb5_fcc_data *)id->data)->file);
1617 ret = fstat(data->file, &buf);
1619 kret = krb5_fcc_interpret(context, errno);
1620 if (OPENCLOSE(id)) {
1621 (void) close(((krb5_fcc_data *)id->data)->file);
1627 /* XXX This may not be legal XXX */
1628 size = (unsigned long) buf.st_size;
1629 memset(zeros, 0, BUFSIZ);
1630 for (i=0; i < size / BUFSIZ; i++)
1631 if (write(data->file, zeros, BUFSIZ) < 0) {
1632 kret = krb5_fcc_interpret(context, errno);
1633 if (OPENCLOSE(id)) {
1634 (void) close(((krb5_fcc_data *)id->data)->file);
1640 wlen = (unsigned int) (size % BUFSIZ);
1641 if (write(data->file, zeros, wlen) < 0) {
1642 kret = krb5_fcc_interpret(context, errno);
1643 if (OPENCLOSE(id)) {
1644 (void) close(((krb5_fcc_data *)id->data)->file);
1650 ret = close(data->file);
1654 kret = krb5_fcc_interpret(context, errno);
1656 #endif /* MSDOS_FILESYSTEM */
1659 k5_cc_mutex_unlock(context, &data->lock);
1660 dereference(context, data);
1663 krb5_change_cache ();
1667 extern const krb5_cc_ops krb5_fcc_ops;
1671 * residual is a legal path name, and a null-terminated string
1677 * creates a file-based cred cache that will reside in the file
1678 * residual. The cache is not opened, but the filename is reserved.
1681 * A filled in krb5_ccache structure "id".
1684 * KRB5_CC_NOMEM - there was insufficient memory to allocate the
1685 * krb5_ccache. id is undefined.
1688 static krb5_error_code KRB5_CALLCONV
1689 krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
1692 krb5_error_code kret;
1693 krb5_fcc_data *data;
1694 struct fcc_set *setptr;
1696 kret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
1699 for (setptr = fccs; setptr; setptr = setptr->next) {
1700 if (!strcmp(setptr->data->filename, residual))
1704 data = setptr->data;
1705 assert(setptr->refcount != 0);
1707 assert(setptr->refcount != 0);
1708 kret = k5_cc_mutex_lock(context, &data->lock);
1710 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1713 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1715 data = malloc(sizeof(krb5_fcc_data));
1717 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1718 return KRB5_CC_NOMEM;
1720 data->filename = strdup(residual);
1721 if (data->filename == NULL) {
1722 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1724 return KRB5_CC_NOMEM;
1726 kret = k5_cc_mutex_init(&data->lock);
1728 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1729 free(data->filename);
1733 kret = k5_cc_mutex_lock(context, &data->lock);
1735 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1736 k5_cc_mutex_destroy(&data->lock);
1737 free(data->filename);
1741 /* data->version,mode filled in for real later */
1742 data->version = data->mode = 0;
1743 data->flags = KRB5_TC_OPENCLOSE;
1745 data->valid_bytes = 0;
1746 setptr = malloc(sizeof(struct fcc_set));
1747 if (setptr == NULL) {
1748 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1749 k5_cc_mutex_unlock(context, &data->lock);
1750 k5_cc_mutex_destroy(&data->lock);
1751 free(data->filename);
1753 return KRB5_CC_NOMEM;
1755 setptr->refcount = 1;
1756 setptr->data = data;
1757 setptr->next = fccs;
1759 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1762 k5_cc_mutex_assert_locked(context, &data->lock);
1763 k5_cc_mutex_unlock(context, &data->lock);
1764 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
1766 dereference(context, data);
1767 return KRB5_CC_NOMEM;
1770 lid->ops = &krb5_fcc_ops;
1772 lid->magic = KV5M_CCACHE;
1774 /* other routines will get errors on open, and callers must expect them,
1775 if cache is non-existent/unusable */
1782 * Prepares for a sequential search of the credentials cache.
1783 * Returns and krb5_cc_cursor to be used with krb5_fcc_next_cred and
1784 * krb5_fcc_end_seq_get.
1786 * If the cache is modified between the time of this call and the time
1787 * of the final krb5_fcc_end_seq_get, the results are undefined.
1793 static krb5_error_code KRB5_CALLCONV
1794 krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id,
1795 krb5_cc_cursor *cursor)
1797 krb5_fcc_cursor *fcursor;
1798 krb5_error_code kret = KRB5_OK;
1799 krb5_fcc_data *data = (krb5_fcc_data *)id->data;
1801 kret = k5_cc_mutex_lock(context, &data->lock);
1805 fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor));
1806 if (fcursor == NULL) {
1807 k5_cc_mutex_unlock(context, &data->lock);
1808 return KRB5_CC_NOMEM;
1810 if (OPENCLOSE(id)) {
1811 kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY);
1814 k5_cc_mutex_unlock(context, &data->lock);
1819 /* Make sure we start reading right after the primary principal */
1820 kret = krb5_fcc_skip_header(context, id);
1825 kret = krb5_fcc_skip_principal(context, id);
1831 fcursor->pos = fcc_lseek(data, (off_t) 0, SEEK_CUR);
1832 *cursor = (krb5_cc_cursor) fcursor;
1835 MAYBE_CLOSE(context, id, kret);
1836 k5_cc_mutex_unlock(context, &data->lock);
1843 * cursor is a krb5_cc_cursor originally obtained from
1844 * krb5_fcc_start_seq_get.
1850 * Fills in creds with the "next" credentals structure from the cache
1851 * id. The actual order the creds are returned in is arbitrary.
1852 * Space is allocated for the variable length fields in the
1853 * credentials structure, so the object returned must be passed to
1854 * krb5_destroy_credential.
1856 * The cursor is updated for the next call to krb5_fcc_next_cred.
1861 static krb5_error_code KRB5_CALLCONV
1862 krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
1865 #define TCHECK(ret) if (ret != KRB5_OK) goto lose;
1866 krb5_error_code kret;
1867 krb5_fcc_cursor *fcursor;
1870 krb5_fcc_data *d = (krb5_fcc_data *) id->data;
1872 kret = k5_cc_mutex_lock(context, &d->lock);
1876 memset(creds, 0, sizeof(*creds));
1877 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
1878 fcursor = (krb5_fcc_cursor *) *cursor;
1880 kret = (fcc_lseek(d, fcursor->pos, SEEK_SET) == (off_t) -1);
1882 kret = krb5_fcc_interpret(context, errno);
1883 MAYBE_CLOSE(context, id, kret);
1884 k5_cc_mutex_unlock(context, &d->lock);
1888 kret = krb5_fcc_read_principal(context, id, &creds->client);
1890 kret = krb5_fcc_read_principal(context, id, &creds->server);
1892 kret = krb5_fcc_read_keyblock(context, id, &creds->keyblock);
1894 kret = krb5_fcc_read_times(context, id, &creds->times);
1896 kret = krb5_fcc_read_octet(context, id, &octet);
1898 creds->is_skey = octet;
1899 kret = krb5_fcc_read_int32(context, id, &int32);
1901 creds->ticket_flags = int32;
1902 kret = krb5_fcc_read_addrs(context, id, &creds->addresses);
1904 kret = krb5_fcc_read_authdata(context, id, &creds->authdata);
1906 kret = krb5_fcc_read_data(context, id, &creds->ticket);
1908 kret = krb5_fcc_read_data(context, id, &creds->second_ticket);
1911 fcursor->pos = fcc_lseek(d, (off_t) 0, SEEK_CUR);
1914 MAYBE_CLOSE (context, id, kret);
1915 k5_cc_mutex_unlock(context, &d->lock);
1916 if (kret != KRB5_OK)
1917 krb5_free_cred_contents(context, creds);
1923 * cursor is a krb5_cc_cursor originally obtained from
1924 * krb5_fcc_start_seq_get.
1930 * Finishes sequential processing of the file credentials ccache id,
1931 * and invalidates the cursor (it must never be used after this call).
1934 static krb5_error_code KRB5_CALLCONV
1935 krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
1937 /* We don't do anything with the file cache itself, so
1938 no need to lock anything. */
1940 /* don't close; it may be left open by the caller,
1941 and if not, fcc_start_seq_get and/or fcc_next_cred will do the
1943 MAYBE_CLOSE(context, id, kret); */
1944 free((krb5_fcc_cursor *) *cursor);
1951 * Creates a new file cred cache whose name is guaranteed to be
1952 * unique. The name begins with the string TKT_ROOT (from fcc.h).
1953 * The cache is not opened, but the new filename is reserved.
1956 * The filled in krb5_ccache id.
1959 * KRB5_CC_NOMEM - there was insufficient memory to allocate the
1960 * krb5_ccache. id is undefined.
1961 * system errors (from open)
1963 static krb5_error_code KRB5_CALLCONV
1964 krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
1968 krb5_error_code kret = 0;
1969 char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for
1971 krb5_fcc_data *data;
1972 krb5_int16 fcc_fvno = htons(context->fcc_default_format);
1973 krb5_int16 fcc_flen = 0;
1975 struct fcc_set *setptr;
1977 /* Set master lock */
1978 kret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
1982 (void) snprintf(scratch, sizeof(scratch), "%sXXXXXX", TKT_ROOT);
1983 ret = mkstemp(scratch);
1985 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1986 return krb5_fcc_interpret(context, errno);
1988 set_cloexec_fd(ret);
1990 /* Allocate memory */
1991 data = (krb5_pointer) malloc(sizeof(krb5_fcc_data));
1993 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
1996 return KRB5_CC_NOMEM;
1999 data->filename = strdup(scratch);
2000 if (data->filename == NULL) {
2001 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2005 return KRB5_CC_NOMEM;
2008 kret = k5_cc_mutex_init(&data->lock);
2010 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2011 free(data->filename);
2017 kret = k5_cc_mutex_lock(context, &data->lock);
2019 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2020 k5_cc_mutex_destroy(&data->lock);
2021 free(data->filename);
2029 * The file is initially closed at the end of this call...
2033 data->valid_bytes = 0;
2034 /* data->version,mode filled in for real later */
2035 data->version = data->mode = 0;
2038 /* Ignore user's umask, set mode = 0600 */
2041 chmod(data->filename, S_IRUSR | S_IWUSR);
2044 fchmod(ret, S_IRUSR | S_IWUSR);
2046 if ((cnt = write(ret, (char *)&fcc_fvno, sizeof(fcc_fvno)))
2047 != sizeof(fcc_fvno)) {
2050 (void) unlink(data->filename);
2051 kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2054 /* For version 4 we save a length for the rest of the header */
2055 if (context->fcc_default_format == KRB5_FCC_FVNO_4) {
2056 if ((cnt = write(ret, (char *)&fcc_flen, sizeof(fcc_flen)))
2057 != sizeof(fcc_flen)) {
2060 (void) unlink(data->filename);
2061 kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2065 if (close(ret) == -1) {
2067 (void) unlink(data->filename);
2068 kret = krb5_fcc_interpret(context, errsave);
2073 setptr = malloc(sizeof(struct fcc_set));
2074 if (setptr == NULL) {
2075 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2076 k5_cc_mutex_unlock(context, &data->lock);
2077 k5_cc_mutex_destroy(&data->lock);
2078 free(data->filename);
2081 (void) unlink(scratch);
2082 return KRB5_CC_NOMEM;
2084 setptr->refcount = 1;
2085 setptr->data = data;
2086 setptr->next = fccs;
2088 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2090 k5_cc_mutex_assert_locked(context, &data->lock);
2091 k5_cc_mutex_unlock(context, &data->lock);
2092 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
2094 dereference(context, data);
2095 return KRB5_CC_NOMEM;
2098 lid->ops = &krb5_fcc_ops;
2100 lid->magic = KV5M_CCACHE;
2102 /* default to open/close on every trn - otherwise destroy
2103 will get as to state confused */
2104 ((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE;
2109 krb5_change_cache ();
2113 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2114 k5_cc_mutex_unlock(context, &data->lock);
2115 k5_cc_mutex_destroy(&data->lock);
2116 free(data->filename);
2123 * id is a file credential cache
2126 * The name of the file cred cache id.
2128 static const char * KRB5_CALLCONV
2129 krb5_fcc_get_name (krb5_context context, krb5_ccache id)
2131 return (char *) ((krb5_fcc_data *) id->data)->filename;
2139 * Retrieves the primary principal from id, as set with
2140 * krb5_fcc_initialize. The principal is returned is allocated
2141 * storage that must be freed by the caller via krb5_free_principal.
2147 static krb5_error_code KRB5_CALLCONV
2148 krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
2150 krb5_error_code kret = KRB5_OK;
2152 kret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2156 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2158 /* make sure we're beyond the header */
2159 kret = krb5_fcc_skip_header(context, id);
2160 if (kret) goto done;
2161 kret = krb5_fcc_read_principal(context, id, princ);
2164 MAYBE_CLOSE(context, id, kret);
2165 k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2170 static krb5_error_code KRB5_CALLCONV
2171 krb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
2173 return krb5_cc_retrieve_cred_default (context, id, whichfields,
2183 * stores creds in the file cred cache
2187 * storage failure errors
2189 static krb5_error_code KRB5_CALLCONV
2190 krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
2192 #define TCHECK(ret) if (ret != KRB5_OK) goto lose;
2193 krb5_error_code ret;
2195 ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2199 /* Make sure we are writing to the end of the file */
2200 MAYBE_OPEN(context, id, FCC_OPEN_RDWR);
2202 /* Make sure we are writing to the end of the file */
2203 ret = fcc_lseek((krb5_fcc_data *) id->data, (off_t) 0, SEEK_END);
2205 MAYBE_CLOSE_IGNORE(context, id);
2206 k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2207 return krb5_fcc_interpret(context, errno);
2210 ret = krb5_fcc_store_principal(context, id, creds->client);
2212 ret = krb5_fcc_store_principal(context, id, creds->server);
2214 ret = krb5_fcc_store_keyblock(context, id, &creds->keyblock);
2216 ret = krb5_fcc_store_times(context, id, &creds->times);
2218 ret = krb5_fcc_store_octet(context, id, (krb5_int32) creds->is_skey);
2220 ret = krb5_fcc_store_int32(context, id, creds->ticket_flags);
2222 ret = krb5_fcc_store_addrs(context, id, creds->addresses);
2224 ret = krb5_fcc_store_authdata(context, id, creds->authdata);
2226 ret = krb5_fcc_store_data(context, id, &creds->ticket);
2228 ret = krb5_fcc_store_data(context, id, &creds->second_ticket);
2232 MAYBE_CLOSE(context, id, ret);
2233 k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2234 krb5_change_cache ();
2240 * Non-functional stub implementation for krb5_fcc_remove
2243 * KRB5_CC_NOSUPP - not implemented
2245 static krb5_error_code KRB5_CALLCONV
2246 krb5_fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
2249 return KRB5_CC_NOSUPP;
2254 * id is a cred cache returned by krb5_fcc_resolve or
2255 * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2261 * Sets the operational flags of id to flags.
2263 static krb5_error_code KRB5_CALLCONV
2264 krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
2266 krb5_error_code ret = KRB5_OK;
2268 ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2272 /* XXX This should check for illegal combinations, if any.. */
2273 if (flags & KRB5_TC_OPENCLOSE) {
2274 /* asking to turn on OPENCLOSE mode */
2276 /* XXX Is this test necessary? */
2277 && ((krb5_fcc_data *) id->data)->file != NO_FILE)
2278 (void) krb5_fcc_close_file (context, ((krb5_fcc_data *) id->data));
2280 /* asking to turn off OPENCLOSE mode, meaning it must be
2281 left open. We open if it's not yet open */
2282 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2285 ((krb5_fcc_data *) id->data)->flags = flags;
2286 k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2292 * id is a cred cache returned by krb5_fcc_resolve or
2293 * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2296 * id (mutex only; temporary)
2299 * Returns the operational flags of id.
2301 static krb5_error_code KRB5_CALLCONV
2302 krb5_fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
2304 krb5_error_code ret = KRB5_OK;
2306 ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2309 *flags = ((krb5_fcc_data *) id->data)->flags;
2310 k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2314 static krb5_error_code KRB5_CALLCONV
2315 krb5_fcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor)
2317 krb5_error_code ret = 0;
2318 krb5_cc_ptcursor n = NULL;
2319 struct krb5_fcc_ptcursor_data *cdata = NULL;
2323 n = malloc(sizeof(*n));
2326 n->ops = &krb5_fcc_ops;
2327 cdata = malloc(sizeof(struct krb5_fcc_ptcursor_data));
2328 if (cdata == NULL) {
2333 ret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2337 ret = k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2343 krb5_fcc_ptcursor_free(context, &n);
2349 static krb5_error_code KRB5_CALLCONV
2350 krb5_fcc_ptcursor_next(krb5_context context,
2351 krb5_cc_ptcursor cursor,
2352 krb5_ccache *ccache)
2354 krb5_error_code ret = 0;
2355 struct krb5_fcc_ptcursor_data *cdata = NULL;
2359 n = malloc(sizeof(*n));
2363 cdata = cursor->data;
2365 ret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2369 if (cdata->cur == NULL) {
2370 k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2376 n->ops = &krb5_fcc_ops;
2377 n->data = cdata->cur->data;
2378 cdata->cur->refcount++;
2380 cdata->cur = cdata->cur->next;
2382 ret = k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2386 if (ret && n != NULL) {
2394 static krb5_error_code KRB5_CALLCONV
2395 krb5_fcc_ptcursor_free(krb5_context context,
2396 krb5_cc_ptcursor *cursor)
2398 if (*cursor == NULL)
2400 if ((*cursor)->data != NULL)
2401 free((*cursor)->data);
2412 * Returns the timestamp of id's file modification date.
2413 * If an error occurs, change_time is set to 0.
2415 static krb5_error_code KRB5_CALLCONV
2416 krb5_fcc_last_change_time(krb5_context context, krb5_ccache id,
2417 krb5_timestamp *change_time)
2419 krb5_error_code kret = KRB5_OK;
2420 krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2422 kret = krb5_fcc_data_last_change_time(context, data, change_time);
2427 static krb5_error_code KRB5_CALLCONV
2428 krb5_fcc_lock(krb5_context context, krb5_ccache id)
2430 krb5_error_code ret = 0;
2431 krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2432 ret = k5_cc_mutex_lock(context, &data->lock);
2436 static krb5_error_code KRB5_CALLCONV
2437 krb5_fcc_unlock(krb5_context context, krb5_ccache id)
2439 krb5_error_code ret = 0;
2440 krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2441 ret = k5_cc_mutex_unlock(context, &data->lock);
2445 static krb5_error_code
2446 krb5_fcc_data_last_change_time(krb5_context context, krb5_fcc_data *data,
2447 krb5_timestamp *change_time)
2449 krb5_error_code kret = KRB5_OK;
2456 kret = k5_cc_mutex_lock(context, &data->lock);
2461 ret = stat(data->filename, &buf);
2463 kret = krb5_fcc_interpret(context, errno);
2465 *change_time = (krb5_timestamp) buf.st_mtime;
2468 k5_cc_mutex_unlock(context, &data->lock);
2473 static krb5_error_code
2474 krb5_fcc_interpret(krb5_context context, int errnum)
2476 register krb5_error_code retval;
2479 retval = KRB5_FCC_NOFILE;
2484 case EISDIR: /* Mac doesn't have EISDIR */
2488 case ELOOP: /* Bad symlink is like no file. */
2495 retval = KRB5_FCC_PERM;
2498 case EEXIST: /* XXX */
2507 retval = KRB5_FCC_INTERNAL;
2518 retval = KRB5_CC_IO; /* XXX */
2519 krb5_set_error_message(context, retval,
2520 "Credentials cache I/O operation failed (%s)",
2526 const krb5_cc_ops krb5_fcc_ops = {
2531 krb5_fcc_generate_new,
2532 krb5_fcc_initialize,
2537 krb5_fcc_get_principal,
2538 krb5_fcc_start_seq_get,
2540 krb5_fcc_end_seq_get,
2541 krb5_fcc_remove_cred,
2544 krb5_fcc_ptcursor_new,
2545 krb5_fcc_ptcursor_next,
2546 krb5_fcc_ptcursor_free,
2548 krb5_fcc_last_change_time,
2549 NULL, /* wasdefault */
2556 * krb5_change_cache should be called after the cache changes.
2557 * A notification message is is posted out to all top level
2558 * windows so that they may recheck the cache based on the
2559 * changes made. We register a unique message type with which
2560 * we'll communicate to all other processes.
2564 krb5_change_cache (void) {
2566 PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0);
2571 unsigned int KRB5_CALLCONV
2572 krb5_get_notification_message (void) {
2573 static unsigned int message = 0;
2576 message = RegisterWindowMessage(WM_KERBEROS5_CHANGED);
2583 krb5_change_cache (void)
2588 krb5_get_notification_message (void)
2595 const krb5_cc_ops krb5_cc_file_ops = {
2600 krb5_fcc_generate_new,
2601 krb5_fcc_initialize,
2606 krb5_fcc_get_principal,
2607 krb5_fcc_start_seq_get,
2609 krb5_fcc_end_seq_get,
2610 krb5_fcc_remove_cred,
2613 krb5_fcc_ptcursor_new,
2614 krb5_fcc_ptcursor_next,
2615 krb5_fcc_ptcursor_free,
2617 krb5_fcc_last_change_time,
2618 NULL, /* wasdefault */