use DES_set_key_unchecked().
[heimdal.git] / lib / krb5 / fcache.c
blob74b12b21b7411dd564e49447b1433ec7962b647d
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 RCSID("$Id$");
38 typedef struct krb5_fcache{
39 char *filename;
40 int version;
41 }krb5_fcache;
43 struct fcc_cursor {
44 int fd;
45 krb5_storage *sp;
48 #define KRB5_FCC_FVNO_1 1
49 #define KRB5_FCC_FVNO_2 2
50 #define KRB5_FCC_FVNO_3 3
51 #define KRB5_FCC_FVNO_4 4
53 #define FCC_TAG_DELTATIME 1
55 #define FCACHE(X) ((krb5_fcache*)(X)->data.data)
57 #define FILENAME(X) (FCACHE(X)->filename)
59 #define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
61 static const char*
62 fcc_get_name(krb5_context context,
63 krb5_ccache id)
65 return FILENAME(id);
68 int
69 _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
70 const char *filename)
72 int ret;
73 #ifdef HAVE_FCNTL
74 struct flock l;
76 l.l_start = 0;
77 l.l_len = 0;
78 l.l_type = exclusive ? F_WRLCK : F_RDLCK;
79 l.l_whence = SEEK_SET;
80 ret = fcntl(fd, F_SETLKW, &l);
81 #else
82 ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH);
83 #endif
84 if(ret < 0)
85 ret = errno;
86 if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */
87 ret = EAGAIN;
89 switch (ret) {
90 case 0:
91 break;
92 case EINVAL: /* filesystem doesn't support locking, let the user have it */
93 ret = 0;
94 break;
95 case EAGAIN:
96 krb5_set_error_string(context, "timed out locking cache file %s",
97 filename);
98 break;
99 default:
100 krb5_set_error_string(context, "error locking cache file %s: %s",
101 filename, strerror(ret));
102 break;
104 return ret;
108 _krb5_xunlock(krb5_context context, int fd)
110 int ret;
111 #ifdef HAVE_FCNTL
112 struct flock l;
113 l.l_start = 0;
114 l.l_len = 0;
115 l.l_type = F_UNLCK;
116 l.l_whence = SEEK_SET;
117 ret = fcntl(fd, F_SETLKW, &l);
118 #else
119 ret = flock(fd, LOCK_UN);
120 #endif
121 if (ret < 0)
122 ret = errno;
123 switch (ret) {
124 case 0:
125 break;
126 case EINVAL: /* filesystem doesn't support locking, let the user have it */
127 ret = 0;
128 break;
129 default:
130 krb5_set_error_string(context,
131 "Failed to unlock file: %s", strerror(ret));
132 break;
134 return ret;
137 static krb5_error_code
138 write_storage(krb5_context context, krb5_storage *sp, int fd)
140 krb5_error_code ret;
141 krb5_data data;
142 ssize_t sret;
144 ret = krb5_storage_to_data(sp, &data);
145 if (ret) {
146 krb5_set_error_string(context, "malloc: out of memory");
147 return ret;
149 sret = write(fd, data.data, data.length);
150 ret = (sret != data.length);
151 krb5_data_free(&data);
152 if (ret) {
153 ret = errno;
154 krb5_set_error_string(context, "Failed to write FILE credential data");
155 return ret;
157 return 0;
161 static krb5_error_code
162 fcc_lock(krb5_context context, krb5_ccache id,
163 int fd, krb5_boolean exclusive)
165 return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
168 static krb5_error_code
169 fcc_unlock(krb5_context context, int fd)
171 return _krb5_xunlock(context, fd);
174 static krb5_error_code
175 fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
177 krb5_fcache *f;
178 f = malloc(sizeof(*f));
179 if(f == NULL) {
180 krb5_set_error_string(context, "malloc: out of memory");
181 return KRB5_CC_NOMEM;
183 f->filename = strdup(res);
184 if(f->filename == NULL){
185 free(f);
186 krb5_set_error_string(context, "malloc: out of memory");
187 return KRB5_CC_NOMEM;
189 f->version = 0;
190 (*id)->data.data = f;
191 (*id)->data.length = sizeof(*f);
192 return 0;
196 * Try to scrub the contents of `filename' safely.
199 static int
200 scrub_file (int fd)
202 off_t pos;
203 char buf[128];
205 pos = lseek(fd, 0, SEEK_END);
206 if (pos < 0)
207 return errno;
208 if (lseek(fd, 0, SEEK_SET) < 0)
209 return errno;
210 memset(buf, 0, sizeof(buf));
211 while(pos > 0) {
212 ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
214 if (tmp < 0)
215 return errno;
216 pos -= tmp;
218 fsync (fd);
219 return 0;
223 * Erase `filename' if it exists, trying to remove the contents if
224 * it's `safe'. We always try to remove the file, it it exists. It's
225 * only overwritten if it's a regular file (not a symlink and not a
226 * hardlink)
229 static krb5_error_code
230 erase_file(krb5_context context, const char *filename)
232 int fd;
233 struct stat sb1, sb2;
234 int ret;
236 ret = lstat (filename, &sb1);
237 if (ret < 0)
238 return errno;
240 fd = open(filename, O_RDWR | O_BINARY);
241 if(fd < 0) {
242 if(errno == ENOENT)
243 return 0;
244 else
245 return errno;
247 ret = _krb5_xlock(context, fd, 1, filename);
248 if (ret) {
249 close(fd);
250 return ret;
252 if (unlink(filename) < 0) {
253 _krb5_xunlock(context, fd);
254 close (fd);
255 return errno;
257 ret = fstat (fd, &sb2);
258 if (ret < 0) {
259 _krb5_xunlock(context, fd);
260 close (fd);
261 return errno;
264 /* check if someone was playing with symlinks */
266 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
267 _krb5_xunlock(context, fd);
268 close (fd);
269 return EPERM;
272 /* there are still hard links to this file */
274 if (sb2.st_nlink != 0) {
275 _krb5_xunlock(context, fd);
276 close (fd);
277 return 0;
280 ret = scrub_file (fd);
281 if (ret) {
282 _krb5_xunlock(context, fd);
283 close(fd);
284 return ret;
286 ret = _krb5_xunlock(context, fd);
287 close (fd);
288 return ret;
291 static krb5_error_code
292 fcc_gen_new(krb5_context context, krb5_ccache *id)
294 krb5_fcache *f;
295 int fd;
296 char *file;
298 f = malloc(sizeof(*f));
299 if(f == NULL) {
300 krb5_set_error_string(context, "malloc: out of memory");
301 return KRB5_CC_NOMEM;
303 asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
304 if(file == NULL) {
305 free(f);
306 krb5_set_error_string(context, "malloc: out of memory");
307 return KRB5_CC_NOMEM;
309 fd = mkstemp(file);
310 if(fd < 0) {
311 int ret = errno;
312 krb5_set_error_string(context, "mkstemp %s", file);
313 free(f);
314 free(file);
315 return ret;
317 close(fd);
318 f->filename = file;
319 f->version = 0;
320 (*id)->data.data = f;
321 (*id)->data.length = sizeof(*f);
322 return 0;
325 static void
326 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
328 int flags = 0;
329 switch(vno) {
330 case KRB5_FCC_FVNO_1:
331 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
332 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
333 flags |= KRB5_STORAGE_HOST_BYTEORDER;
334 break;
335 case KRB5_FCC_FVNO_2:
336 flags |= KRB5_STORAGE_HOST_BYTEORDER;
337 break;
338 case KRB5_FCC_FVNO_3:
339 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
340 break;
341 case KRB5_FCC_FVNO_4:
342 break;
343 default:
344 krb5_abortx(context,
345 "storage_set_flags called with bad vno (%x)", vno);
347 krb5_storage_set_flags(sp, flags);
350 static krb5_error_code
351 fcc_open(krb5_context context,
352 krb5_ccache id,
353 int *fd_ret,
354 int flags,
355 mode_t mode)
357 krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
358 (flags | O_RDWR) == flags);
359 krb5_error_code ret;
360 const char *filename = FILENAME(id);
361 int fd;
362 fd = open(filename, flags, mode);
363 if(fd < 0) {
364 ret = errno;
365 krb5_set_error_string(context, "open(%s): %s", filename,
366 strerror(ret));
367 return ret;
370 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
371 close(fd);
372 return ret;
374 *fd_ret = fd;
375 return 0;
378 static krb5_error_code
379 fcc_initialize(krb5_context context,
380 krb5_ccache id,
381 krb5_principal primary_principal)
383 krb5_fcache *f = FCACHE(id);
384 int ret = 0;
385 int fd;
386 char *filename = f->filename;
388 unlink (filename);
390 ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
391 if(ret)
392 return ret;
394 krb5_storage *sp;
395 sp = krb5_storage_emem();
396 krb5_storage_set_eof_code(sp, KRB5_CC_END);
397 if(context->fcache_vno != 0)
398 f->version = context->fcache_vno;
399 else
400 f->version = KRB5_FCC_FVNO_4;
401 ret |= krb5_store_int8(sp, 5);
402 ret |= krb5_store_int8(sp, f->version);
403 storage_set_flags(context, sp, f->version);
404 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
405 /* V4 stuff */
406 if (context->kdc_sec_offset) {
407 ret |= krb5_store_int16 (sp, 12); /* length */
408 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
409 ret |= krb5_store_int16 (sp, 8); /* length of data */
410 ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
411 ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
412 } else {
413 ret |= krb5_store_int16 (sp, 0);
416 ret |= krb5_store_principal(sp, primary_principal);
418 ret |= write_storage(context, sp, fd);
420 krb5_storage_free(sp);
422 fcc_unlock(context, fd);
423 if (close(fd) < 0)
424 if (ret == 0) {
425 ret = errno;
426 krb5_set_error_string (context, "close %s: %s",
427 FILENAME(id), strerror(ret));
429 return ret;
432 static krb5_error_code
433 fcc_close(krb5_context context,
434 krb5_ccache id)
436 free (FILENAME(id));
437 krb5_data_free(&id->data);
438 return 0;
441 static krb5_error_code
442 fcc_destroy(krb5_context context,
443 krb5_ccache id)
445 erase_file(context, FILENAME(id));
446 return 0;
449 static krb5_error_code
450 fcc_store_cred(krb5_context context,
451 krb5_ccache id,
452 krb5_creds *creds)
454 int ret;
455 int fd;
457 ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY, 0);
458 if(ret)
459 return ret;
461 krb5_storage *sp;
463 sp = krb5_storage_emem();
464 krb5_storage_set_eof_code(sp, KRB5_CC_END);
465 storage_set_flags(context, sp, FCACHE(id)->version);
466 if (!krb5_config_get_bool_default(context, NULL, TRUE,
467 "libdefaults",
468 "fcc-mit-ticketflags",
469 NULL))
470 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
471 ret = krb5_store_creds(sp, creds);
472 if (ret == 0)
473 ret = write_storage(context, sp, fd);
474 krb5_storage_free(sp);
476 fcc_unlock(context, fd);
477 if (close(fd) < 0) {
478 if (ret == 0) {
479 ret = errno;
480 krb5_set_error_string (context, "close %s: %s",
481 FILENAME(id), strerror(ret));
484 return ret;
487 static krb5_error_code
488 init_fcc (krb5_context context,
489 krb5_ccache id,
490 krb5_storage **ret_sp,
491 int *ret_fd)
493 int fd;
494 int8_t pvno, tag;
495 krb5_storage *sp;
496 krb5_error_code ret;
498 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY, 0);
499 if(ret)
500 return ret;
502 sp = krb5_storage_from_fd(fd);
503 if(sp == NULL) {
504 krb5_clear_error_string(context);
505 ret = ENOMEM;
506 goto out;
508 krb5_storage_set_eof_code(sp, KRB5_CC_END);
509 ret = krb5_ret_int8(sp, &pvno);
510 if(ret != 0) {
511 if(ret == KRB5_CC_END) {
512 krb5_set_error_string(context, "Empty credential cache file: %s",
513 FILENAME(id));
514 ret = ENOENT;
515 } else
516 krb5_set_error_string(context, "Error reading pvno in "
517 "cache file: %s", FILENAME(id));
518 goto out;
520 if(pvno != 5) {
521 krb5_set_error_string(context, "Bad version number in credential "
522 "cache file: %s", FILENAME(id));
523 ret = KRB5_CCACHE_BADVNO;
524 goto out;
526 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
527 if(ret != 0) {
528 krb5_set_error_string(context, "Error reading tag in "
529 "cache file: %s", FILENAME(id));
530 ret = KRB5_CC_FORMAT;
531 goto out;
533 FCACHE(id)->version = tag;
534 storage_set_flags(context, sp, FCACHE(id)->version);
535 switch (tag) {
536 case KRB5_FCC_FVNO_4: {
537 int16_t length;
539 ret = krb5_ret_int16 (sp, &length);
540 if(ret) {
541 ret = KRB5_CC_FORMAT;
542 krb5_set_error_string(context, "Error reading tag length in "
543 "cache file: %s", FILENAME(id));
544 goto out;
546 while(length > 0) {
547 int16_t dtag, data_len;
548 int i;
549 int8_t dummy;
551 ret = krb5_ret_int16 (sp, &dtag);
552 if(ret) {
553 krb5_set_error_string(context, "Error reading dtag in "
554 "cache file: %s", FILENAME(id));
555 ret = KRB5_CC_FORMAT;
556 goto out;
558 ret = krb5_ret_int16 (sp, &data_len);
559 if(ret) {
560 krb5_set_error_string(context, "Error reading dlength in "
561 "cache file: %s", FILENAME(id));
562 ret = KRB5_CC_FORMAT;
563 goto out;
565 switch (dtag) {
566 case FCC_TAG_DELTATIME :
567 ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
568 if(ret) {
569 krb5_set_error_string(context, "Error reading kdc_sec in "
570 "cache file: %s", FILENAME(id));
571 ret = KRB5_CC_FORMAT;
572 goto out;
574 ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
575 if(ret) {
576 krb5_set_error_string(context, "Error reading kdc_usec in "
577 "cache file: %s", FILENAME(id));
578 ret = KRB5_CC_FORMAT;
579 goto out;
581 break;
582 default :
583 for (i = 0; i < data_len; ++i) {
584 ret = krb5_ret_int8 (sp, &dummy);
585 if(ret) {
586 krb5_set_error_string(context, "Error reading unknown "
587 "tag in cache file: %s",
588 FILENAME(id));
589 ret = KRB5_CC_FORMAT;
590 goto out;
593 break;
595 length -= 4 + data_len;
597 break;
599 case KRB5_FCC_FVNO_3:
600 case KRB5_FCC_FVNO_2:
601 case KRB5_FCC_FVNO_1:
602 break;
603 default :
604 ret = KRB5_CCACHE_BADVNO;
605 krb5_set_error_string(context, "Unknown version number (%d) in "
606 "credential cache file: %s",
607 (int)tag, FILENAME(id));
608 goto out;
610 *ret_sp = sp;
611 *ret_fd = fd;
613 return 0;
614 out:
615 if(sp != NULL)
616 krb5_storage_free(sp);
617 fcc_unlock(context, fd);
618 close(fd);
619 return ret;
622 static krb5_error_code
623 fcc_get_principal(krb5_context context,
624 krb5_ccache id,
625 krb5_principal *principal)
627 krb5_error_code ret;
628 int fd;
629 krb5_storage *sp;
631 ret = init_fcc (context, id, &sp, &fd);
632 if (ret)
633 return ret;
634 ret = krb5_ret_principal(sp, principal);
635 if (ret)
636 krb5_clear_error_string(context);
637 krb5_storage_free(sp);
638 fcc_unlock(context, fd);
639 close(fd);
640 return ret;
643 static krb5_error_code
644 fcc_end_get (krb5_context context,
645 krb5_ccache id,
646 krb5_cc_cursor *cursor);
648 static krb5_error_code
649 fcc_get_first (krb5_context context,
650 krb5_ccache id,
651 krb5_cc_cursor *cursor)
653 krb5_error_code ret;
654 krb5_principal principal;
656 *cursor = malloc(sizeof(struct fcc_cursor));
657 if (*cursor == NULL) {
658 krb5_set_error_string (context, "malloc: out of memory");
659 return ENOMEM;
661 memset(*cursor, 0, sizeof(struct fcc_cursor));
663 ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
664 &FCC_CURSOR(*cursor)->fd);
665 if (ret) {
666 free(*cursor);
667 *cursor = NULL;
668 return ret;
670 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
671 if(ret) {
672 krb5_clear_error_string(context);
673 fcc_end_get(context, id, cursor);
674 return ret;
676 krb5_free_principal (context, principal);
677 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
678 return 0;
681 static krb5_error_code
682 fcc_get_next (krb5_context context,
683 krb5_ccache id,
684 krb5_cc_cursor *cursor,
685 krb5_creds *creds)
687 krb5_error_code ret;
688 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
689 return ret;
691 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
692 if (ret)
693 krb5_clear_error_string(context);
695 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
696 return ret;
699 static krb5_error_code
700 fcc_end_get (krb5_context context,
701 krb5_ccache id,
702 krb5_cc_cursor *cursor)
704 krb5_storage_free(FCC_CURSOR(*cursor)->sp);
705 close (FCC_CURSOR(*cursor)->fd);
706 free(*cursor);
707 *cursor = NULL;
708 return 0;
711 static krb5_error_code
712 fcc_remove_cred(krb5_context context,
713 krb5_ccache id,
714 krb5_flags which,
715 krb5_creds *cred)
717 krb5_error_code ret;
718 krb5_ccache copy, newfile;
720 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &copy);
721 if (ret)
722 return ret;
724 ret = krb5_cc_copy_cache(context, id, copy);
725 if (ret) {
726 krb5_cc_destroy(context, copy);
727 return ret;
730 ret = krb5_cc_remove_cred(context, copy, which, cred);
731 if (ret) {
732 krb5_cc_destroy(context, copy);
733 return ret;
736 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &newfile);
737 if (ret) {
738 krb5_cc_destroy(context, copy);
739 return ret;
742 ret = krb5_cc_copy_cache(context, copy, newfile);
743 krb5_cc_destroy(context, copy);
744 if (ret) {
745 krb5_cc_destroy(context, newfile);
746 return ret;
749 return krb5_cc_move(context, newfile, id);
752 static krb5_error_code
753 fcc_set_flags(krb5_context context,
754 krb5_ccache id,
755 krb5_flags flags)
757 return 0; /* XXX */
760 static int
761 fcc_get_version(krb5_context context,
762 krb5_ccache id)
764 return FCACHE(id)->version;
767 struct fcache_iter {
768 int first;
771 static krb5_error_code
772 fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
774 struct fcache_iter *iter;
776 iter = calloc(1, sizeof(*iter));
777 if (iter == NULL) {
778 krb5_set_error_string(context, "malloc - out of memory");
779 return ENOMEM;
781 iter->first = 1;
782 *cursor = iter;
783 return 0;
786 static krb5_error_code
787 fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
789 struct fcache_iter *iter = cursor;
790 krb5_error_code ret;
791 const char *fn;
792 char *expandedfn = NULL;
794 if (!iter->first) {
795 krb5_clear_error_string(context);
796 return KRB5_CC_END;
798 iter->first = 0;
800 fn = krb5_cc_default_name(context);
801 if (strncasecmp(fn, "FILE:", 5) != 0) {
802 ret = _krb5_expand_default_cc_name(context,
803 KRB5_DEFAULT_CCNAME_FILE,
804 &expandedfn);
805 if (ret)
806 return ret;
808 ret = krb5_cc_resolve(context, fn, id);
809 if (expandedfn)
810 free(expandedfn);
812 return ret;
815 static krb5_error_code
816 fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
818 struct fcache_iter *iter = cursor;
819 free(iter);
820 return 0;
823 static krb5_error_code
824 fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
826 krb5_error_code ret = 0;
828 ret = rename(FILENAME(from), FILENAME(to));
829 if (ret && errno != EXDEV) {
830 ret = errno;
831 krb5_set_error_string(context,
832 "Rename of file from %s to %s failed: %s",
833 FILENAME(from), FILENAME(to),
834 strerror(ret));
835 return ret;
836 } else if (ret && errno == EXDEV) {
837 /* make a copy and delete the orignal */
838 krb5_ssize_t sz1, sz2;
839 int fd1, fd2;
840 char buf[BUFSIZ];
842 ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY, 0);
843 if(ret)
844 return ret;
846 unlink(FILENAME(to));
848 ret = fcc_open(context, to, &fd2,
849 O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600);
850 if(ret)
851 goto out1;
853 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
854 sz2 = write(fd2, buf, sz1);
855 if (sz1 != sz2) {
856 ret = EIO;
857 krb5_set_error_string(context,
858 "Failed to write data from one file "
859 "credential cache to the other");
860 goto out2;
863 if (sz1 < 0) {
864 ret = EIO;
865 krb5_set_error_string(context,
866 "Failed to read data from one file "
867 "credential cache to the other");
868 goto out2;
870 out2:
871 fcc_unlock(context, fd2);
872 close(fd2);
874 out1:
875 fcc_unlock(context, fd1);
876 close(fd1);
878 erase_file(context, FILENAME(from));
880 if (ret) {
881 erase_file(context, FILENAME(to));
882 return ret;
886 /* make sure ->version is uptodate */
888 krb5_storage *sp;
889 int fd;
890 ret = init_fcc (context, to, &sp, &fd);
891 krb5_storage_free(sp);
892 fcc_unlock(context, fd);
893 close(fd);
895 return ret;
898 static krb5_error_code
899 fcc_default_name(krb5_context context, char **str)
901 return _krb5_expand_default_cc_name(context,
902 KRB5_DEFAULT_CCNAME_FILE,
903 str);
907 * Variable containing the FILE based credential cache implemention.
909 * @ingroup krb5_ccache
912 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = {
913 KRB5_CC_OPS_VERSION,
914 "FILE",
915 fcc_get_name,
916 fcc_resolve,
917 fcc_gen_new,
918 fcc_initialize,
919 fcc_destroy,
920 fcc_close,
921 fcc_store_cred,
922 NULL, /* fcc_retrieve */
923 fcc_get_principal,
924 fcc_get_first,
925 fcc_get_next,
926 fcc_end_get,
927 fcc_remove_cred,
928 fcc_set_flags,
929 fcc_get_version,
930 fcc_get_cache_first,
931 fcc_get_cache_next,
932 fcc_end_cache_get,
933 fcc_move,
934 fcc_default_name