Add PAC to the first entry in the array since Windows and samba3 expects it there.
[Samba.git] / lib / krb5 / fcache.c
blobf8e74f1ddc1c1964a1290fe717b388f1098dab76
1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
36 typedef struct krb5_fcache{
37 char *filename;
38 int version;
39 }krb5_fcache;
41 struct fcc_cursor {
42 int fd;
43 krb5_storage *sp;
46 #define KRB5_FCC_FVNO_1 1
47 #define KRB5_FCC_FVNO_2 2
48 #define KRB5_FCC_FVNO_3 3
49 #define KRB5_FCC_FVNO_4 4
51 #define FCC_TAG_DELTATIME 1
53 #define FCACHE(X) ((krb5_fcache*)(X)->data.data)
55 #define FILENAME(X) (FCACHE(X)->filename)
57 #define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
59 static const char*
60 fcc_get_name(krb5_context context,
61 krb5_ccache id)
63 return FILENAME(id);
66 int
67 _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
68 const char *filename)
70 int ret;
71 #ifdef HAVE_FCNTL
72 struct flock l;
74 l.l_start = 0;
75 l.l_len = 0;
76 l.l_type = exclusive ? F_WRLCK : F_RDLCK;
77 l.l_whence = SEEK_SET;
78 ret = fcntl(fd, F_SETLKW, &l);
79 #else
80 ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH);
81 #endif
82 if(ret < 0)
83 ret = errno;
84 if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */
85 ret = EAGAIN;
87 switch (ret) {
88 case 0:
89 break;
90 case EINVAL: /* filesystem doesn't support locking, let the user have it */
91 ret = 0;
92 break;
93 case EAGAIN:
94 krb5_set_error_message(context, ret,
95 N_("timed out locking cache file %s", "file"),
96 filename);
97 break;
98 default:
99 krb5_set_error_message(context, ret,
100 N_("error locking cache file %s: %s",
101 "file, error"),
102 filename, strerror(ret));
103 break;
105 return ret;
109 _krb5_xunlock(krb5_context context, int fd)
111 int ret;
112 #ifdef HAVE_FCNTL
113 struct flock l;
114 l.l_start = 0;
115 l.l_len = 0;
116 l.l_type = F_UNLCK;
117 l.l_whence = SEEK_SET;
118 ret = fcntl(fd, F_SETLKW, &l);
119 #else
120 ret = flock(fd, LOCK_UN);
121 #endif
122 if (ret < 0)
123 ret = errno;
124 switch (ret) {
125 case 0:
126 break;
127 case EINVAL: /* filesystem doesn't support locking, let the user have it */
128 ret = 0;
129 break;
130 default:
131 krb5_set_error_message(context, ret,
132 N_("Failed to unlock file: %s", ""),
133 strerror(ret));
134 break;
136 return ret;
139 static krb5_error_code
140 write_storage(krb5_context context, krb5_storage *sp, int fd)
142 krb5_error_code ret;
143 krb5_data data;
144 ssize_t sret;
146 ret = krb5_storage_to_data(sp, &data);
147 if (ret) {
148 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
149 return ret;
151 sret = write(fd, data.data, data.length);
152 ret = (sret != data.length);
153 krb5_data_free(&data);
154 if (ret) {
155 ret = errno;
156 krb5_set_error_message(context, ret,
157 N_("Failed to write FILE credential data", ""));
158 return ret;
160 return 0;
164 static krb5_error_code
165 fcc_lock(krb5_context context, krb5_ccache id,
166 int fd, krb5_boolean exclusive)
168 return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
171 static krb5_error_code
172 fcc_unlock(krb5_context context, int fd)
174 return _krb5_xunlock(context, fd);
177 static krb5_error_code
178 fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
180 krb5_fcache *f;
181 f = malloc(sizeof(*f));
182 if(f == NULL) {
183 krb5_set_error_message(context, KRB5_CC_NOMEM,
184 N_("malloc: out of memory", ""));
185 return KRB5_CC_NOMEM;
187 f->filename = strdup(res);
188 if(f->filename == NULL){
189 free(f);
190 krb5_set_error_message(context, KRB5_CC_NOMEM,
191 N_("malloc: out of memory", ""));
192 return KRB5_CC_NOMEM;
194 f->version = 0;
195 (*id)->data.data = f;
196 (*id)->data.length = sizeof(*f);
197 return 0;
201 * Try to scrub the contents of `filename' safely.
204 static int
205 scrub_file (int fd)
207 off_t pos;
208 char buf[128];
210 pos = lseek(fd, 0, SEEK_END);
211 if (pos < 0)
212 return errno;
213 if (lseek(fd, 0, SEEK_SET) < 0)
214 return errno;
215 memset(buf, 0, sizeof(buf));
216 while(pos > 0) {
217 ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
219 if (tmp < 0)
220 return errno;
221 pos -= tmp;
223 fsync (fd);
224 return 0;
228 * Erase `filename' if it exists, trying to remove the contents if
229 * it's `safe'. We always try to remove the file, it it exists. It's
230 * only overwritten if it's a regular file (not a symlink and not a
231 * hardlink)
234 krb5_error_code
235 _krb5_erase_file(krb5_context context, const char *filename)
237 int fd;
238 struct stat sb1, sb2;
239 int ret;
241 ret = lstat (filename, &sb1);
242 if (ret < 0)
243 return errno;
245 fd = open(filename, O_RDWR | O_BINARY);
246 if(fd < 0) {
247 if(errno == ENOENT)
248 return 0;
249 else
250 return errno;
252 rk_cloexec(fd);
253 ret = _krb5_xlock(context, fd, 1, filename);
254 if (ret) {
255 close(fd);
256 return ret;
258 if (unlink(filename) < 0) {
259 _krb5_xunlock(context, fd);
260 close (fd);
261 return errno;
263 ret = fstat (fd, &sb2);
264 if (ret < 0) {
265 _krb5_xunlock(context, fd);
266 close (fd);
267 return errno;
270 /* check if someone was playing with symlinks */
272 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
273 _krb5_xunlock(context, fd);
274 close (fd);
275 return EPERM;
278 /* there are still hard links to this file */
280 if (sb2.st_nlink != 0) {
281 _krb5_xunlock(context, fd);
282 close (fd);
283 return 0;
286 ret = scrub_file (fd);
287 if (ret) {
288 _krb5_xunlock(context, fd);
289 close(fd);
290 return ret;
292 ret = _krb5_xunlock(context, fd);
293 close (fd);
294 return ret;
297 static krb5_error_code
298 fcc_gen_new(krb5_context context, krb5_ccache *id)
300 krb5_fcache *f;
301 int fd;
302 char *file;
304 f = malloc(sizeof(*f));
305 if(f == NULL) {
306 krb5_set_error_message(context, KRB5_CC_NOMEM,
307 N_("malloc: out of memory", ""));
308 return KRB5_CC_NOMEM;
310 asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
311 if(file == NULL) {
312 free(f);
313 krb5_set_error_message(context, KRB5_CC_NOMEM,
314 N_("malloc: out of memory", ""));
315 return KRB5_CC_NOMEM;
317 fd = mkstemp(file);
318 if(fd < 0) {
319 int ret = errno;
320 krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), file);
321 free(f);
322 free(file);
323 return ret;
325 close(fd);
326 f->filename = file;
327 f->version = 0;
328 (*id)->data.data = f;
329 (*id)->data.length = sizeof(*f);
330 return 0;
333 static void
334 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
336 int flags = 0;
337 switch(vno) {
338 case KRB5_FCC_FVNO_1:
339 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
340 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
341 flags |= KRB5_STORAGE_HOST_BYTEORDER;
342 break;
343 case KRB5_FCC_FVNO_2:
344 flags |= KRB5_STORAGE_HOST_BYTEORDER;
345 break;
346 case KRB5_FCC_FVNO_3:
347 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
348 break;
349 case KRB5_FCC_FVNO_4:
350 break;
351 default:
352 krb5_abortx(context,
353 "storage_set_flags called with bad vno (%x)", vno);
355 krb5_storage_set_flags(sp, flags);
358 static krb5_error_code
359 fcc_open(krb5_context context,
360 krb5_ccache id,
361 int *fd_ret,
362 int flags,
363 mode_t mode)
365 krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
366 (flags | O_RDWR) == flags);
367 krb5_error_code ret;
368 const char *filename = FILENAME(id);
369 int fd;
370 fd = open(filename, flags, mode);
371 if(fd < 0) {
372 ret = errno;
373 krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"),
374 filename, strerror(ret));
375 return ret;
377 rk_cloexec(fd);
379 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
380 close(fd);
381 return ret;
383 *fd_ret = fd;
384 return 0;
387 static krb5_error_code
388 fcc_initialize(krb5_context context,
389 krb5_ccache id,
390 krb5_principal primary_principal)
392 krb5_fcache *f = FCACHE(id);
393 int ret = 0;
394 int fd;
395 char *filename = f->filename;
397 unlink (filename);
399 ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
400 if(ret)
401 return ret;
403 krb5_storage *sp;
404 sp = krb5_storage_emem();
405 krb5_storage_set_eof_code(sp, KRB5_CC_END);
406 if(context->fcache_vno != 0)
407 f->version = context->fcache_vno;
408 else
409 f->version = KRB5_FCC_FVNO_4;
410 ret |= krb5_store_int8(sp, 5);
411 ret |= krb5_store_int8(sp, f->version);
412 storage_set_flags(context, sp, f->version);
413 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
414 /* V4 stuff */
415 if (context->kdc_sec_offset) {
416 ret |= krb5_store_int16 (sp, 12); /* length */
417 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
418 ret |= krb5_store_int16 (sp, 8); /* length of data */
419 ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
420 ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
421 } else {
422 ret |= krb5_store_int16 (sp, 0);
425 ret |= krb5_store_principal(sp, primary_principal);
427 ret |= write_storage(context, sp, fd);
429 krb5_storage_free(sp);
431 fcc_unlock(context, fd);
432 if (close(fd) < 0)
433 if (ret == 0) {
434 ret = errno;
435 krb5_set_error_message (context, ret, N_("close %s: %s", ""),
436 FILENAME(id), strerror(ret));
438 return ret;
441 static krb5_error_code
442 fcc_close(krb5_context context,
443 krb5_ccache id)
445 free (FILENAME(id));
446 krb5_data_free(&id->data);
447 return 0;
450 static krb5_error_code
451 fcc_destroy(krb5_context context,
452 krb5_ccache id)
454 _krb5_erase_file(context, FILENAME(id));
455 return 0;
458 static krb5_error_code
459 fcc_store_cred(krb5_context context,
460 krb5_ccache id,
461 krb5_creds *creds)
463 int ret;
464 int fd;
466 ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0);
467 if(ret)
468 return ret;
470 krb5_storage *sp;
472 sp = krb5_storage_emem();
473 krb5_storage_set_eof_code(sp, KRB5_CC_END);
474 storage_set_flags(context, sp, FCACHE(id)->version);
475 if (!krb5_config_get_bool_default(context, NULL, TRUE,
476 "libdefaults",
477 "fcc-mit-ticketflags",
478 NULL))
479 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
480 ret = krb5_store_creds(sp, creds);
481 if (ret == 0)
482 ret = write_storage(context, sp, fd);
483 krb5_storage_free(sp);
485 fcc_unlock(context, fd);
486 if (close(fd) < 0) {
487 if (ret == 0) {
488 ret = errno;
489 krb5_set_error_message (context, ret, N_("close %s: %s", ""),
490 FILENAME(id), strerror(ret));
493 return ret;
496 static krb5_error_code
497 init_fcc (krb5_context context,
498 krb5_ccache id,
499 krb5_storage **ret_sp,
500 int *ret_fd)
502 int fd;
503 int8_t pvno, tag;
504 krb5_storage *sp;
505 krb5_error_code ret;
507 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
508 if(ret)
509 return ret;
511 sp = krb5_storage_from_fd(fd);
512 if(sp == NULL) {
513 krb5_clear_error_message(context);
514 ret = ENOMEM;
515 goto out;
517 krb5_storage_set_eof_code(sp, KRB5_CC_END);
518 ret = krb5_ret_int8(sp, &pvno);
519 if(ret != 0) {
520 if(ret == KRB5_CC_END) {
521 ret = ENOENT;
522 krb5_set_error_message(context, ret,
523 N_("Empty credential cache file: %s", ""),
524 FILENAME(id));
525 } else
526 krb5_set_error_message(context, ret, N_("Error reading pvno "
527 "in cache file: %s", ""),
528 FILENAME(id));
529 goto out;
531 if(pvno != 5) {
532 ret = KRB5_CCACHE_BADVNO;
533 krb5_set_error_message(context, ret, N_("Bad version number in credential "
534 "cache file: %s", ""),
535 FILENAME(id));
536 goto out;
538 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
539 if(ret != 0) {
540 ret = KRB5_CC_FORMAT;
541 krb5_set_error_message(context, ret, "Error reading tag in "
542 "cache file: %s", FILENAME(id));
543 goto out;
545 FCACHE(id)->version = tag;
546 storage_set_flags(context, sp, FCACHE(id)->version);
547 switch (tag) {
548 case KRB5_FCC_FVNO_4: {
549 int16_t length;
551 ret = krb5_ret_int16 (sp, &length);
552 if(ret) {
553 ret = KRB5_CC_FORMAT;
554 krb5_set_error_message(context, ret,
555 N_("Error reading tag length in "
556 "cache file: %s", ""), FILENAME(id));
557 goto out;
559 while(length > 0) {
560 int16_t dtag, data_len;
561 int i;
562 int8_t dummy;
564 ret = krb5_ret_int16 (sp, &dtag);
565 if(ret) {
566 ret = KRB5_CC_FORMAT;
567 krb5_set_error_message(context, ret, N_("Error reading dtag in "
568 "cache file: %s", ""),
569 FILENAME(id));
570 goto out;
572 ret = krb5_ret_int16 (sp, &data_len);
573 if(ret) {
574 ret = KRB5_CC_FORMAT;
575 krb5_set_error_message(context, ret,
576 N_("Error reading dlength "
577 "in cache file: %s",""),
578 FILENAME(id));
579 goto out;
581 switch (dtag) {
582 case FCC_TAG_DELTATIME :
583 ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
584 if(ret) {
585 ret = KRB5_CC_FORMAT;
586 krb5_set_error_message(context, ret,
587 N_("Error reading kdc_sec in "
588 "cache file: %s", ""),
589 FILENAME(id));
590 goto out;
592 ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
593 if(ret) {
594 ret = KRB5_CC_FORMAT;
595 krb5_set_error_message(context, ret,
596 N_("Error reading kdc_usec in "
597 "cache file: %s", ""),
598 FILENAME(id));
599 goto out;
601 break;
602 default :
603 for (i = 0; i < data_len; ++i) {
604 ret = krb5_ret_int8 (sp, &dummy);
605 if(ret) {
606 ret = KRB5_CC_FORMAT;
607 krb5_set_error_message(context, ret,
608 N_("Error reading unknown "
609 "tag in cache file: %s", ""),
610 FILENAME(id));
611 goto out;
614 break;
616 length -= 4 + data_len;
618 break;
620 case KRB5_FCC_FVNO_3:
621 case KRB5_FCC_FVNO_2:
622 case KRB5_FCC_FVNO_1:
623 break;
624 default :
625 ret = KRB5_CCACHE_BADVNO;
626 krb5_set_error_message(context, ret,
627 N_("Unknown version number (%d) in "
628 "credential cache file: %s", ""),
629 (int)tag, FILENAME(id));
630 goto out;
632 *ret_sp = sp;
633 *ret_fd = fd;
635 return 0;
636 out:
637 if(sp != NULL)
638 krb5_storage_free(sp);
639 fcc_unlock(context, fd);
640 close(fd);
641 return ret;
644 static krb5_error_code
645 fcc_get_principal(krb5_context context,
646 krb5_ccache id,
647 krb5_principal *principal)
649 krb5_error_code ret;
650 int fd;
651 krb5_storage *sp;
653 ret = init_fcc (context, id, &sp, &fd);
654 if (ret)
655 return ret;
656 ret = krb5_ret_principal(sp, principal);
657 if (ret)
658 krb5_clear_error_message(context);
659 krb5_storage_free(sp);
660 fcc_unlock(context, fd);
661 close(fd);
662 return ret;
665 static krb5_error_code
666 fcc_end_get (krb5_context context,
667 krb5_ccache id,
668 krb5_cc_cursor *cursor);
670 static krb5_error_code
671 fcc_get_first (krb5_context context,
672 krb5_ccache id,
673 krb5_cc_cursor *cursor)
675 krb5_error_code ret;
676 krb5_principal principal;
678 *cursor = malloc(sizeof(struct fcc_cursor));
679 if (*cursor == NULL) {
680 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
681 return ENOMEM;
683 memset(*cursor, 0, sizeof(struct fcc_cursor));
685 ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
686 &FCC_CURSOR(*cursor)->fd);
687 if (ret) {
688 free(*cursor);
689 *cursor = NULL;
690 return ret;
692 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
693 if(ret) {
694 krb5_clear_error_message(context);
695 fcc_end_get(context, id, cursor);
696 return ret;
698 krb5_free_principal (context, principal);
699 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
700 return 0;
703 static krb5_error_code
704 fcc_get_next (krb5_context context,
705 krb5_ccache id,
706 krb5_cc_cursor *cursor,
707 krb5_creds *creds)
709 krb5_error_code ret;
710 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
711 return ret;
713 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
714 if (ret)
715 krb5_clear_error_message(context);
717 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
718 return ret;
721 static krb5_error_code
722 fcc_end_get (krb5_context context,
723 krb5_ccache id,
724 krb5_cc_cursor *cursor)
726 krb5_storage_free(FCC_CURSOR(*cursor)->sp);
727 close (FCC_CURSOR(*cursor)->fd);
728 free(*cursor);
729 *cursor = NULL;
730 return 0;
733 static krb5_error_code
734 fcc_remove_cred(krb5_context context,
735 krb5_ccache id,
736 krb5_flags which,
737 krb5_creds *cred)
739 krb5_error_code ret;
740 krb5_ccache copy, newfile;
741 char *newname;
742 int fd;
744 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &copy);
745 if (ret)
746 return ret;
748 ret = krb5_cc_copy_cache(context, id, copy);
749 if (ret) {
750 krb5_cc_destroy(context, copy);
751 return ret;
754 ret = krb5_cc_remove_cred(context, copy, which, cred);
755 if (ret) {
756 krb5_cc_destroy(context, copy);
757 return ret;
760 asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id));
761 if (newname == NULL) {
762 krb5_cc_destroy(context, copy);
763 return ret;
766 fd = mkstemp(&newname[5]);
767 if (fd < 0) {
768 ret = errno;
769 krb5_cc_destroy(context, copy);
770 return ret;
772 close(fd);
774 ret = krb5_cc_resolve(context, newname, &newfile);
775 if (ret) {
776 unlink(&newname[5]);
777 free(newname);
778 krb5_cc_destroy(context, copy);
779 return ret;
782 ret = krb5_cc_copy_cache(context, copy, newfile);
783 krb5_cc_destroy(context, copy);
784 if (ret) {
785 free(newname);
786 krb5_cc_destroy(context, newfile);
787 return ret;
790 ret = rename(&newname[5], FILENAME(id));
791 if (ret)
792 ret = errno;
793 free(newname);
794 krb5_cc_close(context, newfile);
796 return ret;
799 static krb5_error_code
800 fcc_set_flags(krb5_context context,
801 krb5_ccache id,
802 krb5_flags flags)
804 return 0; /* XXX */
807 static int
808 fcc_get_version(krb5_context context,
809 krb5_ccache id)
811 return FCACHE(id)->version;
814 struct fcache_iter {
815 int first;
818 static krb5_error_code
819 fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
821 struct fcache_iter *iter;
823 iter = calloc(1, sizeof(*iter));
824 if (iter == NULL) {
825 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
826 return ENOMEM;
828 iter->first = 1;
829 *cursor = iter;
830 return 0;
833 static krb5_error_code
834 fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
836 struct fcache_iter *iter = cursor;
837 krb5_error_code ret;
838 const char *fn;
839 char *expandedfn = NULL;
841 if (!iter->first) {
842 krb5_clear_error_message(context);
843 return KRB5_CC_END;
845 iter->first = 0;
847 fn = krb5_cc_default_name(context);
848 if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) {
849 ret = _krb5_expand_default_cc_name(context,
850 KRB5_DEFAULT_CCNAME_FILE,
851 &expandedfn);
852 if (ret)
853 return ret;
854 fn = expandedfn;
856 ret = krb5_cc_resolve(context, fn, id);
857 if (expandedfn)
858 free(expandedfn);
860 return ret;
863 static krb5_error_code
864 fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
866 struct fcache_iter *iter = cursor;
867 free(iter);
868 return 0;
871 static krb5_error_code
872 fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
874 krb5_error_code ret = 0;
876 ret = rename(FILENAME(from), FILENAME(to));
877 if (ret && errno != EXDEV) {
878 ret = errno;
879 krb5_set_error_message(context, ret,
880 N_("Rename of file from %s "
881 "to %s failed: %s", ""),
882 FILENAME(from), FILENAME(to),
883 strerror(ret));
884 return ret;
885 } else if (ret && errno == EXDEV) {
886 /* make a copy and delete the orignal */
887 krb5_ssize_t sz1, sz2;
888 int fd1, fd2;
889 char buf[BUFSIZ];
891 ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
892 if(ret)
893 return ret;
895 unlink(FILENAME(to));
897 ret = fcc_open(context, to, &fd2,
898 O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
899 if(ret)
900 goto out1;
902 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
903 sz2 = write(fd2, buf, sz1);
904 if (sz1 != sz2) {
905 ret = EIO;
906 krb5_set_error_message(context, ret,
907 N_("Failed to write data from one file "
908 "credential cache to the other", ""));
909 goto out2;
912 if (sz1 < 0) {
913 ret = EIO;
914 krb5_set_error_message(context, ret,
915 N_("Failed to read data from one file "
916 "credential cache to the other", ""));
917 goto out2;
919 out2:
920 fcc_unlock(context, fd2);
921 close(fd2);
923 out1:
924 fcc_unlock(context, fd1);
925 close(fd1);
927 _krb5_erase_file(context, FILENAME(from));
929 if (ret) {
930 _krb5_erase_file(context, FILENAME(to));
931 return ret;
935 /* make sure ->version is uptodate */
937 krb5_storage *sp;
938 int fd;
939 ret = init_fcc (context, to, &sp, &fd);
940 if (sp)
941 krb5_storage_free(sp);
942 fcc_unlock(context, fd);
943 close(fd);
946 fcc_destroy(context, from);
948 return ret;
951 static krb5_error_code
952 fcc_get_default_name(krb5_context context, char **str)
954 return _krb5_expand_default_cc_name(context,
955 KRB5_DEFAULT_CCNAME_FILE,
956 str);
959 static krb5_error_code
960 fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
962 krb5_error_code ret;
963 struct stat sb;
964 int fd;
966 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
967 if(ret)
968 return ret;
969 ret = fstat(fd, &sb);
970 close(fd);
971 if (ret) {
972 ret = errno;
973 krb5_set_error_message(context, ret, N_("Failed to stat cache file", ""));
974 return ret;
976 *mtime = sb.st_mtime;
977 return 0;
981 * Variable containing the FILE based credential cache implemention.
983 * @ingroup krb5_ccache
986 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = {
987 KRB5_CC_OPS_VERSION,
988 "FILE",
989 fcc_get_name,
990 fcc_resolve,
991 fcc_gen_new,
992 fcc_initialize,
993 fcc_destroy,
994 fcc_close,
995 fcc_store_cred,
996 NULL, /* fcc_retrieve */
997 fcc_get_principal,
998 fcc_get_first,
999 fcc_get_next,
1000 fcc_end_get,
1001 fcc_remove_cred,
1002 fcc_set_flags,
1003 fcc_get_version,
1004 fcc_get_cache_first,
1005 fcc_get_cache_next,
1006 fcc_end_cache_get,
1007 fcc_move,
1008 fcc_get_default_name,
1009 NULL,
1010 fcc_lastchange