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 * A psuedo-BNF grammar for the FILE credential cache format is:
33 * version (2 bytes; 05 01 for version 1 through 05 04 for version 4)
34 * header [not present before version 4]
42 * header1tag (16 bits)
43 * header1len (16 bits)
44 * header1val (header1len bytes)
46 * See ccmarshal.c for the principal and credential formats. Although versions
47 * 1 and 2 of the FILE format use native byte order for integer representations
48 * within principals and credentials, the integer fields in the grammar above
49 * are always in big-endian byte order.
51 * Only one header tag is currently defined. The tag value is 1
52 * (FCC_TAG_DELTATIME), and its contents are two 32-bit integers giving the
53 * seconds and microseconds of the time offset of the KDC relative to the
56 * Each of the file ccache functions opens and closes the file whenever it
59 * This module depends on UNIX-like file descriptors, and UNIX-like behavior
60 * from the functions: open, close, read, write, lseek.
77 extern const krb5_cc_ops krb5_cc_file_ops;
79 krb5_error_code krb5_change_cache(void);
81 static krb5_error_code interpret_errno(krb5_context, int);
83 /* The cache format version is a positive integer, represented in the cache
84 * file as a two-byte big endian number with 0x0500 added to it. */
85 #define FVNO_BASE 0x0500
87 #define FCC_TAG_DELTATIME 1
90 #ifdef MSDOS_FILESYSTEM
91 #define TKT_ROOT "\\tkt"
93 #define TKT_ROOT "/tmp/tkt"
97 typedef struct fcc_data_st {
102 /* Iterator over file caches. */
103 struct krb5_fcc_ptcursor_data {
107 /* Iterator over a cache. */
108 typedef struct _krb5_fcc_cursor {
113 k5_cc_mutex krb5int_cc_file_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
115 /* Add fname to the standard error message for ret. */
116 static krb5_error_code
117 set_errmsg_filename(krb5_context context, krb5_error_code ret,
122 k5_setmsg(context, ret, "%s (filename: %s)", error_message(ret), fname);
126 /* Get the size of the cache file as a size_t, or SIZE_MAX if it is too
127 * large to be represented as a size_t. */
128 static krb5_error_code
129 get_size(krb5_context context, FILE *fp, size_t *size_out)
134 if (fstat(fileno(fp), &sb) == -1)
135 return interpret_errno(context, errno);
136 if (sizeof(off_t) > sizeof(size_t) && sb.st_size > (off_t)SIZE_MAX)
137 *size_out = SIZE_MAX;
139 *size_out = sb.st_size;
143 /* Read len bytes from fp, storing them in buf. Return KRB5_CC_END
144 * if not enough bytes are present. */
145 static krb5_error_code
146 read_bytes(krb5_context context, FILE *fp, void *buf, size_t len)
150 nread = fread(buf, 1, len, fp);
152 return ferror(fp) ? errno : KRB5_CC_END;
156 /* Load four bytes from the cache file. Add them to buf (if set) and return
157 * their value as a 32-bit unsigned integer according to the file format. */
158 static krb5_error_code
159 read32(krb5_context context, FILE *fp, int version, struct k5buf *buf,
165 ret = read_bytes(context, fp, bytes, 4);
169 k5_buf_add_len(buf, bytes, 4);
170 *out = (version < 3) ? load_32_n(bytes) : load_32_be(bytes);
174 /* Load two bytes from the cache file and return their value as a 16-bit
175 * unsigned integer according to the file format. */
176 static krb5_error_code
177 read16(krb5_context context, FILE *fp, int version, uint16_t *out)
182 ret = read_bytes(context, fp, bytes, 2);
185 *out = (version < 3) ? load_16_n(bytes) : load_16_be(bytes);
189 /* Read len bytes from the cache file and add them to buf. */
190 static krb5_error_code
191 load_bytes(krb5_context context, FILE *fp, size_t len, struct k5buf *buf)
195 ptr = k5_buf_get_space(buf, len);
196 return (ptr == NULL) ? KRB5_CC_NOMEM : read_bytes(context, fp, ptr, len);
199 /* Load a 32-bit length and data from the cache file into buf, but not more
200 * than maxsize bytes. */
201 static krb5_error_code
202 load_data(krb5_context context, FILE *fp, int version, size_t maxsize,
208 ret = read32(context, fp, version, buf, &count);
212 return KRB5_CC_FORMAT;
213 return load_bytes(context, fp, count, buf);
216 /* Load a marshalled principal from the cache file into buf, without
217 * unmarshalling it. */
218 static krb5_error_code
219 load_principal(krb5_context context, FILE *fp, int version, size_t maxsize,
226 ret = load_bytes(context, fp, 4, buf);
230 ret = read32(context, fp, version, buf, &count);
233 /* Add one for the realm (except in version 1 which already counts it). */
236 while (count-- > 0) {
237 ret = load_data(context, fp, version, maxsize, buf);
244 /* Load a marshalled credential from the cache file into buf, without
245 * unmarshalling it. */
246 static krb5_error_code
247 load_cred(krb5_context context, FILE *fp, int version, size_t maxsize,
253 /* client and server */
254 ret = load_principal(context, fp, version, maxsize, buf);
257 ret = load_principal(context, fp, version, maxsize, buf);
261 /* keyblock (enctype, enctype again for version 3, length, value) */
262 ret = load_bytes(context, fp, (version == 3) ? 4 : 2, buf);
265 ret = load_data(context, fp, version, maxsize, buf);
269 /* times (4*4 bytes), is_skey (1 byte), ticket flags (4 bytes) */
270 ret = load_bytes(context, fp, 4 * 4 + 1 + 4, buf);
274 /* addresses and authdata, both lists of {type, length, data} */
275 for (i = 0; i < 2; i++) {
276 ret = read32(context, fp, version, buf, &count);
279 while (count-- > 0) {
280 ret = load_bytes(context, fp, 2, buf);
283 ret = load_data(context, fp, version, maxsize, buf);
289 /* ticket and second_ticket */
290 ret = load_data(context, fp, version, maxsize, buf);
293 return load_data(context, fp, version, maxsize, buf);
296 static krb5_error_code
297 read_principal(krb5_context context, FILE *fp, int version,
298 krb5_principal *princ)
305 k5_buf_init_dynamic(&buf);
307 /* Read the principal representation into memory. */
308 ret = get_size(context, fp, &maxsize);
311 ret = load_principal(context, fp, version, maxsize, &buf);
314 ret = k5_buf_status(&buf);
318 /* Unmarshal it from buf into princ. */
319 ret = k5_unmarshal_princ(buf.data, buf.len, version, princ);
327 * Open and lock an existing cache file. If writable is true, open it for
328 * writing (with O_APPEND) and get an exclusive lock; otherwise open it for
329 * reading and get a shared lock.
331 static krb5_error_code
332 open_cache_file(krb5_context context, const char *filename,
333 krb5_boolean writable, FILE **fp_out)
336 int fd, flags, lockmode;
341 flags = writable ? (O_RDWR | O_APPEND) : O_RDONLY;
342 fd = open(filename, flags | O_BINARY | O_CLOEXEC, 0600);
344 return interpret_errno(context, errno);
347 lockmode = writable ? KRB5_LOCKMODE_EXCLUSIVE : KRB5_LOCKMODE_SHARED;
348 ret = krb5_lock_file(context, fd, lockmode);
354 fp = fdopen(fd, writable ? "r+b" : "rb");
356 (void)krb5_unlock_file(context, fd);
358 return KRB5_CC_NOMEM;
365 /* Unlock and close the cache file. Do nothing if fp is NULL. */
366 static krb5_error_code
367 close_cache_file(krb5_context context, FILE *fp)
374 ret = krb5_unlock_file(context, fileno(fp));
378 return st ? interpret_errno(context, errno) : 0;
381 /* Read the cache file header. Set time offsets in context from the header if
382 * appropriate. Set *version_out to the cache file format version. */
383 static krb5_error_code
384 read_header(krb5_context context, FILE *fp, int *version_out)
387 krb5_os_context os_ctx = &context->os_context;
388 uint16_t fields_len, tag, flen;
389 uint32_t time_offset, usec_offset;
395 /* Get the file format version. */
396 ret = read_bytes(context, fp, i16buf, 2);
398 return KRB5_CC_FORMAT;
399 version = load_16_be(i16buf) - FVNO_BASE;
400 if (version < 1 || version > 4)
401 return KRB5_CCACHE_BADVNO;
402 *version_out = version;
404 /* Tagged header fields begin with version 4. */
408 if (read16(context, fp, version, &fields_len))
409 return KRB5_CC_FORMAT;
411 if (fields_len < 4 || read16(context, fp, version, &tag) ||
412 read16(context, fp, version, &flen) || flen > fields_len - 4)
413 return KRB5_CC_FORMAT;
416 case FCC_TAG_DELTATIME:
418 read32(context, fp, version, NULL, &time_offset) ||
419 read32(context, fp, version, NULL, &usec_offset))
420 return KRB5_CC_FORMAT;
422 if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
423 (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID))
426 os_ctx->time_offset = time_offset;
427 os_ctx->usec_offset = usec_offset;
428 os_ctx->os_flags = ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
429 KRB5_OS_TOFFSET_VALID);
433 if (flen && fseek(fp, flen, SEEK_CUR) != 0)
434 return KRB5_CC_FORMAT;
437 fields_len -= (4 + flen);
442 /* Create or overwrite the cache file with a header and default principal. */
443 static krb5_error_code KRB5_CALLCONV
444 fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
447 krb5_os_context os_ctx = &context->os_context;
448 fcc_data *data = id->data;
449 char i16buf[2], i32buf[4];
452 int st, flags, version, fd = -1;
453 struct k5buf buf = EMPTY_K5BUF;
454 krb5_boolean file_locked = FALSE;
456 k5_cc_mutex_lock(context, &data->lock);
458 unlink(data->filename);
459 flags = O_CREAT | O_EXCL | O_RDWR | O_BINARY | O_CLOEXEC;
460 fd = open(data->filename, flags, 0600);
462 ret = interpret_errno(context, errno);
467 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
469 st = fchmod(fd, S_IRUSR | S_IWUSR);
471 st = chmod(data->filename, S_IRUSR | S_IWUSR);
474 ret = interpret_errno(context, errno);
479 ret = krb5_lock_file(context, fd, KRB5_LOCKMODE_EXCLUSIVE);
484 /* Prepare the header and principal in buf. */
485 k5_buf_init_dynamic(&buf);
486 version = context->fcc_default_format - FVNO_BASE;
487 store_16_be(FVNO_BASE + version, i16buf);
488 k5_buf_add_len(&buf, i16buf, 2);
490 /* Add tagged header fields. */
492 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
494 store_16_be(fields_len, i16buf);
495 k5_buf_add_len(&buf, i16buf, 2);
496 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
497 /* Add time offset tag. */
498 store_16_be(FCC_TAG_DELTATIME, i16buf);
499 k5_buf_add_len(&buf, i16buf, 2);
500 store_16_be(8, i16buf);
501 k5_buf_add_len(&buf, i16buf, 2);
502 store_32_be(os_ctx->time_offset, i32buf);
503 k5_buf_add_len(&buf, i32buf, 4);
504 store_32_be(os_ctx->usec_offset, i32buf);
505 k5_buf_add_len(&buf, i32buf, 4);
508 k5_marshal_princ(&buf, version, princ);
509 ret = k5_buf_status(&buf);
513 /* Write the header and principal. */
514 nwritten = write(fd, buf.data, buf.len);
516 ret = interpret_errno(context, errno);
517 if ((size_t)nwritten != buf.len)
523 krb5_unlock_file(context, fd);
526 k5_cc_mutex_unlock(context, &data->lock);
528 return set_errmsg_filename(context, ret, data->filename);
531 /* Release an fcc_data object. */
533 free_fccdata(krb5_context context, fcc_data *data)
535 k5_cc_mutex_assert_unlocked(context, &data->lock);
536 free(data->filename);
537 k5_cc_mutex_destroy(&data->lock);
541 /* Release the ccache handle. */
542 static krb5_error_code KRB5_CALLCONV
543 fcc_close(krb5_context context, krb5_ccache id)
545 free_fccdata(context, id->data);
550 /* Destroy the cache file and release the handle. */
551 static krb5_error_code KRB5_CALLCONV
552 fcc_destroy(krb5_context context, krb5_ccache id)
554 krb5_error_code ret = 0;
555 fcc_data *data = id->data;
558 unsigned long i, size;
562 k5_cc_mutex_lock(context, &data->lock);
564 fd = open(data->filename, O_RDWR | O_BINARY | O_CLOEXEC, 0);
566 ret = interpret_errno(context, errno);
571 #ifdef MSDOS_FILESYSTEM
573 * "Disgusting bit of UNIX trivia" - that's how the writers of NFS describe
574 * the ability of UNIX to still write to a file which has been unlinked.
575 * Naturally, the PC can't do this. As a result, we have to delete the
576 * file after we wipe it clean, but that throws off all the error handling
577 * code. So we have do the work ourselves.
579 st = fstat(fd, &buf);
581 ret = interpret_errno(context, errno);
582 size = 0; /* Nothing to wipe clean */
584 size = (unsigned long)buf.st_size;
587 memset(zeros, 0, BUFSIZ);
589 wlen = (int)((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */
590 i = write(fd, zeros, wlen);
592 ret = interpret_errno(context, errno);
593 /* Don't jump to cleanup--we still want to delete the file. */
601 st = unlink(data->filename);
603 ret = interpret_errno(context, errno);
607 #else /* MSDOS_FILESYSTEM */
609 st = unlink(data->filename);
611 ret = interpret_errno(context, errno);
616 st = fstat(fd, &buf);
618 ret = interpret_errno(context, errno);
623 /* XXX This may not be legal XXX */
624 size = (unsigned long)buf.st_size;
625 memset(zeros, 0, BUFSIZ);
626 for (i = 0; i < size / BUFSIZ; i++) {
627 if (write(fd, zeros, BUFSIZ) < 0) {
628 ret = interpret_errno(context, errno);
634 wlen = size % BUFSIZ;
635 if (write(fd, zeros, wlen) < 0) {
636 ret = interpret_errno(context, errno);
644 ret = interpret_errno(context, errno);
646 #endif /* MSDOS_FILESYSTEM */
649 (void)set_errmsg_filename(context, ret, data->filename);
650 k5_cc_mutex_unlock(context, &data->lock);
651 free_fccdata(context, data);
658 extern const krb5_cc_ops krb5_fcc_ops;
660 /* Create a file ccache handle for the pathname given by residual. */
661 static krb5_error_code KRB5_CALLCONV
662 fcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)
668 data = malloc(sizeof(fcc_data));
670 return KRB5_CC_NOMEM;
671 data->filename = strdup(residual);
672 if (data->filename == NULL) {
674 return KRB5_CC_NOMEM;
676 ret = k5_cc_mutex_init(&data->lock);
678 free(data->filename);
683 lid = malloc(sizeof(struct _krb5_ccache));
685 free_fccdata(context, data);
686 return KRB5_CC_NOMEM;
689 lid->ops = &krb5_fcc_ops;
691 lid->magic = KV5M_CCACHE;
693 /* Other routines will get errors on open, and callers must expect them, if
694 * cache is non-existent/unusable. */
699 /* Prepare for a sequential iteration over the cache file. */
700 static krb5_error_code KRB5_CALLCONV
701 fcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
703 krb5_fcc_cursor *fcursor = NULL;
705 krb5_principal princ = NULL;
706 fcc_data *data = id->data;
710 k5_cc_mutex_lock(context, &data->lock);
712 fcursor = malloc(sizeof(krb5_fcc_cursor));
713 if (fcursor == NULL) {
718 /* Open the cache file and read the header. */
719 ret = open_cache_file(context, data->filename, FALSE, &fp);
722 ret = read_header(context, fp, &version);
726 /* Read past the default client principal name. */
727 ret = read_principal(context, fp, version, &princ);
731 /* Drop the shared file lock but retain the file handle. */
732 (void)krb5_unlock_file(context, fileno(fp));
735 fcursor->version = version;
736 *cursor = (krb5_cc_cursor)fcursor;
740 (void)close_cache_file(context, fp);
742 krb5_free_principal(context, princ);
743 k5_cc_mutex_unlock(context, &data->lock);
744 return set_errmsg_filename(context, ret, data->filename);
747 /* Get the next credential from the cache file. */
748 static krb5_error_code KRB5_CALLCONV
749 fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
753 krb5_fcc_cursor *fcursor = *cursor;
754 fcc_data *data = id->data;
757 krb5_boolean file_locked = FALSE;
759 memset(creds, 0, sizeof(*creds));
760 k5_cc_mutex_lock(context, &data->lock);
761 k5_buf_init_dynamic(&buf);
763 ret = krb5_lock_file(context, fileno(fcursor->fp), KRB5_LOCKMODE_SHARED);
768 /* Load a marshalled cred into memory. */
769 ret = get_size(context, fcursor->fp, &maxsize);
772 ret = load_cred(context, fcursor->fp, fcursor->version, maxsize, &buf);
775 ret = k5_buf_status(&buf);
779 /* Unmarshal it from buf into creds. */
780 ret = k5_unmarshal_cred(buf.data, buf.len, fcursor->version, creds);
784 (void)krb5_unlock_file(context, fileno(fcursor->fp));
785 k5_cc_mutex_unlock(context, &data->lock);
787 return set_errmsg_filename(context, ret, data->filename);
790 /* Release an iteration cursor. */
791 static krb5_error_code KRB5_CALLCONV
792 fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
794 krb5_fcc_cursor *fcursor = *cursor;
796 (void)fclose(fcursor->fp);
802 /* Generate a unique file ccache using the given template (which will be
803 * modified to contain the actual name of the file). */
805 krb5int_fcc_new_unique(krb5_context context, char *template, krb5_ccache *id)
812 int16_t fcc_flen = 0;
815 fd = mkstemp(template);
817 return interpret_errno(context, errno);
820 /* Allocate memory */
821 data = malloc(sizeof(fcc_data));
825 return KRB5_CC_NOMEM;
828 data->filename = strdup(template);
829 if (data->filename == NULL) {
833 return KRB5_CC_NOMEM;
836 ret = k5_cc_mutex_init(&data->lock);
838 free(data->filename);
844 k5_cc_mutex_lock(context, &data->lock);
846 /* Ignore user's umask, set mode = 0600 */
849 chmod(data->filename, S_IRUSR | S_IWUSR);
852 fchmod(fd, S_IRUSR | S_IWUSR);
854 store_16_be(context->fcc_default_format, fcc_fvno);
855 cnt = write(fd, &fcc_fvno, 2);
859 (void)unlink(data->filename);
860 ret = (cnt == -1) ? interpret_errno(context, errsave) : KRB5_CC_IO;
863 /* For version 4 we save a length for the rest of the header */
864 if (context->fcc_default_format == FVNO_BASE + 4) {
865 cnt = write(fd, &fcc_flen, sizeof(fcc_flen));
866 if (cnt != sizeof(fcc_flen)) {
869 (void)unlink(data->filename);
870 ret = (cnt == -1) ? interpret_errno(context, errsave) : KRB5_CC_IO;
874 if (close(fd) == -1) {
876 (void)unlink(data->filename);
877 ret = interpret_errno(context, errsave);
881 k5_cc_mutex_assert_locked(context, &data->lock);
882 k5_cc_mutex_unlock(context, &data->lock);
883 lid = malloc(sizeof(*lid));
885 free_fccdata(context, data);
886 return KRB5_CC_NOMEM;
889 lid->ops = &krb5_fcc_ops;
891 lid->magic = KV5M_CCACHE;
899 (void)set_errmsg_filename(context, ret, data->filename);
900 k5_cc_mutex_unlock(context, &data->lock);
901 k5_cc_mutex_destroy(&data->lock);
902 free(data->filename);
908 * Create a new file cred cache whose name is guaranteed to be unique. The
909 * name begins with the string TKT_ROOT (from fcc.h). The cache file is not
910 * opened, but the new filename is reserved.
912 static krb5_error_code KRB5_CALLCONV
913 fcc_generate_new(krb5_context context, krb5_ccache *id)
915 char scratch[sizeof(TKT_ROOT) + 7]; /* Room for XXXXXX and terminator */
917 (void)snprintf(scratch, sizeof(scratch), "%sXXXXXX", TKT_ROOT);
918 return krb5int_fcc_new_unique(context, scratch, id);
921 /* Return an alias to the pathname of the cache file. */
922 static const char * KRB5_CALLCONV
923 fcc_get_name(krb5_context context, krb5_ccache id)
925 return ((fcc_data *)id->data)->filename;
928 /* Retrieve a copy of the default principal, if the cache is initialized. */
929 static krb5_error_code KRB5_CALLCONV
930 fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
933 fcc_data *data = id->data;
937 k5_cc_mutex_lock(context, &data->lock);
938 ret = open_cache_file(context, data->filename, FALSE, &fp);
941 ret = read_header(context, fp, &version);
944 ret = read_principal(context, fp, version, princ);
947 (void)close_cache_file(context, fp);
948 k5_cc_mutex_unlock(context, &data->lock);
949 return set_errmsg_filename(context, ret, data->filename);
952 /* Search for a credential within the cache file. */
953 static krb5_error_code KRB5_CALLCONV
954 fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields,
955 krb5_creds *mcreds, krb5_creds *creds)
959 ret = k5_cc_retrieve_cred_default(context, id, whichfields, mcreds, creds);
960 return set_errmsg_filename(context, ret, ((fcc_data *)id->data)->filename);
963 /* Store a credential in the cache file. */
964 static krb5_error_code KRB5_CALLCONV
965 fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
967 krb5_error_code ret, ret2;
968 fcc_data *data = id->data;
971 struct k5buf buf = EMPTY_K5BUF;
974 k5_cc_mutex_lock(context, &data->lock);
976 /* Open the cache file for O_APPEND writing. */
977 ret = open_cache_file(context, data->filename, TRUE, &fp);
980 ret = read_header(context, fp, &version);
984 /* Marshal the cred and write it to the file with a single append write. */
985 k5_buf_init_dynamic(&buf);
986 k5_marshal_cred(&buf, version, creds);
987 ret = k5_buf_status(&buf);
990 nwritten = write(fileno(fp), buf.data, buf.len);
992 ret = interpret_errno(context, errno);
993 if ((size_t)nwritten != buf.len)
1000 ret2 = close_cache_file(context, fp);
1001 k5_cc_mutex_unlock(context, &data->lock);
1002 return set_errmsg_filename(context, ret ? ret : ret2, data->filename);
1005 /* Non-functional stub for removing a cred from the cache file. */
1006 static krb5_error_code KRB5_CALLCONV
1007 fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
1010 return KRB5_CC_NOSUPP;
1013 static krb5_error_code KRB5_CALLCONV
1014 fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
1019 static krb5_error_code KRB5_CALLCONV
1020 fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
1026 /* Prepare to iterate over the caches in the per-type collection. */
1027 static krb5_error_code KRB5_CALLCONV
1028 fcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor)
1030 krb5_cc_ptcursor n = NULL;
1031 struct krb5_fcc_ptcursor_data *cdata = NULL;
1035 n = malloc(sizeof(*n));
1038 n->ops = &krb5_fcc_ops;
1039 cdata = malloc(sizeof(*cdata));
1040 if (cdata == NULL) {
1044 cdata->first = TRUE;
1050 /* Get the next cache in the per-type collection. The FILE per-type collection
1051 * contains only the context's default cache if it is a file cache. */
1052 static krb5_error_code KRB5_CALLCONV
1053 fcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
1054 krb5_ccache *cache_out)
1056 krb5_error_code ret;
1057 struct krb5_fcc_ptcursor_data *cdata = cursor->data;
1058 const char *defname, *residual;
1065 cdata->first = FALSE;
1067 defname = krb5_cc_default_name(context);
1071 /* Check if the default has type FILE or no type; find the residual. */
1072 if (strncmp(defname, "FILE:", 5) == 0)
1073 residual = defname + 5;
1074 else if (strchr(defname + 2, ':') == NULL) /* Skip drive prefix if any. */
1079 /* Don't yield a nonexistent default file cache. */
1080 if (stat(residual, &sb) != 0)
1083 ret = krb5_cc_resolve(context, defname, &cache);
1085 return set_errmsg_filename(context, ret, defname);
1090 /* Release a per-type collection iteration cursor. */
1091 static krb5_error_code KRB5_CALLCONV
1092 fcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
1094 if (*cursor == NULL)
1096 free((*cursor)->data);
1102 /* Get the cache file's last modification time. */
1103 static krb5_error_code KRB5_CALLCONV
1104 fcc_last_change_time(krb5_context context, krb5_ccache id,
1105 krb5_timestamp *change_time)
1107 krb5_error_code ret = 0;
1108 fcc_data *data = id->data;
1113 k5_cc_mutex_lock(context, &data->lock);
1115 if (stat(data->filename, &buf) == -1)
1116 ret = interpret_errno(context, errno);
1118 *change_time = (krb5_timestamp)buf.st_mtime;
1120 k5_cc_mutex_unlock(context, &data->lock);
1122 return set_errmsg_filename(context, ret, data->filename);
1125 /* Lock the cache handle against other threads. (This does not lock the cache
1126 * file against other processes.) */
1127 static krb5_error_code KRB5_CALLCONV
1128 fcc_lock(krb5_context context, krb5_ccache id)
1130 fcc_data *data = id->data;
1131 k5_cc_mutex_lock(context, &data->lock);
1135 /* Unlock the cache handle. */
1136 static krb5_error_code KRB5_CALLCONV
1137 fcc_unlock(krb5_context context, krb5_ccache id)
1139 fcc_data *data = id->data;
1140 k5_cc_mutex_unlock(context, &data->lock);
1144 /* Translate a system errno value to a Kerberos com_err code. */
1145 static krb5_error_code
1146 interpret_errno(krb5_context context, int errnum)
1148 krb5_error_code ret;
1159 ret = KRB5_FCC_NOFILE;
1164 case EISDIR: /* Mac doesn't have EISDIR */
1167 ret = KRB5_FCC_PERM;
1176 ret = KRB5_FCC_INTERNAL;
1179 * The rest all map to KRB5_CC_IO. These errnos are listed to
1180 * document that they've been considered explicitly:
1198 const krb5_cc_ops krb5_fcc_ops = {
1220 fcc_last_change_time,
1221 NULL, /* wasdefault */
1224 NULL, /* switch_to */
1229 * krb5_change_cache should be called after the cache changes.
1230 * A notification message is is posted out to all top level
1231 * windows so that they may recheck the cache based on the
1232 * changes made. We register a unique message type with which
1233 * we'll communicate to all other processes.
1237 krb5_change_cache(void)
1239 PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0);
1243 unsigned int KRB5_CALLCONV
1244 krb5_get_notification_message(void)
1246 static unsigned int message = 0;
1249 message = RegisterWindowMessage(WM_KERBEROS5_CHANGED);
1256 krb5_change_cache(void)
1262 krb5_get_notification_message(void)
1269 const krb5_cc_ops krb5_cc_file_ops = {
1291 fcc_last_change_time,
1292 NULL, /* wasdefault */
1295 NULL, /* switch_to */