1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_file.c - File-based credential cache */
3 /*
4 * Copyright 1990,1991,1992,1993,1994,2000,2004,2007 Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Original stdio support copyright 1995 by Cygnus Support.
8 *
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.
13 *
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.
27 */
28
29 /*
30 * A psuedo-BNF grammar for the FILE credential cache format is:
31 *
32 * file ::=
33 * version (2 bytes; 05 01 for version 1 through 05 04 for version 4)
34 * header [not present before version 4]
35 * principal
36 * credential1
37 * credential2
38 * ...
39 *
40 * header ::=
41 * headerlen (16 bits)
42 * header1tag (16 bits)
43 * header1len (16 bits)
44 * header1val (header1len bytes)
45 *
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.
50 *
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
54 * client.
55 *
56 * Each of the file ccache functions opens and closes the file whenever it
57 * needs to access it.
58 *
59 * This module depends on UNIX-like file descriptors, and UNIX-like behavior
60 * from the functions: open, close, read, write, lseek.
61 */
62
63 #include "k5-int.h"
64 #include "cc-int.h"
65
66 #include <stdio.h>
67 #include <errno.h>
68
69 #if HAVE_UNISTD_H
70 #include <unistd.h>
71 #endif
72
73 #ifndef O_CLOEXEC
74 #define O_CLOEXEC 0
75 #endif
76
77 extern const krb5_cc_ops krb5_cc_file_ops;
78
79 krb5_error_code krb5_change_cache(void);
80
81 static krb5_error_code interpret_errno(krb5_context, int);
82
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
86
87 #define FCC_TAG_DELTATIME 1
88
89 #ifndef TKT_ROOT
90 #ifdef MSDOS_FILESYSTEM
91 #define TKT_ROOT "\\tkt"
92 #else
93 #define TKT_ROOT "/tmp/tkt"
94 #endif
95 #endif
96
97 typedef struct fcc_data_st {
98 k5_cc_mutex lock;
99 char *filename;
100 } fcc_data;
101
102 /* Iterator over file caches. */
103 struct krb5_fcc_ptcursor_data {
104 krb5_boolean first;
105 };
106
107 /* Iterator over a cache. */
108 typedef struct _krb5_fcc_cursor {
109 FILE *fp;
110 int version;
111 } krb5_fcc_cursor;
112
113 k5_cc_mutex krb5int_cc_file_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
114
115 /* Add fname to the standard error message for ret. */
116 static krb5_error_code
set_errmsg_filename(krb5_context context,krb5_error_code ret,const char * fname)117 set_errmsg_filename(krb5_context context, krb5_error_code ret,
118 const char *fname)
119 {
120 if (!ret)
121 return 0;
122 k5_setmsg(context, ret, "%s (filename: %s)", error_message(ret), fname);
123 return ret;
124 }
125
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
get_size(krb5_context context,FILE * fp,size_t * size_out)129 get_size(krb5_context context, FILE *fp, size_t *size_out)
130 {
131 struct stat sb;
132
133 *size_out = 0;
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;
138 else
139 *size_out = sb.st_size;
140 return 0;
141 }
142
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
read_bytes(krb5_context context,FILE * fp,void * buf,size_t len)146 read_bytes(krb5_context context, FILE *fp, void *buf, size_t len)
147 {
148 size_t nread;
149
150 nread = fread(buf, 1, len, fp);
151 if (nread < len)
152 return ferror(fp) ? errno : KRB5_CC_END;
153 return 0;
154 }
155
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
read32(krb5_context context,FILE * fp,int version,struct k5buf * buf,uint32_t * out)159 read32(krb5_context context, FILE *fp, int version, struct k5buf *buf,
160 uint32_t *out)
161 {
162 krb5_error_code ret;
163 char bytes[4];
164
165 ret = read_bytes(context, fp, bytes, 4);
166 if (ret)
167 return ret;
168 if (buf != NULL)
169 k5_buf_add_len(buf, bytes, 4);
170 *out = (version < 3) ? load_32_n(bytes) : load_32_be(bytes);
171 return 0;
172 }
173
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
read16(krb5_context context,FILE * fp,int version,uint16_t * out)177 read16(krb5_context context, FILE *fp, int version, uint16_t *out)
178 {
179 krb5_error_code ret;
180 char bytes[2];
181
182 ret = read_bytes(context, fp, bytes, 2);
183 if (ret)
184 return ret;
185 *out = (version < 3) ? load_16_n(bytes) : load_16_be(bytes);
186 return 0;
187 }
188
189 /* Read len bytes from the cache file and add them to buf. */
190 static krb5_error_code
load_bytes(krb5_context context,FILE * fp,size_t len,struct k5buf * buf)191 load_bytes(krb5_context context, FILE *fp, size_t len, struct k5buf *buf)
192 {
193 void *ptr;
194
195 ptr = k5_buf_get_space(buf, len);
196 return (ptr == NULL) ? KRB5_CC_NOMEM : read_bytes(context, fp, ptr, len);
197 }
198
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
load_data(krb5_context context,FILE * fp,int version,size_t maxsize,struct k5buf * buf)202 load_data(krb5_context context, FILE *fp, int version, size_t maxsize,
203 struct k5buf *buf)
204 {
205 krb5_error_code ret;
206 uint32_t count;
207
208 ret = read32(context, fp, version, buf, &count);
209 if (ret)
210 return ret;
211 if (count > maxsize)
212 return KRB5_CC_FORMAT;
213 return load_bytes(context, fp, count, buf);
214 }
215
216 /* Load a marshalled principal from the cache file into buf, without
217 * unmarshalling it. */
218 static krb5_error_code
load_principal(krb5_context context,FILE * fp,int version,size_t maxsize,struct k5buf * buf)219 load_principal(krb5_context context, FILE *fp, int version, size_t maxsize,
220 struct k5buf *buf)
221 {
222 krb5_error_code ret;
223 uint32_t count;
224
225 if (version > 1) {
226 ret = load_bytes(context, fp, 4, buf);
227 if (ret)
228 return ret;
229 }
230 ret = read32(context, fp, version, buf, &count);
231 if (ret)
232 return ret;
233 /* Add one for the realm (except in version 1 which already counts it). */
234 if (version != 1)
235 count++;
236 while (count-- > 0) {
237 ret = load_data(context, fp, version, maxsize, buf);
238 if (ret)
239 return ret;
240 }
241 return 0;
242 }
243
244 /* Load a marshalled credential from the cache file into buf, without
245 * unmarshalling it. */
246 static krb5_error_code
load_cred(krb5_context context,FILE * fp,int version,size_t maxsize,struct k5buf * buf)247 load_cred(krb5_context context, FILE *fp, int version, size_t maxsize,
248 struct k5buf *buf)
249 {
250 krb5_error_code ret;
251 uint32_t count, i;
252
253 /* client and server */
254 ret = load_principal(context, fp, version, maxsize, buf);
255 if (ret)
256 return ret;
257 ret = load_principal(context, fp, version, maxsize, buf);
258 if (ret)
259 return ret;
260
261 /* keyblock (enctype, enctype again for version 3, length, value) */
262 ret = load_bytes(context, fp, (version == 3) ? 4 : 2, buf);
263 if (ret)
264 return ret;
265 ret = load_data(context, fp, version, maxsize, buf);
266 if (ret)
267 return ret;
268
269 /* times (4*4 bytes), is_skey (1 byte), ticket flags (4 bytes) */
270 ret = load_bytes(context, fp, 4 * 4 + 1 + 4, buf);
271 if (ret)
272 return ret;
273
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);
277 if (ret)
278 return ret;
279 while (count-- > 0) {
280 ret = load_bytes(context, fp, 2, buf);
281 if (ret)
282 return ret;
283 ret = load_data(context, fp, version, maxsize, buf);
284 if (ret)
285 return ret;
286 }
287 }
288
289 /* ticket and second_ticket */
290 ret = load_data(context, fp, version, maxsize, buf);
291 if (ret)
292 return ret;
293 return load_data(context, fp, version, maxsize, buf);
294 }
295
296 static krb5_error_code
read_principal(krb5_context context,FILE * fp,int version,krb5_principal * princ)297 read_principal(krb5_context context, FILE *fp, int version,
298 krb5_principal *princ)
299 {
300 krb5_error_code ret;
301 struct k5buf buf;
302 size_t maxsize;
303
304 *princ = NULL;
305 k5_buf_init_dynamic(&buf);
306
307 /* Read the principal representation into memory. */
308 ret = get_size(context, fp, &maxsize);
309 if (ret)
310 goto cleanup;
311 ret = load_principal(context, fp, version, maxsize, &buf);
312 if (ret)
313 goto cleanup;
314 ret = k5_buf_status(&buf);
315 if (ret)
316 goto cleanup;
317
318 /* Unmarshal it from buf into princ. */
319 ret = k5_unmarshal_princ(buf.data, buf.len, version, princ);
320
321 cleanup:
322 k5_buf_free(&buf);
323 return ret;
324 }
325
326 /*
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.
330 */
331 static krb5_error_code
open_cache_file(krb5_context context,const char * filename,krb5_boolean writable,FILE ** fp_out)332 open_cache_file(krb5_context context, const char *filename,
333 krb5_boolean writable, FILE **fp_out)
334 {
335 krb5_error_code ret;
336 int fd, flags, lockmode;
337 FILE *fp;
338
339 *fp_out = NULL;
340
341 flags = writable ? (O_RDWR | O_APPEND) : O_RDONLY;
342 fd = open(filename, flags | O_BINARY | O_CLOEXEC, 0600);
343 if (fd == -1)
344 return interpret_errno(context, errno);
345 set_cloexec_fd(fd);
346
347 lockmode = writable ? KRB5_LOCKMODE_EXCLUSIVE : KRB5_LOCKMODE_SHARED;
348 ret = krb5_lock_file(context, fd, lockmode);
349 if (ret) {
350 (void)close(fd);
351 return ret;
352 }
353
354 fp = fdopen(fd, writable ? "r+b" : "rb");
355 if (fp == NULL) {
356 (void)krb5_unlock_file(context, fd);
357 (void)close(fd);
358 return KRB5_CC_NOMEM;
359 }
360
361 *fp_out = fp;
362 return 0;
363 }
364
365 /* Unlock and close the cache file. Do nothing if fp is NULL. */
366 static krb5_error_code
close_cache_file(krb5_context context,FILE * fp)367 close_cache_file(krb5_context context, FILE *fp)
368 {
369 int st;
370 krb5_error_code ret;
371
372 if (fp == NULL)
373 return 0;
374 ret = krb5_unlock_file(context, fileno(fp));
375 st = fclose(fp);
376 if (ret)
377 return ret;
378 return st ? interpret_errno(context, errno) : 0;
379 }
380
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
read_header(krb5_context context,FILE * fp,int * version_out)384 read_header(krb5_context context, FILE *fp, int *version_out)
385 {
386 krb5_error_code ret;
387 krb5_os_context os_ctx = &context->os_context;
388 uint16_t fields_len, tag, flen;
389 uint32_t time_offset, usec_offset;
390 char i16buf[2];
391 int version;
392
393 *version_out = 0;
394
395 /* Get the file format version. */
396 ret = read_bytes(context, fp, i16buf, 2);
397 if (ret)
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;
403
404 /* Tagged header fields begin with version 4. */
405 if (version < 4)
406 return 0;
407
408 if (read16(context, fp, version, &fields_len))
409 return KRB5_CC_FORMAT;
410 while (fields_len) {
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;
414
415 switch (tag) {
416 case FCC_TAG_DELTATIME:
417 if (flen != 8 ||
418 read32(context, fp, version, NULL, &time_offset) ||
419 read32(context, fp, version, NULL, &usec_offset))
420 return KRB5_CC_FORMAT;
421
422 if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
423 (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID))
424 break;
425
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);
430 break;
431
432 default:
433 if (flen && fseek(fp, flen, SEEK_CUR) != 0)
434 return KRB5_CC_FORMAT;
435 break;
436 }
437 fields_len -= (4 + flen);
438 }
439 return 0;
440 }
441
442 static void
marshal_header(krb5_context context,struct k5buf * buf,krb5_principal princ)443 marshal_header(krb5_context context, struct k5buf *buf, krb5_principal princ)
444 {
445 krb5_os_context os_ctx = &context->os_context;
446 int version = context->fcc_default_format - FVNO_BASE;
447 uint16_t fields_len;
448
449 version = context->fcc_default_format - FVNO_BASE;
450 k5_buf_add_uint16_be(buf, FVNO_BASE + version);
451 if (version >= 4) {
452 /* Add tagged header fields. */
453 fields_len = 0;
454 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
455 fields_len += 12;
456 k5_buf_add_uint16_be(buf, fields_len);
457 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
458 /* Add time offset tag. */
459 k5_buf_add_uint16_be(buf, FCC_TAG_DELTATIME);
460 k5_buf_add_uint16_be(buf, 8);
461 k5_buf_add_uint32_be(buf, os_ctx->time_offset);
462 k5_buf_add_uint32_be(buf, os_ctx->usec_offset);
463 }
464 }
465 k5_marshal_princ(buf, version, princ);
466 }
467
468 /* Create or overwrite the cache file with a header and default principal. */
469 static krb5_error_code KRB5_CALLCONV
fcc_initialize(krb5_context context,krb5_ccache id,krb5_principal princ)470 fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
471 {
472 krb5_error_code ret;
473 fcc_data *data = id->data;
474 ssize_t nwritten;
475 int st, flags, fd = -1;
476 struct k5buf buf = EMPTY_K5BUF;
477 krb5_boolean file_locked = FALSE;
478
479 k5_cc_mutex_lock(context, &data->lock);
480
481 unlink(data->filename);
482 flags = O_CREAT | O_EXCL | O_RDWR | O_BINARY | O_CLOEXEC;
483 fd = open(data->filename, flags, 0600);
484 if (fd == -1) {
485 ret = interpret_errno(context, errno);
486 goto cleanup;
487 }
488 set_cloexec_fd(fd);
489
490 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
491 #ifdef HAVE_FCHMOD
492 st = fchmod(fd, S_IRUSR | S_IWUSR);
493 #else
494 st = chmod(data->filename, S_IRUSR | S_IWUSR);
495 #endif
496 if (st == -1) {
497 ret = interpret_errno(context, errno);
498 goto cleanup;
499 }
500 #endif
501
502 ret = krb5_lock_file(context, fd, KRB5_LOCKMODE_EXCLUSIVE);
503 if (ret)
504 goto cleanup;
505 file_locked = TRUE;
506
507 /* Prepare the header and principal in buf. */
508 k5_buf_init_dynamic(&buf);
509 marshal_header(context, &buf, princ);
510 ret = k5_buf_status(&buf);
511 if (ret)
512 goto cleanup;
513
514 /* Write the header and principal. */
515 nwritten = write(fd, buf.data, buf.len);
516 if (nwritten == -1)
517 ret = interpret_errno(context, errno);
518 if ((size_t)nwritten != buf.len)
519 ret = KRB5_CC_IO;
520
521 cleanup:
522 k5_buf_free(&buf);
523 if (file_locked)
524 krb5_unlock_file(context, fd);
525 if (fd != -1)
526 close(fd);
527 k5_cc_mutex_unlock(context, &data->lock);
528 krb5_change_cache();
529 return set_errmsg_filename(context, ret, data->filename);
530 }
531
532 /* Release an fcc_data object. */
533 static void
free_fccdata(krb5_context context,fcc_data * data)534 free_fccdata(krb5_context context, fcc_data *data)
535 {
536 k5_cc_mutex_assert_unlocked(context, &data->lock);
537 free(data->filename);
538 k5_cc_mutex_destroy(&data->lock);
539 free(data);
540 }
541
542 /* Release the ccache handle. */
543 static krb5_error_code KRB5_CALLCONV
fcc_close(krb5_context context,krb5_ccache id)544 fcc_close(krb5_context context, krb5_ccache id)
545 {
546 free_fccdata(context, id->data);
547 free(id);
548 return 0;
549 }
550
551 /* Destroy the cache file and release the handle. */
552 static krb5_error_code KRB5_CALLCONV
fcc_destroy(krb5_context context,krb5_ccache id)553 fcc_destroy(krb5_context context, krb5_ccache id)
554 {
555 krb5_error_code ret = 0;
556 fcc_data *data = id->data;
557 int st, fd;
558 struct stat buf;
559 unsigned long i, size;
560 unsigned int wlen;
561 char zeros[BUFSIZ];
562
563 k5_cc_mutex_lock(context, &data->lock);
564
565 fd = open(data->filename, O_RDWR | O_BINARY | O_CLOEXEC, 0);
566 if (fd < 0) {
567 ret = interpret_errno(context, errno);
568 goto cleanup;
569 }
570 set_cloexec_fd(fd);
571
572 #ifdef MSDOS_FILESYSTEM
573 /*
574 * "Disgusting bit of UNIX trivia" - that's how the writers of NFS describe
575 * the ability of UNIX to still write to a file which has been unlinked.
576 * Naturally, the PC can't do this. As a result, we have to delete the
577 * file after we wipe it clean, but that throws off all the error handling
578 * code. So we have do the work ourselves.
579 */
580 st = fstat(fd, &buf);
581 if (st == -1) {
582 ret = interpret_errno(context, errno);
583 size = 0; /* Nothing to wipe clean */
584 } else {
585 size = (unsigned long)buf.st_size;
586 }
587
588 memset(zeros, 0, BUFSIZ);
589 while (size > 0) {
590 wlen = (int)((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */
591 i = write(fd, zeros, wlen);
592 if (i < 0) {
593 ret = interpret_errno(context, errno);
594 /* Don't jump to cleanup--we still want to delete the file. */
595 break;
596 }
597 size -= i;
598 }
599
600 (void)close(fd);
601
602 st = unlink(data->filename);
603 if (st < 0) {
604 ret = interpret_errno(context, errno);
605 goto cleanup;
606 }
607
608 #else /* MSDOS_FILESYSTEM */
609
610 st = unlink(data->filename);
611 if (st < 0) {
612 ret = interpret_errno(context, errno);
613 (void)close(fd);
614 goto cleanup;
615 }
616
617 st = fstat(fd, &buf);
618 if (st < 0) {
619 ret = interpret_errno(context, errno);
620 (void)close(fd);
621 goto cleanup;
622 }
623
624 /* XXX This may not be legal XXX */
625 size = (unsigned long)buf.st_size;
626 memset(zeros, 0, BUFSIZ);
627 for (i = 0; i < size / BUFSIZ; i++) {
628 if (write(fd, zeros, BUFSIZ) < 0) {
629 ret = interpret_errno(context, errno);
630 (void)close(fd);
631 goto cleanup;
632 }
633 }
634
635 wlen = size % BUFSIZ;
636 if (write(fd, zeros, wlen) < 0) {
637 ret = interpret_errno(context, errno);
638 (void)close(fd);
639 goto cleanup;
640 }
641
642 st = close(fd);
643
644 if (st)
645 ret = interpret_errno(context, errno);
646
647 #endif /* MSDOS_FILESYSTEM */
648
649 cleanup:
650 (void)set_errmsg_filename(context, ret, data->filename);
651 k5_cc_mutex_unlock(context, &data->lock);
652 free_fccdata(context, data);
653 free(id);
654
655 krb5_change_cache();
656 return ret;
657 }
658
659 extern const krb5_cc_ops krb5_fcc_ops;
660
661 /* Create a file ccache handle for the pathname given by residual. */
662 static krb5_error_code KRB5_CALLCONV
fcc_resolve(krb5_context context,krb5_ccache * id,const char * residual)663 fcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)
664 {
665 krb5_ccache lid;
666 krb5_error_code ret;
667 fcc_data *data;
668
669 data = malloc(sizeof(fcc_data));
670 if (data == NULL)
671 return KRB5_CC_NOMEM;
672 data->filename = strdup(residual);
673 if (data->filename == NULL) {
674 free(data);
675 return KRB5_CC_NOMEM;
676 }
677 ret = k5_cc_mutex_init(&data->lock);
678 if (ret) {
679 free(data->filename);
680 free(data);
681 return ret;
682 }
683
684 lid = malloc(sizeof(struct _krb5_ccache));
685 if (lid == NULL) {
686 free_fccdata(context, data);
687 return KRB5_CC_NOMEM;
688 }
689
690 lid->ops = &krb5_fcc_ops;
691 lid->data = data;
692 lid->magic = KV5M_CCACHE;
693
694 /* Other routines will get errors on open, and callers must expect them, if
695 * cache is non-existent/unusable. */
696 *id = lid;
697 return 0;
698 }
699
700 /* Prepare for a sequential iteration over the cache file. */
701 static krb5_error_code KRB5_CALLCONV
fcc_start_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)702 fcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
703 {
704 krb5_fcc_cursor *fcursor = NULL;
705 krb5_error_code ret;
706 krb5_principal princ = NULL;
707 fcc_data *data = id->data;
708 FILE *fp = NULL;
709 int version;
710
711 k5_cc_mutex_lock(context, &data->lock);
712
713 fcursor = malloc(sizeof(krb5_fcc_cursor));
714 if (fcursor == NULL) {
715 ret = KRB5_CC_NOMEM;
716 goto cleanup;
717 }
718
719 /* Open the cache file and read the header. */
720 ret = open_cache_file(context, data->filename, FALSE, &fp);
721 if (ret)
722 goto cleanup;
723 ret = read_header(context, fp, &version);
724 if (ret)
725 goto cleanup;
726
727 /* Read past the default client principal name. */
728 ret = read_principal(context, fp, version, &princ);
729 if (ret)
730 goto cleanup;
731
732 /* Drop the shared file lock but retain the file handle. */
733 (void)krb5_unlock_file(context, fileno(fp));
734 fcursor->fp = fp;
735 fp = NULL;
736 fcursor->version = version;
737 *cursor = (krb5_cc_cursor)fcursor;
738 fcursor = NULL;
739
740 cleanup:
741 (void)close_cache_file(context, fp);
742 free(fcursor);
743 krb5_free_principal(context, princ);
744 k5_cc_mutex_unlock(context, &data->lock);
745 return set_errmsg_filename(context, ret, data->filename);
746 }
747
748 /*
749 * Return true if cred is a removed entry. We assume that any active entry
750 * with endtime=0 (such as a config entry or gssproxy encrypted credential)
751 * will also have authtime=0.
752 */
753 static inline krb5_boolean
cred_removed(krb5_creds * c)754 cred_removed(krb5_creds *c)
755 {
756 return c->times.endtime == 0 && c->times.authtime != 0;
757 }
758
759 /* Get the next credential from the cache file. */
760 static krb5_error_code KRB5_CALLCONV
fcc_next_cred(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)761 fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
762 krb5_creds *creds)
763 {
764 krb5_error_code ret;
765 krb5_fcc_cursor *fcursor = *cursor;
766 fcc_data *data = id->data;
767 struct k5buf buf;
768 size_t maxsize;
769 krb5_boolean file_locked = FALSE;
770
771 memset(creds, 0, sizeof(*creds));
772 k5_cc_mutex_lock(context, &data->lock);
773 k5_buf_init_dynamic_zap(&buf);
774
775 ret = krb5_lock_file(context, fileno(fcursor->fp), KRB5_LOCKMODE_SHARED);
776 if (ret)
777 goto cleanup;
778 file_locked = TRUE;
779
780 for (;;) {
781 /* Load a marshalled cred into memory. */
782 ret = get_size(context, fcursor->fp, &maxsize);
783 if (ret)
784 goto cleanup;
785 ret = load_cred(context, fcursor->fp, fcursor->version, maxsize, &buf);
786 if (ret)
787 goto cleanup;
788 ret = k5_buf_status(&buf);
789 if (ret)
790 goto cleanup;
791
792 /* Unmarshal it from buf into creds. */
793 ret = k5_unmarshal_cred(buf.data, buf.len, fcursor->version, creds);
794 if (ret)
795 goto cleanup;
796
797 /* Keep going if this entry has been removed; otherwise stop. */
798 if (!cred_removed(creds))
799 break;
800
801 k5_buf_truncate(&buf, 0);
802 krb5_free_cred_contents(context, creds);
803 }
804
805 cleanup:
806 if (file_locked)
807 (void)krb5_unlock_file(context, fileno(fcursor->fp));
808 k5_cc_mutex_unlock(context, &data->lock);
809 k5_buf_free(&buf);
810 return set_errmsg_filename(context, ret, data->filename);
811 }
812
813 /* Release an iteration cursor. */
814 static krb5_error_code KRB5_CALLCONV
fcc_end_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)815 fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
816 {
817 krb5_fcc_cursor *fcursor = *cursor;
818
819 (void)fclose(fcursor->fp);
820 free(fcursor);
821 *cursor = NULL;
822 return 0;
823 }
824
825 /* Generate a unique file ccache using the given template (which will be
826 * modified to contain the actual name of the file). */
827 krb5_error_code
krb5int_fcc_new_unique(krb5_context context,char * template,krb5_ccache * id)828 krb5int_fcc_new_unique(krb5_context context, char *template, krb5_ccache *id)
829 {
830 krb5_ccache lid;
831 int fd;
832 krb5_error_code ret;
833 fcc_data *data;
834 char fcc_fvno[2];
835 int16_t fcc_flen = 0;
836 int errsave, cnt;
837
838 fd = mkstemp(template);
839 if (fd == -1)
840 return interpret_errno(context, errno);
841 set_cloexec_fd(fd);
842
843 /* Allocate memory */
844 data = malloc(sizeof(fcc_data));
845 if (data == NULL) {
846 close(fd);
847 unlink(template);
848 return KRB5_CC_NOMEM;
849 }
850
851 data->filename = strdup(template);
852 if (data->filename == NULL) {
853 free(data);
854 close(fd);
855 unlink(template);
856 return KRB5_CC_NOMEM;
857 }
858
859 ret = k5_cc_mutex_init(&data->lock);
860 if (ret) {
861 free(data->filename);
862 free(data);
863 close(fd);
864 unlink(template);
865 return ret;
866 }
867 k5_cc_mutex_lock(context, &data->lock);
868
869 /* Ignore user's umask, set mode = 0600 */
870 #ifndef HAVE_FCHMOD
871 #ifdef HAVE_CHMOD
872 chmod(data->filename, S_IRUSR | S_IWUSR);
873 #endif
874 #else
875 fchmod(fd, S_IRUSR | S_IWUSR);
876 #endif
877 store_16_be(context->fcc_default_format, fcc_fvno);
878 cnt = write(fd, &fcc_fvno, 2);
879 if (cnt != 2) {
880 errsave = errno;
881 (void)close(fd);
882 (void)unlink(data->filename);
883 ret = (cnt == -1) ? interpret_errno(context, errsave) : KRB5_CC_IO;
884 goto err_out;
885 }
886 /* For version 4 we save a length for the rest of the header */
887 if (context->fcc_default_format == FVNO_BASE + 4) {
888 cnt = write(fd, &fcc_flen, sizeof(fcc_flen));
889 if (cnt != sizeof(fcc_flen)) {
890 errsave = errno;
891 (void)close(fd);
892 (void)unlink(data->filename);
893 ret = (cnt == -1) ? interpret_errno(context, errsave) : KRB5_CC_IO;
894 goto err_out;
895 }
896 }
897 if (close(fd) == -1) {
898 errsave = errno;
899 (void)unlink(data->filename);
900 ret = interpret_errno(context, errsave);
901 goto err_out;
902 }
903
904 k5_cc_mutex_assert_locked(context, &data->lock);
905 k5_cc_mutex_unlock(context, &data->lock);
906 lid = malloc(sizeof(*lid));
907 if (lid == NULL) {
908 free_fccdata(context, data);
909 return KRB5_CC_NOMEM;
910 }
911
912 lid->ops = &krb5_fcc_ops;
913 lid->data = data;
914 lid->magic = KV5M_CCACHE;
915
916 *id = lid;
917
918 krb5_change_cache();
919 return 0;
920
921 err_out:
922 (void)set_errmsg_filename(context, ret, data->filename);
923 k5_cc_mutex_unlock(context, &data->lock);
924 k5_cc_mutex_destroy(&data->lock);
925 free(data->filename);
926 free(data);
927 return ret;
928 }
929
930 /*
931 * Create a new file cred cache whose name is guaranteed to be unique. The
932 * name begins with the string TKT_ROOT (from fcc.h). The cache file is not
933 * opened, but the new filename is reserved.
934 */
935 static krb5_error_code KRB5_CALLCONV
fcc_generate_new(krb5_context context,krb5_ccache * id)936 fcc_generate_new(krb5_context context, krb5_ccache *id)
937 {
938 char scratch[sizeof(TKT_ROOT) + 7]; /* Room for XXXXXX and terminator */
939
940 (void)snprintf(scratch, sizeof(scratch), "%sXXXXXX", TKT_ROOT);
941 return krb5int_fcc_new_unique(context, scratch, id);
942 }
943
944 /* Return an alias to the pathname of the cache file. */
945 static const char * KRB5_CALLCONV
fcc_get_name(krb5_context context,krb5_ccache id)946 fcc_get_name(krb5_context context, krb5_ccache id)
947 {
948 return ((fcc_data *)id->data)->filename;
949 }
950
951 /* Retrieve a copy of the default principal, if the cache is initialized. */
952 static krb5_error_code KRB5_CALLCONV
fcc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * princ)953 fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
954 {
955 krb5_error_code ret;
956 fcc_data *data = id->data;
957 FILE *fp = NULL;
958 int version;
959
960 k5_cc_mutex_lock(context, &data->lock);
961 ret = open_cache_file(context, data->filename, FALSE, &fp);
962 if (ret)
963 goto cleanup;
964 ret = read_header(context, fp, &version);
965 if (ret)
966 goto cleanup;
967 ret = read_principal(context, fp, version, princ);
968
969 cleanup:
970 (void)close_cache_file(context, fp);
971 k5_cc_mutex_unlock(context, &data->lock);
972 return set_errmsg_filename(context, ret, data->filename);
973 }
974
975 /* Search for a credential within the cache file. */
976 static krb5_error_code KRB5_CALLCONV
fcc_retrieve(krb5_context context,krb5_ccache id,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds)977 fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields,
978 krb5_creds *mcreds, krb5_creds *creds)
979 {
980 krb5_error_code ret;
981
982 ret = k5_cc_retrieve_cred_default(context, id, whichfields, mcreds, creds);
983 return set_errmsg_filename(context, ret, ((fcc_data *)id->data)->filename);
984 }
985
986 /* Store a credential in the cache file. */
987 static krb5_error_code KRB5_CALLCONV
fcc_store(krb5_context context,krb5_ccache id,krb5_creds * creds)988 fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
989 {
990 krb5_error_code ret, ret2;
991 fcc_data *data = id->data;
992 FILE *fp = NULL;
993 int version;
994 struct k5buf buf = EMPTY_K5BUF;
995 ssize_t nwritten;
996
997 k5_cc_mutex_lock(context, &data->lock);
998
999 /* Open the cache file for O_APPEND writing. */
1000 ret = open_cache_file(context, data->filename, TRUE, &fp);
1001 if (ret)
1002 goto cleanup;
1003 ret = read_header(context, fp, &version);
1004 if (ret)
1005 goto cleanup;
1006
1007 /* Marshal the cred and write it to the file with a single append write. */
1008 k5_buf_init_dynamic_zap(&buf);
1009 k5_marshal_cred(&buf, version, creds);
1010 ret = k5_buf_status(&buf);
1011 if (ret)
1012 goto cleanup;
1013 nwritten = write(fileno(fp), buf.data, buf.len);
1014 if (nwritten == -1)
1015 ret = interpret_errno(context, errno);
1016 if ((size_t)nwritten != buf.len)
1017 ret = KRB5_CC_IO;
1018
1019 krb5_change_cache();
1020
1021 cleanup:
1022 k5_buf_free(&buf);
1023 ret2 = close_cache_file(context, fp);
1024 k5_cc_mutex_unlock(context, &data->lock);
1025 return set_errmsg_filename(context, ret ? ret : ret2, data->filename);
1026 }
1027
1028 /*
1029 * Overwrite cred in the ccache file with an entry that should not match any
1030 * reasonable search. Deletion is not guaranteed. This method is originally
1031 * from Heimdal, with the addition of setting authtime to -1.
1032 */
1033 static krb5_error_code
delete_cred(krb5_context context,krb5_ccache cache,krb5_cc_cursor * cursor,krb5_creds * cred)1034 delete_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
1035 krb5_creds *cred)
1036 {
1037 krb5_error_code ret;
1038 krb5_fcc_cursor *fcursor = *cursor;
1039 fcc_data *data = cache->data;
1040 struct k5buf expected = EMPTY_K5BUF, overwrite = EMPTY_K5BUF;
1041 int fd = -1;
1042 uint8_t *on_disk = NULL;
1043 ssize_t rwret;
1044 off_t start_offset;
1045
1046 k5_buf_init_dynamic_zap(&expected);
1047 k5_buf_init_dynamic_zap(&overwrite);
1048
1049 /* Re-marshal cred to get its byte representation in the file. */
1050 k5_marshal_cred(&expected, fcursor->version, cred);
1051 ret = k5_buf_status(&expected);
1052 if (ret)
1053 goto cleanup;
1054
1055 /*
1056 * Mark the cred expired so that it will be skipped over by any future
1057 * match checks. Heimdal only sets endtime, but we also set authtime to
1058 * distinguish from gssproxy's creds.
1059 */
1060 cred->times.endtime = 0;
1061 cred->times.authtime = -1;
1062
1063 /* For config entries, also change the realm so that other implementations
1064 * won't match them. */
1065 if (data_eq_string(cred->server->realm, "X-CACHECONF:"))
1066 memcpy(cred->server->realm.data, "X-RMED-CONF:", 12);
1067
1068 k5_marshal_cred(&overwrite, fcursor->version, cred);
1069 ret = k5_buf_status(&overwrite);
1070 if (ret)
1071 goto cleanup;
1072
1073 if (expected.len != overwrite.len) {
1074 ret = KRB5_CC_FORMAT;
1075 goto cleanup;
1076 }
1077
1078 /* Get a non-O_APPEND handle to the raw file. */
1079 fd = open(data->filename, O_RDWR | O_BINARY | O_CLOEXEC);
1080 if (fd == -1) {
1081 ret = interpret_errno(context, errno);
1082 goto cleanup;
1083 }
1084
1085 start_offset = ftell(fcursor->fp);
1086 if (start_offset == -1) {
1087 ret = interpret_errno(context, errno);
1088 goto cleanup;
1089 }
1090 start_offset -= expected.len;
1091
1092 /* Read the bytes at the entry to be overwritten. */
1093 if (lseek(fd, start_offset, SEEK_SET) == -1) {
1094 ret = interpret_errno(context, errno);
1095 goto cleanup;
1096 }
1097 on_disk = k5alloc(expected.len, &ret);
1098 if (ret != 0)
1099 goto cleanup;
1100 rwret = read(fd, on_disk, expected.len);
1101 if (rwret < 0) {
1102 ret = interpret_errno(context, errno);
1103 goto cleanup;
1104 } else if ((size_t)rwret != expected.len) {
1105 ret = KRB5_CC_FORMAT;
1106 goto cleanup;
1107 }
1108
1109 /*
1110 * If the bytes have changed, either someone else removed the same cred or
1111 * the cache was reinitialized. Either way the cred is no longer present,
1112 * so return successfully.
1113 */
1114 if (memcmp(on_disk, expected.data, expected.len) != 0)
1115 goto cleanup;
1116
1117 /* Write out the altered entry. */
1118 if (lseek(fd, start_offset, SEEK_SET) == -1) {
1119 ret = interpret_errno(context, errno);
1120 goto cleanup;
1121 }
1122 rwret = write(fd, overwrite.data, overwrite.len);
1123 if (rwret < 0) {
1124 ret = interpret_errno(context, errno);
1125 goto cleanup;
1126 }
1127
1128 cleanup:
1129 if (fd >= 0)
1130 close(fd);
1131 zapfree(on_disk, expected.len);
1132 k5_buf_free(&expected);
1133 k5_buf_free(&overwrite);
1134 return ret;
1135 }
1136
1137 /* Remove the given creds from the ccache file. */
1138 static krb5_error_code KRB5_CALLCONV
fcc_remove_cred(krb5_context context,krb5_ccache cache,krb5_flags flags,krb5_creds * creds)1139 fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
1140 krb5_creds *creds)
1141 {
1142 krb5_error_code ret;
1143 krb5_cc_cursor cursor;
1144 krb5_creds cur;
1145
1146 ret = krb5_cc_start_seq_get(context, cache, &cursor);
1147 if (ret)
1148 return ret;
1149
1150 for (;;) {
1151 ret = krb5_cc_next_cred(context, cache, &cursor, &cur);
1152 if (ret)
1153 break;
1154
1155 if (krb5int_cc_creds_match_request(context, flags, creds, &cur))
1156 ret = delete_cred(context, cache, &cursor, &cur);
1157 krb5_free_cred_contents(context, &cur);
1158 if (ret)
1159 break;
1160 }
1161
1162 krb5_cc_end_seq_get(context, cache, &cursor);
1163 return (ret == KRB5_CC_END) ? 0 : ret;
1164 }
1165
1166 static krb5_error_code KRB5_CALLCONV
fcc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)1167 fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
1168 {
1169 return 0;
1170 }
1171
1172 static krb5_error_code KRB5_CALLCONV
fcc_get_flags(krb5_context context,krb5_ccache id,krb5_flags * flags)1173 fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
1174 {
1175 *flags = 0;
1176 return 0;
1177 }
1178
1179 /* Prepare to iterate over the caches in the per-type collection. */
1180 static krb5_error_code KRB5_CALLCONV
fcc_ptcursor_new(krb5_context context,krb5_cc_ptcursor * cursor)1181 fcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor)
1182 {
1183 krb5_cc_ptcursor n = NULL;
1184 struct krb5_fcc_ptcursor_data *cdata = NULL;
1185
1186 *cursor = NULL;
1187
1188 n = malloc(sizeof(*n));
1189 if (n == NULL)
1190 return ENOMEM;
1191 n->ops = &krb5_fcc_ops;
1192 cdata = malloc(sizeof(*cdata));
1193 if (cdata == NULL) {
1194 free(n);
1195 return ENOMEM;
1196 }
1197 cdata->first = TRUE;
1198 n->data = cdata;
1199 *cursor = n;
1200 return 0;
1201 }
1202
1203 /* Get the next cache in the per-type collection. The FILE per-type collection
1204 * contains only the context's default cache if it is a file cache. */
1205 static krb5_error_code KRB5_CALLCONV
fcc_ptcursor_next(krb5_context context,krb5_cc_ptcursor cursor,krb5_ccache * cache_out)1206 fcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
1207 krb5_ccache *cache_out)
1208 {
1209 krb5_error_code ret;
1210 struct krb5_fcc_ptcursor_data *cdata = cursor->data;
1211 const char *defname, *residual;
1212 krb5_ccache cache;
1213 struct stat sb;
1214
1215 *cache_out = NULL;
1216 if (!cdata->first)
1217 return 0;
1218 cdata->first = FALSE;
1219
1220 defname = krb5_cc_default_name(context);
1221 if (!defname)
1222 return 0;
1223
1224 /* Check if the default has type FILE or no type; find the residual. */
1225 if (strncmp(defname, "FILE:", 5) == 0)
1226 residual = defname + 5;
1227 else if (strchr(defname + 2, ':') == NULL) /* Skip drive prefix if any. */
1228 residual = defname;
1229 else
1230 return 0;
1231
1232 /* Don't yield a nonexistent default file cache. */
1233 if (stat(residual, &sb) != 0)
1234 return 0;
1235
1236 ret = krb5_cc_resolve(context, defname, &cache);
1237 if (ret)
1238 return set_errmsg_filename(context, ret, defname);
1239 *cache_out = cache;
1240 return 0;
1241 }
1242
1243 /* Release a per-type collection iteration cursor. */
1244 static krb5_error_code KRB5_CALLCONV
fcc_ptcursor_free(krb5_context context,krb5_cc_ptcursor * cursor)1245 fcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
1246 {
1247 if (*cursor == NULL)
1248 return 0;
1249 free((*cursor)->data);
1250 free(*cursor);
1251 *cursor = NULL;
1252 return 0;
1253 }
1254
1255 /* Lock the cache handle against other threads. (This does not lock the cache
1256 * file against other processes.) */
1257 static krb5_error_code KRB5_CALLCONV
fcc_lock(krb5_context context,krb5_ccache id)1258 fcc_lock(krb5_context context, krb5_ccache id)
1259 {
1260 fcc_data *data = id->data;
1261 k5_cc_mutex_lock(context, &data->lock);
1262 return 0;
1263 }
1264
1265 /* Unlock the cache handle. */
1266 static krb5_error_code KRB5_CALLCONV
fcc_unlock(krb5_context context,krb5_ccache id)1267 fcc_unlock(krb5_context context, krb5_ccache id)
1268 {
1269 fcc_data *data = id->data;
1270 k5_cc_mutex_unlock(context, &data->lock);
1271 return 0;
1272 }
1273
1274 static krb5_error_code KRB5_CALLCONV
fcc_replace(krb5_context context,krb5_ccache id,krb5_principal princ,krb5_creds ** creds)1275 fcc_replace(krb5_context context, krb5_ccache id, krb5_principal princ,
1276 krb5_creds **creds)
1277 {
1278 krb5_error_code ret;
1279 fcc_data *data = id->data;
1280 char *tmpname = NULL;
1281 int i, st, fd = -1, version = context->fcc_default_format - FVNO_BASE;
1282 ssize_t nwritten;
1283 struct k5buf buf = EMPTY_K5BUF;
1284 krb5_boolean tmpfile_exists = FALSE;
1285
1286 if (asprintf(&tmpname, "%s.XXXXXX", data->filename) < 0)
1287 return ENOMEM;
1288 fd = mkstemp(tmpname);
1289 if (fd < 0)
1290 goto errno_cleanup;
1291 tmpfile_exists = TRUE;
1292
1293 k5_buf_init_dynamic_zap(&buf);
1294 marshal_header(context, &buf, princ);
1295 for (i = 0; creds[i] != NULL; i++)
1296 k5_marshal_cred(&buf, version, creds[i]);
1297 ret = k5_buf_status(&buf);
1298 if (ret)
1299 goto cleanup;
1300
1301 nwritten = write(fd, buf.data, buf.len);
1302 if (nwritten == -1)
1303 goto errno_cleanup;
1304 if ((size_t)nwritten != buf.len) {
1305 ret = KRB5_CC_IO;
1306 goto cleanup;
1307 }
1308 st = close(fd);
1309 fd = -1;
1310 if (st != 0)
1311 goto errno_cleanup;
1312
1313 st = rename(tmpname, data->filename);
1314 if (st != 0)
1315 goto errno_cleanup;
1316 tmpfile_exists = FALSE;
1317
1318 cleanup:
1319 k5_buf_free(&buf);
1320 if (fd != -1)
1321 close(fd);
1322 if (tmpfile_exists)
1323 unlink(tmpname);
1324 free(tmpname);
1325 return ret;
1326
1327 errno_cleanup:
1328 ret = interpret_errno(context, errno);
1329 goto cleanup;
1330 }
1331
1332 /* Translate a system errno value to a Kerberos com_err code. */
1333 static krb5_error_code
interpret_errno(krb5_context context,int errnum)1334 interpret_errno(krb5_context context, int errnum)
1335 {
1336 krb5_error_code ret;
1337
1338 switch (errnum) {
1339 case ENOENT:
1340 case ENOTDIR:
1341 #ifdef ELOOP
1342 case ELOOP:
1343 #endif
1344 #ifdef ENAMETOOLONG
1345 case ENAMETOOLONG:
1346 #endif
1347 ret = KRB5_FCC_NOFILE;
1348 break;
1349 case EPERM:
1350 case EACCES:
1351 #ifdef EISDIR
1352 case EISDIR: /* Mac doesn't have EISDIR */
1353 #endif
1354 case EROFS:
1355 ret = KRB5_FCC_PERM;
1356 break;
1357 case EINVAL:
1358 case EEXIST:
1359 case EFAULT:
1360 case EBADF:
1361 #ifdef EWOULDBLOCK
1362 case EWOULDBLOCK:
1363 #endif
1364 ret = KRB5_FCC_INTERNAL;
1365 break;
1366 /*
1367 * The rest all map to KRB5_CC_IO. These errnos are listed to
1368 * document that they've been considered explicitly:
1369 *
1370 * - EDQUOT
1371 * - ENOSPC
1372 * - EIO
1373 * - ENFILE
1374 * - EMFILE
1375 * - ENXIO
1376 * - EBUSY
1377 * - ETXTBSY
1378 */
1379 default:
1380 ret = KRB5_CC_IO;
1381 break;
1382 }
1383 return ret;
1384 }
1385
1386 const krb5_cc_ops krb5_fcc_ops = {
1387 0,
1388 "FILE",
1389 fcc_get_name,
1390 fcc_resolve,
1391 fcc_generate_new,
1392 fcc_initialize,
1393 fcc_destroy,
1394 fcc_close,
1395 fcc_store,
1396 fcc_retrieve,
1397 fcc_get_principal,
1398 fcc_start_seq_get,
1399 fcc_next_cred,
1400 fcc_end_seq_get,
1401 fcc_remove_cred,
1402 fcc_set_flags,
1403 fcc_get_flags,
1404 fcc_ptcursor_new,
1405 fcc_ptcursor_next,
1406 fcc_ptcursor_free,
1407 fcc_replace,
1408 NULL, /* wasdefault */
1409 fcc_lock,
1410 fcc_unlock,
1411 NULL, /* switch_to */
1412 };
1413
1414 #if defined(_WIN32)
1415 /*
1416 * krb5_change_cache should be called after the cache changes.
1417 * A notification message is is posted out to all top level
1418 * windows so that they may recheck the cache based on the
1419 * changes made. We register a unique message type with which
1420 * we'll communicate to all other processes.
1421 */
1422
1423 krb5_error_code
krb5_change_cache(void)1424 krb5_change_cache(void)
1425 {
1426 PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0);
1427 return 0;
1428 }
1429
1430 unsigned int KRB5_CALLCONV
krb5_get_notification_message(void)1431 krb5_get_notification_message(void)
1432 {
1433 static unsigned int message = 0;
1434
1435 if (message == 0)
1436 message = RegisterWindowMessage(WM_KERBEROS5_CHANGED);
1437
1438 return message;
1439 }
1440 #else /* _WIN32 */
1441
1442 krb5_error_code
krb5_change_cache(void)1443 krb5_change_cache(void)
1444 {
1445 return 0;
1446 }
1447
1448 unsigned int
krb5_get_notification_message(void)1449 krb5_get_notification_message(void)
1450 {
1451 return 0;
1452 }
1453
1454 #endif /* _WIN32 */
1455
1456 const krb5_cc_ops krb5_cc_file_ops = {
1457 0,
1458 "FILE",
1459 fcc_get_name,
1460 fcc_resolve,
1461 fcc_generate_new,
1462 fcc_initialize,
1463 fcc_destroy,
1464 fcc_close,
1465 fcc_store,
1466 fcc_retrieve,
1467 fcc_get_principal,
1468 fcc_start_seq_get,
1469 fcc_next_cred,
1470 fcc_end_seq_get,
1471 fcc_remove_cred,
1472 fcc_set_flags,
1473 fcc_get_flags,
1474 fcc_ptcursor_new,
1475 fcc_ptcursor_next,
1476 fcc_ptcursor_free,
1477 fcc_replace,
1478 NULL, /* wasdefault */
1479 fcc_lock,
1480 fcc_unlock,
1481 NULL, /* switch_to */
1482 };
1483