Fix memory leak in fcc_move
[heimdal.git] / lib / krb5 / fcache.c
blobf1aeb31b0cab790c61ddeaac3124fdf049295237
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 char buf[128];
100 strerror_r(ret, buf, sizeof(buf));
101 krb5_set_error_message(context, ret,
102 N_("error locking cache file %s: %s",
103 "file, error"), filename, buf);
104 break;
107 return ret;
111 _krb5_xunlock(krb5_context context, int fd)
113 int ret;
114 #ifdef HAVE_FCNTL
115 struct flock l;
116 l.l_start = 0;
117 l.l_len = 0;
118 l.l_type = F_UNLCK;
119 l.l_whence = SEEK_SET;
120 ret = fcntl(fd, F_SETLKW, &l);
121 #else
122 ret = flock(fd, LOCK_UN);
123 #endif
124 if (ret < 0)
125 ret = errno;
126 switch (ret) {
127 case 0:
128 break;
129 case EINVAL: /* filesystem doesn't support locking, let the user have it */
130 ret = 0;
131 break;
132 default: {
133 char buf[128];
134 strerror_r(ret, buf, sizeof(buf));
135 krb5_set_error_message(context, ret,
136 N_("Failed to unlock file: %s", ""), buf);
137 break;
140 return ret;
143 static krb5_error_code
144 write_storage(krb5_context context, krb5_storage *sp, int fd)
146 krb5_error_code ret;
147 krb5_data data;
148 ssize_t sret;
150 ret = krb5_storage_to_data(sp, &data);
151 if (ret) {
152 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
153 return ret;
155 sret = write(fd, data.data, data.length);
156 ret = (sret != data.length);
157 krb5_data_free(&data);
158 if (ret) {
159 ret = errno;
160 krb5_set_error_message(context, ret,
161 N_("Failed to write FILE credential data", ""));
162 return ret;
164 return 0;
168 static krb5_error_code
169 fcc_lock(krb5_context context, krb5_ccache id,
170 int fd, krb5_boolean exclusive)
172 return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
175 static krb5_error_code
176 fcc_unlock(krb5_context context, int fd)
178 return _krb5_xunlock(context, fd);
181 static krb5_error_code
182 fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
184 krb5_fcache *f;
185 f = malloc(sizeof(*f));
186 if(f == NULL) {
187 krb5_set_error_message(context, KRB5_CC_NOMEM,
188 N_("malloc: out of memory", ""));
189 return KRB5_CC_NOMEM;
191 f->filename = strdup(res);
192 if(f->filename == NULL){
193 free(f);
194 krb5_set_error_message(context, KRB5_CC_NOMEM,
195 N_("malloc: out of memory", ""));
196 return KRB5_CC_NOMEM;
198 f->version = 0;
199 (*id)->data.data = f;
200 (*id)->data.length = sizeof(*f);
201 return 0;
205 * Try to scrub the contents of `filename' safely.
208 static int
209 scrub_file (int fd)
211 off_t pos;
212 char buf[128];
214 pos = lseek(fd, 0, SEEK_END);
215 if (pos < 0)
216 return errno;
217 if (lseek(fd, 0, SEEK_SET) < 0)
218 return errno;
219 memset(buf, 0, sizeof(buf));
220 while(pos > 0) {
221 ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
223 if (tmp < 0)
224 return errno;
225 pos -= tmp;
227 fsync (fd);
228 return 0;
232 * Erase `filename' if it exists, trying to remove the contents if
233 * it's `safe'. We always try to remove the file, it it exists. It's
234 * only overwritten if it's a regular file (not a symlink and not a
235 * hardlink)
238 krb5_error_code
239 _krb5_erase_file(krb5_context context, const char *filename)
241 int fd;
242 struct stat sb1, sb2;
243 int ret;
245 ret = lstat (filename, &sb1);
246 if (ret < 0)
247 return errno;
249 fd = open(filename, O_RDWR | O_BINARY);
250 if(fd < 0) {
251 if(errno == ENOENT)
252 return 0;
253 else
254 return errno;
256 rk_cloexec(fd);
257 ret = _krb5_xlock(context, fd, 1, filename);
258 if (ret) {
259 close(fd);
260 return ret;
262 if (unlink(filename) < 0) {
263 _krb5_xunlock(context, fd);
264 close (fd);
265 return errno;
267 ret = fstat (fd, &sb2);
268 if (ret < 0) {
269 _krb5_xunlock(context, fd);
270 close (fd);
271 return errno;
274 /* check if someone was playing with symlinks */
276 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
277 _krb5_xunlock(context, fd);
278 close (fd);
279 return EPERM;
282 /* there are still hard links to this file */
284 if (sb2.st_nlink != 0) {
285 _krb5_xunlock(context, fd);
286 close (fd);
287 return 0;
290 ret = scrub_file (fd);
291 if (ret) {
292 _krb5_xunlock(context, fd);
293 close(fd);
294 return ret;
296 ret = _krb5_xunlock(context, fd);
297 close (fd);
298 return ret;
301 static krb5_error_code
302 fcc_gen_new(krb5_context context, krb5_ccache *id)
304 krb5_fcache *f;
305 int fd;
306 char *file;
308 f = malloc(sizeof(*f));
309 if(f == NULL) {
310 krb5_set_error_message(context, KRB5_CC_NOMEM,
311 N_("malloc: out of memory", ""));
312 return KRB5_CC_NOMEM;
314 asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
315 if(file == NULL) {
316 free(f);
317 krb5_set_error_message(context, KRB5_CC_NOMEM,
318 N_("malloc: out of memory", ""));
319 return KRB5_CC_NOMEM;
321 fd = mkstemp(file);
322 if(fd < 0) {
323 int ret = errno;
324 krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), file);
325 free(f);
326 free(file);
327 return ret;
329 close(fd);
330 f->filename = file;
331 f->version = 0;
332 (*id)->data.data = f;
333 (*id)->data.length = sizeof(*f);
334 return 0;
337 static void
338 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
340 int flags = 0;
341 switch(vno) {
342 case KRB5_FCC_FVNO_1:
343 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
344 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
345 flags |= KRB5_STORAGE_HOST_BYTEORDER;
346 break;
347 case KRB5_FCC_FVNO_2:
348 flags |= KRB5_STORAGE_HOST_BYTEORDER;
349 break;
350 case KRB5_FCC_FVNO_3:
351 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
352 break;
353 case KRB5_FCC_FVNO_4:
354 break;
355 default:
356 krb5_abortx(context,
357 "storage_set_flags called with bad vno (%x)", vno);
359 krb5_storage_set_flags(sp, flags);
362 static krb5_error_code
363 fcc_open(krb5_context context,
364 krb5_ccache id,
365 int *fd_ret,
366 int flags,
367 mode_t mode)
369 krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
370 (flags | O_RDWR) == flags);
371 krb5_error_code ret;
372 const char *filename = FILENAME(id);
373 int fd;
374 fd = open(filename, flags, mode);
375 if(fd < 0) {
376 char buf[128];
377 ret = errno;
378 strerror_r(ret, buf, sizeof(buf));
379 krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"),
380 filename, buf);
381 return ret;
383 rk_cloexec(fd);
385 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
386 close(fd);
387 return ret;
389 *fd_ret = fd;
390 return 0;
393 static krb5_error_code
394 fcc_initialize(krb5_context context,
395 krb5_ccache id,
396 krb5_principal primary_principal)
398 krb5_fcache *f = FCACHE(id);
399 int ret = 0;
400 int fd;
401 char *filename = f->filename;
403 unlink (filename);
405 ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
406 if(ret)
407 return ret;
409 krb5_storage *sp;
410 sp = krb5_storage_emem();
411 krb5_storage_set_eof_code(sp, KRB5_CC_END);
412 if(context->fcache_vno != 0)
413 f->version = context->fcache_vno;
414 else
415 f->version = KRB5_FCC_FVNO_4;
416 ret |= krb5_store_int8(sp, 5);
417 ret |= krb5_store_int8(sp, f->version);
418 storage_set_flags(context, sp, f->version);
419 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
420 /* V4 stuff */
421 if (context->kdc_sec_offset) {
422 ret |= krb5_store_int16 (sp, 12); /* length */
423 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
424 ret |= krb5_store_int16 (sp, 8); /* length of data */
425 ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
426 ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
427 } else {
428 ret |= krb5_store_int16 (sp, 0);
431 ret |= krb5_store_principal(sp, primary_principal);
433 ret |= write_storage(context, sp, fd);
435 krb5_storage_free(sp);
437 fcc_unlock(context, fd);
438 if (close(fd) < 0)
439 if (ret == 0) {
440 char buf[128];
441 ret = errno;
442 strerror_r(ret, buf, sizeof(buf));
443 krb5_set_error_message (context, ret, N_("close %s: %s", ""),
444 FILENAME(id), buf);
446 return ret;
449 static krb5_error_code
450 fcc_close(krb5_context context,
451 krb5_ccache id)
453 free (FILENAME(id));
454 krb5_data_free(&id->data);
455 return 0;
458 static krb5_error_code
459 fcc_destroy(krb5_context context,
460 krb5_ccache id)
462 _krb5_erase_file(context, FILENAME(id));
463 return 0;
466 static krb5_error_code
467 fcc_store_cred(krb5_context context,
468 krb5_ccache id,
469 krb5_creds *creds)
471 int ret;
472 int fd;
474 ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0);
475 if(ret)
476 return ret;
478 krb5_storage *sp;
480 sp = krb5_storage_emem();
481 krb5_storage_set_eof_code(sp, KRB5_CC_END);
482 storage_set_flags(context, sp, FCACHE(id)->version);
483 if (!krb5_config_get_bool_default(context, NULL, TRUE,
484 "libdefaults",
485 "fcc-mit-ticketflags",
486 NULL))
487 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
488 ret = krb5_store_creds(sp, creds);
489 if (ret == 0)
490 ret = write_storage(context, sp, fd);
491 krb5_storage_free(sp);
493 fcc_unlock(context, fd);
494 if (close(fd) < 0) {
495 if (ret == 0) {
496 char buf[128];
497 strerror_r(ret, buf, sizeof(buf));
498 ret = errno;
499 krb5_set_error_message (context, ret, N_("close %s: %s", ""),
500 FILENAME(id), buf);
503 return ret;
506 static krb5_error_code
507 init_fcc (krb5_context context,
508 krb5_ccache id,
509 krb5_storage **ret_sp,
510 int *ret_fd)
512 int fd;
513 int8_t pvno, tag;
514 krb5_storage *sp;
515 krb5_error_code ret;
517 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
518 if(ret)
519 return ret;
521 sp = krb5_storage_from_fd(fd);
522 if(sp == NULL) {
523 krb5_clear_error_message(context);
524 ret = ENOMEM;
525 goto out;
527 krb5_storage_set_eof_code(sp, KRB5_CC_END);
528 ret = krb5_ret_int8(sp, &pvno);
529 if(ret != 0) {
530 if(ret == KRB5_CC_END) {
531 ret = ENOENT;
532 krb5_set_error_message(context, ret,
533 N_("Empty credential cache file: %s", ""),
534 FILENAME(id));
535 } else
536 krb5_set_error_message(context, ret, N_("Error reading pvno "
537 "in cache file: %s", ""),
538 FILENAME(id));
539 goto out;
541 if(pvno != 5) {
542 ret = KRB5_CCACHE_BADVNO;
543 krb5_set_error_message(context, ret, N_("Bad version number in credential "
544 "cache file: %s", ""),
545 FILENAME(id));
546 goto out;
548 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
549 if(ret != 0) {
550 ret = KRB5_CC_FORMAT;
551 krb5_set_error_message(context, ret, "Error reading tag in "
552 "cache file: %s", FILENAME(id));
553 goto out;
555 FCACHE(id)->version = tag;
556 storage_set_flags(context, sp, FCACHE(id)->version);
557 switch (tag) {
558 case KRB5_FCC_FVNO_4: {
559 int16_t length;
561 ret = krb5_ret_int16 (sp, &length);
562 if(ret) {
563 ret = KRB5_CC_FORMAT;
564 krb5_set_error_message(context, ret,
565 N_("Error reading tag length in "
566 "cache file: %s", ""), FILENAME(id));
567 goto out;
569 while(length > 0) {
570 int16_t dtag, data_len;
571 int i;
572 int8_t dummy;
574 ret = krb5_ret_int16 (sp, &dtag);
575 if(ret) {
576 ret = KRB5_CC_FORMAT;
577 krb5_set_error_message(context, ret, N_("Error reading dtag in "
578 "cache file: %s", ""),
579 FILENAME(id));
580 goto out;
582 ret = krb5_ret_int16 (sp, &data_len);
583 if(ret) {
584 ret = KRB5_CC_FORMAT;
585 krb5_set_error_message(context, ret,
586 N_("Error reading dlength "
587 "in cache file: %s",""),
588 FILENAME(id));
589 goto out;
591 switch (dtag) {
592 case FCC_TAG_DELTATIME :
593 ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
594 if(ret) {
595 ret = KRB5_CC_FORMAT;
596 krb5_set_error_message(context, ret,
597 N_("Error reading kdc_sec in "
598 "cache file: %s", ""),
599 FILENAME(id));
600 goto out;
602 ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
603 if(ret) {
604 ret = KRB5_CC_FORMAT;
605 krb5_set_error_message(context, ret,
606 N_("Error reading kdc_usec in "
607 "cache file: %s", ""),
608 FILENAME(id));
609 goto out;
611 break;
612 default :
613 for (i = 0; i < data_len; ++i) {
614 ret = krb5_ret_int8 (sp, &dummy);
615 if(ret) {
616 ret = KRB5_CC_FORMAT;
617 krb5_set_error_message(context, ret,
618 N_("Error reading unknown "
619 "tag in cache file: %s", ""),
620 FILENAME(id));
621 goto out;
624 break;
626 length -= 4 + data_len;
628 break;
630 case KRB5_FCC_FVNO_3:
631 case KRB5_FCC_FVNO_2:
632 case KRB5_FCC_FVNO_1:
633 break;
634 default :
635 ret = KRB5_CCACHE_BADVNO;
636 krb5_set_error_message(context, ret,
637 N_("Unknown version number (%d) in "
638 "credential cache file: %s", ""),
639 (int)tag, FILENAME(id));
640 goto out;
642 *ret_sp = sp;
643 *ret_fd = fd;
645 return 0;
646 out:
647 if(sp != NULL)
648 krb5_storage_free(sp);
649 fcc_unlock(context, fd);
650 close(fd);
651 return ret;
654 static krb5_error_code
655 fcc_get_principal(krb5_context context,
656 krb5_ccache id,
657 krb5_principal *principal)
659 krb5_error_code ret;
660 int fd;
661 krb5_storage *sp;
663 ret = init_fcc (context, id, &sp, &fd);
664 if (ret)
665 return ret;
666 ret = krb5_ret_principal(sp, principal);
667 if (ret)
668 krb5_clear_error_message(context);
669 krb5_storage_free(sp);
670 fcc_unlock(context, fd);
671 close(fd);
672 return ret;
675 static krb5_error_code
676 fcc_end_get (krb5_context context,
677 krb5_ccache id,
678 krb5_cc_cursor *cursor);
680 static krb5_error_code
681 fcc_get_first (krb5_context context,
682 krb5_ccache id,
683 krb5_cc_cursor *cursor)
685 krb5_error_code ret;
686 krb5_principal principal;
688 *cursor = malloc(sizeof(struct fcc_cursor));
689 if (*cursor == NULL) {
690 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
691 return ENOMEM;
693 memset(*cursor, 0, sizeof(struct fcc_cursor));
695 ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
696 &FCC_CURSOR(*cursor)->fd);
697 if (ret) {
698 free(*cursor);
699 *cursor = NULL;
700 return ret;
702 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
703 if(ret) {
704 krb5_clear_error_message(context);
705 fcc_end_get(context, id, cursor);
706 return ret;
708 krb5_free_principal (context, principal);
709 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
710 return 0;
713 static krb5_error_code
714 fcc_get_next (krb5_context context,
715 krb5_ccache id,
716 krb5_cc_cursor *cursor,
717 krb5_creds *creds)
719 krb5_error_code ret;
720 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
721 return ret;
723 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
724 if (ret)
725 krb5_clear_error_message(context);
727 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
728 return ret;
731 static krb5_error_code
732 fcc_end_get (krb5_context context,
733 krb5_ccache id,
734 krb5_cc_cursor *cursor)
736 krb5_storage_free(FCC_CURSOR(*cursor)->sp);
737 close (FCC_CURSOR(*cursor)->fd);
738 free(*cursor);
739 *cursor = NULL;
740 return 0;
743 static krb5_error_code
744 fcc_remove_cred(krb5_context context,
745 krb5_ccache id,
746 krb5_flags which,
747 krb5_creds *cred)
749 krb5_error_code ret;
750 krb5_ccache copy, newfile;
751 char *newname;
752 int fd;
754 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &copy);
755 if (ret)
756 return ret;
758 ret = krb5_cc_copy_cache(context, id, copy);
759 if (ret) {
760 krb5_cc_destroy(context, copy);
761 return ret;
764 ret = krb5_cc_remove_cred(context, copy, which, cred);
765 if (ret) {
766 krb5_cc_destroy(context, copy);
767 return ret;
770 asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id));
771 if (newname == NULL) {
772 krb5_cc_destroy(context, copy);
773 return ret;
776 fd = mkstemp(&newname[5]);
777 if (fd < 0) {
778 ret = errno;
779 krb5_cc_destroy(context, copy);
780 return ret;
782 close(fd);
784 ret = krb5_cc_resolve(context, newname, &newfile);
785 if (ret) {
786 unlink(&newname[5]);
787 free(newname);
788 krb5_cc_destroy(context, copy);
789 return ret;
792 ret = krb5_cc_copy_cache(context, copy, newfile);
793 krb5_cc_destroy(context, copy);
794 if (ret) {
795 free(newname);
796 krb5_cc_destroy(context, newfile);
797 return ret;
800 ret = rename(&newname[5], FILENAME(id));
801 if (ret)
802 ret = errno;
803 free(newname);
804 krb5_cc_close(context, newfile);
806 return ret;
809 static krb5_error_code
810 fcc_set_flags(krb5_context context,
811 krb5_ccache id,
812 krb5_flags flags)
814 return 0; /* XXX */
817 static int
818 fcc_get_version(krb5_context context,
819 krb5_ccache id)
821 return FCACHE(id)->version;
824 struct fcache_iter {
825 int first;
828 static krb5_error_code
829 fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
831 struct fcache_iter *iter;
833 iter = calloc(1, sizeof(*iter));
834 if (iter == NULL) {
835 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
836 return ENOMEM;
838 iter->first = 1;
839 *cursor = iter;
840 return 0;
843 static krb5_error_code
844 fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
846 struct fcache_iter *iter = cursor;
847 krb5_error_code ret;
848 const char *fn;
849 char *expandedfn = NULL;
851 if (!iter->first) {
852 krb5_clear_error_message(context);
853 return KRB5_CC_END;
855 iter->first = 0;
857 fn = krb5_cc_default_name(context);
858 if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) {
859 ret = _krb5_expand_default_cc_name(context,
860 KRB5_DEFAULT_CCNAME_FILE,
861 &expandedfn);
862 if (ret)
863 return ret;
864 fn = expandedfn;
866 ret = krb5_cc_resolve(context, fn, id);
867 if (expandedfn)
868 free(expandedfn);
870 return ret;
873 static krb5_error_code
874 fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
876 struct fcache_iter *iter = cursor;
877 free(iter);
878 return 0;
881 static krb5_error_code
882 fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
884 krb5_error_code ret = 0;
886 ret = rename(FILENAME(from), FILENAME(to));
887 if (ret && errno != EXDEV) {
888 char buf[128];
889 ret = errno;
890 strerror_r(ret, buf, sizeof(buf));
891 krb5_set_error_message(context, ret,
892 N_("Rename of file from %s "
893 "to %s failed: %s", ""),
894 FILENAME(from), FILENAME(to), buf);
895 return ret;
896 } else if (ret && errno == EXDEV) {
897 /* make a copy and delete the orignal */
898 krb5_ssize_t sz1, sz2;
899 int fd1, fd2;
900 char buf[BUFSIZ];
902 ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
903 if(ret)
904 return ret;
906 unlink(FILENAME(to));
908 ret = fcc_open(context, to, &fd2,
909 O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
910 if(ret)
911 goto out1;
913 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
914 sz2 = write(fd2, buf, sz1);
915 if (sz1 != sz2) {
916 ret = EIO;
917 krb5_set_error_message(context, ret,
918 N_("Failed to write data from one file "
919 "credential cache to the other", ""));
920 goto out2;
923 if (sz1 < 0) {
924 ret = EIO;
925 krb5_set_error_message(context, ret,
926 N_("Failed to read data from one file "
927 "credential cache to the other", ""));
928 goto out2;
930 out2:
931 fcc_unlock(context, fd2);
932 close(fd2);
934 out1:
935 fcc_unlock(context, fd1);
936 close(fd1);
938 _krb5_erase_file(context, FILENAME(from));
940 if (ret) {
941 _krb5_erase_file(context, FILENAME(to));
942 return ret;
946 /* make sure ->version is uptodate */
948 krb5_storage *sp;
949 int fd;
950 ret = init_fcc (context, to, &sp, &fd);
951 if (sp)
952 krb5_storage_free(sp);
953 fcc_unlock(context, fd);
954 close(fd);
957 fcc_close(context, from);
959 return ret;
962 static krb5_error_code
963 fcc_get_default_name(krb5_context context, char **str)
965 return _krb5_expand_default_cc_name(context,
966 KRB5_DEFAULT_CCNAME_FILE,
967 str);
970 static krb5_error_code
971 fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
973 krb5_error_code ret;
974 struct stat sb;
975 int fd;
977 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
978 if(ret)
979 return ret;
980 ret = fstat(fd, &sb);
981 close(fd);
982 if (ret) {
983 ret = errno;
984 krb5_set_error_message(context, ret, N_("Failed to stat cache file", ""));
985 return ret;
987 *mtime = sb.st_mtime;
988 return 0;
992 * Variable containing the FILE based credential cache implemention.
994 * @ingroup krb5_ccache
997 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = {
998 KRB5_CC_OPS_VERSION,
999 "FILE",
1000 fcc_get_name,
1001 fcc_resolve,
1002 fcc_gen_new,
1003 fcc_initialize,
1004 fcc_destroy,
1005 fcc_close,
1006 fcc_store_cred,
1007 NULL, /* fcc_retrieve */
1008 fcc_get_principal,
1009 fcc_get_first,
1010 fcc_get_next,
1011 fcc_end_get,
1012 fcc_remove_cred,
1013 fcc_set_flags,
1014 fcc_get_version,
1015 fcc_get_cache_first,
1016 fcc_get_cache_next,
1017 fcc_end_cache_get,
1018 fcc_move,
1019 fcc_get_default_name,
1020 NULL,
1021 fcc_lastchange