source: heimdal/trunk/lib/krb5/fcache.c

Last change on this file was 4, checked in by Paul Smedley, 10 years ago

heimdal: applied os2 patches, added conf.cmd

File size: 26.5 KB
Line 
1/*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "krb5_locl.h"
37
38typedef struct krb5_fcache{
39 char *filename;
40 int version;
41}krb5_fcache;
42
43struct fcc_cursor {
44 int fd;
45 krb5_storage *sp;
46};
47
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
52
53#define FCC_TAG_DELTATIME 1
54
55#define FCACHE(X) ((krb5_fcache*)(X)->data.data)
56
57#define FILENAME(X) (FCACHE(X)->filename)
58
59#define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
60
61static const char* KRB5_CALLCONV
62fcc_get_name(krb5_context context,
63 krb5_ccache id)
64{
65 if (FCACHE(id) == NULL)
66 return NULL;
67
68 return FILENAME(id);
69}
70
71int
72_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
73 const char *filename)
74{
75 int ret;
76#if defined(HAVE_FCNTL) && !defined(__OS2__)
77 struct flock l;
78
79 l.l_start = 0;
80 l.l_len = 0;
81 l.l_type = exclusive ? F_WRLCK : F_RDLCK;
82 l.l_whence = SEEK_SET;
83 ret = fcntl(fd, F_SETLKW, &l);
84#else
85 ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH);
86#endif
87 if(ret < 0)
88 ret = errno;
89 if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */
90 ret = EAGAIN;
91
92 switch (ret) {
93 case 0:
94 break;
95 case EINVAL: /* filesystem doesn't support locking, let the user have it */
96 ret = 0;
97 break;
98 case EAGAIN:
99 krb5_set_error_message(context, ret,
100 N_("timed out locking cache file %s", "file"),
101 filename);
102 break;
103 default: {
104 char buf[128];
105 rk_strerror_r(ret, buf, sizeof(buf));
106 krb5_set_error_message(context, ret,
107 N_("error locking cache file %s: %s",
108 "file, error"), filename, buf);
109 break;
110 }
111 }
112 return ret;
113}
114
115int
116_krb5_xunlock(krb5_context context, int fd)
117{
118 int ret;
119#if defined(HAVE_FCNTL) && !defined(__OS2__)
120 struct flock l;
121 l.l_start = 0;
122 l.l_len = 0;
123 l.l_type = F_UNLCK;
124 l.l_whence = SEEK_SET;
125 ret = fcntl(fd, F_SETLKW, &l);
126#else
127 ret = flock(fd, LOCK_UN);
128#endif
129 if (ret < 0)
130 ret = errno;
131 switch (ret) {
132 case 0:
133 break;
134 case EINVAL: /* filesystem doesn't support locking, let the user have it */
135 ret = 0;
136 break;
137 default: {
138 char buf[128];
139 rk_strerror_r(ret, buf, sizeof(buf));
140 krb5_set_error_message(context, ret,
141 N_("Failed to unlock file: %s", ""), buf);
142 break;
143 }
144 }
145 return ret;
146}
147
148static krb5_error_code
149write_storage(krb5_context context, krb5_storage *sp, int fd)
150{
151 krb5_error_code ret;
152 krb5_data data;
153 ssize_t sret;
154
155 ret = krb5_storage_to_data(sp, &data);
156 if (ret) {
157 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
158 return ret;
159 }
160 sret = write(fd, data.data, data.length);
161 ret = (sret != (ssize_t)data.length);
162 krb5_data_free(&data);
163 if (ret) {
164 ret = errno;
165 krb5_set_error_message(context, ret,
166 N_("Failed to write FILE credential data", ""));
167 return ret;
168 }
169 return 0;
170}
171
172
173static krb5_error_code KRB5_CALLCONV
174fcc_lock(krb5_context context, krb5_ccache id,
175 int fd, krb5_boolean exclusive)
176{
177 return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
178}
179
180static krb5_error_code KRB5_CALLCONV
181fcc_unlock(krb5_context context, int fd)
182{
183 return _krb5_xunlock(context, fd);
184}
185
186static krb5_error_code KRB5_CALLCONV
187fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
188{
189 krb5_fcache *f;
190 f = malloc(sizeof(*f));
191 if(f == NULL) {
192 krb5_set_error_message(context, KRB5_CC_NOMEM,
193 N_("malloc: out of memory", ""));
194 return KRB5_CC_NOMEM;
195 }
196 f->filename = strdup(res);
197 if(f->filename == NULL){
198 free(f);
199 krb5_set_error_message(context, KRB5_CC_NOMEM,
200 N_("malloc: out of memory", ""));
201 return KRB5_CC_NOMEM;
202 }
203 f->version = 0;
204 (*id)->data.data = f;
205 (*id)->data.length = sizeof(*f);
206 return 0;
207}
208
209/*
210 * Try to scrub the contents of `filename' safely.
211 */
212
213static int
214scrub_file (int fd)
215{
216 off_t pos;
217 char buf[128];
218
219 pos = lseek(fd, 0, SEEK_END);
220 if (pos < 0)
221 return errno;
222 if (lseek(fd, 0, SEEK_SET) < 0)
223 return errno;
224 memset(buf, 0, sizeof(buf));
225 while(pos > 0) {
226 ssize_t tmp = write(fd, buf, min((off_t)sizeof(buf), pos));
227
228 if (tmp < 0)
229 return errno;
230 pos -= tmp;
231 }
232#ifdef _MSC_VER
233 _commit (fd);
234#else
235 fsync (fd);
236#endif
237 return 0;
238}
239
240/*
241 * Erase `filename' if it exists, trying to remove the contents if
242 * it's `safe'. We always try to remove the file, it it exists. It's
243 * only overwritten if it's a regular file (not a symlink and not a
244 * hardlink)
245 */
246
247krb5_error_code
248_krb5_erase_file(krb5_context context, const char *filename)
249{
250 int fd;
251 struct stat sb1, sb2;
252 int ret;
253
254 ret = lstat (filename, &sb1);
255 if (ret < 0)
256 return errno;
257
258#ifndef __OS2__
259 fd = open(filename, O_RDWR | O_BINARY);
260 if(fd < 0) {
261 if(errno == ENOENT)
262 return 0;
263 else
264 return errno;
265 }
266 rk_cloexec(fd);
267
268 ret = _krb5_xlock(context, fd, 1, filename);
269 if (ret) {
270 close(fd);
271 return ret;
272 }
273#endif
274 if (unlink(filename) < 0) {
275 _krb5_xunlock(context, fd);
276 close (fd);
277 return errno;
278 }
279 ret = fstat (fd, &sb2);
280 if (ret < 0) {
281 _krb5_xunlock(context, fd);
282 close (fd);
283 return errno;
284 }
285
286 /* check if someone was playing with symlinks */
287
288 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
289 _krb5_xunlock(context, fd);
290 close (fd);
291 return EPERM;
292 }
293
294 /* there are still hard links to this file */
295
296 if (sb2.st_nlink != 0) {
297 _krb5_xunlock(context, fd);
298 close (fd);
299 return 0;
300 }
301
302 ret = scrub_file (fd);
303 if (ret) {
304 _krb5_xunlock(context, fd);
305 close(fd);
306 return ret;
307 }
308 ret = _krb5_xunlock(context, fd);
309 close (fd);
310 return ret;
311}
312
313static krb5_error_code KRB5_CALLCONV
314fcc_gen_new(krb5_context context, krb5_ccache *id)
315{
316 char *file = NULL, *exp_file = NULL;
317 krb5_error_code ret;
318 krb5_fcache *f;
319 int fd;
320
321 f = malloc(sizeof(*f));
322 if(f == NULL) {
323 krb5_set_error_message(context, KRB5_CC_NOMEM,
324 N_("malloc: out of memory", ""));
325 return KRB5_CC_NOMEM;
326 }
327 ret = asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
328 if(ret < 0 || file == NULL) {
329 free(f);
330 krb5_set_error_message(context, KRB5_CC_NOMEM,
331 N_("malloc: out of memory", ""));
332 return KRB5_CC_NOMEM;
333 }
334 ret = _krb5_expand_path_tokens(context, file, &exp_file);
335 free(file);
336 if (ret)
337 return ret;
338
339 file = exp_file;
340
341 fd = mkstemp(exp_file);
342 if(fd < 0) {
343 int xret = errno;
344 krb5_set_error_message(context, xret, N_("mkstemp %s failed", ""), exp_file);
345 free(f);
346 free(exp_file);
347 return xret;
348 }
349 close(fd);
350 f->filename = exp_file;
351 f->version = 0;
352 (*id)->data.data = f;
353 (*id)->data.length = sizeof(*f);
354 return 0;
355}
356
357static void
358storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
359{
360 int flags = 0;
361 switch(vno) {
362 case KRB5_FCC_FVNO_1:
363 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
364 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
365 flags |= KRB5_STORAGE_HOST_BYTEORDER;
366 break;
367 case KRB5_FCC_FVNO_2:
368 flags |= KRB5_STORAGE_HOST_BYTEORDER;
369 break;
370 case KRB5_FCC_FVNO_3:
371 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
372 break;
373 case KRB5_FCC_FVNO_4:
374 break;
375 default:
376 krb5_abortx(context,
377 "storage_set_flags called with bad vno (%x)", vno);
378 }
379 krb5_storage_set_flags(sp, flags);
380}
381
382static krb5_error_code KRB5_CALLCONV
383fcc_open(krb5_context context,
384 krb5_ccache id,
385 int *fd_ret,
386 int flags,
387 mode_t mode)
388{
389 krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
390 (flags | O_RDWR) == flags);
391 krb5_error_code ret;
392 const char *filename;
393 int fd;
394
395 if (FCACHE(id) == NULL)
396 return krb5_einval(context, 2);
397
398 filename = FILENAME(id);
399
400 fd = open(filename, flags, mode);
401 if(fd < 0) {
402 char buf[128];
403 ret = errno;
404 rk_strerror_r(ret, buf, sizeof(buf));
405 krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"),
406 filename, buf);
407 return ret;
408 }
409 rk_cloexec(fd);
410
411 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
412 close(fd);
413 return ret;
414 }
415 *fd_ret = fd;
416 return 0;
417}
418
419static krb5_error_code KRB5_CALLCONV
420fcc_initialize(krb5_context context,
421 krb5_ccache id,
422 krb5_principal primary_principal)
423{
424 krb5_fcache *f = FCACHE(id);
425 int ret = 0;
426 int fd;
427
428 if (f == NULL)
429 return krb5_einval(context, 2);
430
431 unlink (f->filename);
432
433 ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
434 if(ret)
435 return ret;
436 {
437 krb5_storage *sp;
438 sp = krb5_storage_emem();
439 krb5_storage_set_eof_code(sp, KRB5_CC_END);
440 if(context->fcache_vno != 0)
441 f->version = context->fcache_vno;
442 else
443 f->version = KRB5_FCC_FVNO_4;
444 ret |= krb5_store_int8(sp, 5);
445 ret |= krb5_store_int8(sp, f->version);
446 storage_set_flags(context, sp, f->version);
447 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
448 /* V4 stuff */
449 if (context->kdc_sec_offset) {
450 ret |= krb5_store_int16 (sp, 12); /* length */
451 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
452 ret |= krb5_store_int16 (sp, 8); /* length of data */
453 ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
454 ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
455 } else {
456 ret |= krb5_store_int16 (sp, 0);
457 }
458 }
459 ret |= krb5_store_principal(sp, primary_principal);
460
461 ret |= write_storage(context, sp, fd);
462
463 krb5_storage_free(sp);
464 }
465 fcc_unlock(context, fd);
466 if (close(fd) < 0)
467 if (ret == 0) {
468 char buf[128];
469 ret = errno;
470 rk_strerror_r(ret, buf, sizeof(buf));
471 krb5_set_error_message (context, ret, N_("close %s: %s", ""),
472 FILENAME(id), buf);
473 }
474 return ret;
475}
476
477static krb5_error_code KRB5_CALLCONV
478fcc_close(krb5_context context,
479 krb5_ccache id)
480{
481 if (FCACHE(id) == NULL)
482 return krb5_einval(context, 2);
483
484 free (FILENAME(id));
485 krb5_data_free(&id->data);
486 return 0;
487}
488
489static krb5_error_code KRB5_CALLCONV
490fcc_destroy(krb5_context context,
491 krb5_ccache id)
492{
493 if (FCACHE(id) == NULL)
494 return krb5_einval(context, 2);
495
496 _krb5_erase_file(context, FILENAME(id));
497 return 0;
498}
499
500static krb5_error_code KRB5_CALLCONV
501fcc_store_cred(krb5_context context,
502 krb5_ccache id,
503 krb5_creds *creds)
504{
505 int ret;
506 int fd;
507
508 ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0);
509 if(ret)
510 return ret;
511 {
512 krb5_storage *sp;
513
514 sp = krb5_storage_emem();
515 krb5_storage_set_eof_code(sp, KRB5_CC_END);
516 storage_set_flags(context, sp, FCACHE(id)->version);
517 if (!krb5_config_get_bool_default(context, NULL, TRUE,
518 "libdefaults",
519 "fcc-mit-ticketflags",
520 NULL))
521 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
522 ret = krb5_store_creds(sp, creds);
523 if (ret == 0)
524 ret = write_storage(context, sp, fd);
525 krb5_storage_free(sp);
526 }
527 fcc_unlock(context, fd);
528 if (close(fd) < 0) {
529 if (ret == 0) {
530 char buf[128];
531 rk_strerror_r(ret, buf, sizeof(buf));
532 ret = errno;
533 krb5_set_error_message (context, ret, N_("close %s: %s", ""),
534 FILENAME(id), buf);
535 }
536 }
537 return ret;
538}
539
540static krb5_error_code
541init_fcc (krb5_context context,
542 krb5_ccache id,
543 krb5_storage **ret_sp,
544 int *ret_fd,
545 krb5_deltat *kdc_offset)
546{
547 int fd;
548 int8_t pvno, tag;
549 krb5_storage *sp;
550 krb5_error_code ret;
551
552 if (kdc_offset)
553 *kdc_offset = 0;
554
555 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
556 if(ret)
557 return ret;
558
559 sp = krb5_storage_from_fd(fd);
560 if(sp == NULL) {
561 krb5_clear_error_message(context);
562 ret = ENOMEM;
563 goto out;
564 }
565 krb5_storage_set_eof_code(sp, KRB5_CC_END);
566 ret = krb5_ret_int8(sp, &pvno);
567 if(ret != 0) {
568 if(ret == KRB5_CC_END) {
569 ret = ENOENT;
570 krb5_set_error_message(context, ret,
571 N_("Empty credential cache file: %s", ""),
572 FILENAME(id));
573 } else
574 krb5_set_error_message(context, ret, N_("Error reading pvno "
575 "in cache file: %s", ""),
576 FILENAME(id));
577 goto out;
578 }
579 if(pvno != 5) {
580 ret = KRB5_CCACHE_BADVNO;
581 krb5_set_error_message(context, ret, N_("Bad version number in credential "
582 "cache file: %s", ""),
583 FILENAME(id));
584 goto out;
585 }
586 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
587 if(ret != 0) {
588 ret = KRB5_CC_FORMAT;
589 krb5_set_error_message(context, ret, "Error reading tag in "
590 "cache file: %s", FILENAME(id));
591 goto out;
592 }
593 FCACHE(id)->version = tag;
594 storage_set_flags(context, sp, FCACHE(id)->version);
595 switch (tag) {
596 case KRB5_FCC_FVNO_4: {
597 int16_t length;
598
599 ret = krb5_ret_int16 (sp, &length);
600 if(ret) {
601 ret = KRB5_CC_FORMAT;
602 krb5_set_error_message(context, ret,
603 N_("Error reading tag length in "
604 "cache file: %s", ""), FILENAME(id));
605 goto out;
606 }
607 while(length > 0) {
608 int16_t dtag, data_len;
609 int i;
610 int8_t dummy;
611
612 ret = krb5_ret_int16 (sp, &dtag);
613 if(ret) {
614 ret = KRB5_CC_FORMAT;
615 krb5_set_error_message(context, ret, N_("Error reading dtag in "
616 "cache file: %s", ""),
617 FILENAME(id));
618 goto out;
619 }
620 ret = krb5_ret_int16 (sp, &data_len);
621 if(ret) {
622 ret = KRB5_CC_FORMAT;
623 krb5_set_error_message(context, ret,
624 N_("Error reading dlength "
625 "in cache file: %s",""),
626 FILENAME(id));
627 goto out;
628 }
629 switch (dtag) {
630 case FCC_TAG_DELTATIME : {
631 int32_t offset;
632
633 ret = krb5_ret_int32 (sp, &offset);
634 ret |= krb5_ret_int32 (sp, &context->kdc_usec_offset);
635 if(ret) {
636 ret = KRB5_CC_FORMAT;
637 krb5_set_error_message(context, ret,
638 N_("Error reading kdc_sec in "
639 "cache file: %s", ""),
640 FILENAME(id));
641 goto out;
642 }
643 context->kdc_sec_offset = offset;
644 if (kdc_offset)
645 *kdc_offset = offset;
646 break;
647 }
648 default :
649 for (i = 0; i < data_len; ++i) {
650 ret = krb5_ret_int8 (sp, &dummy);
651 if(ret) {
652 ret = KRB5_CC_FORMAT;
653 krb5_set_error_message(context, ret,
654 N_("Error reading unknown "
655 "tag in cache file: %s", ""),
656 FILENAME(id));
657 goto out;
658 }
659 }
660 break;
661 }
662 length -= 4 + data_len;
663 }
664 break;
665 }
666 case KRB5_FCC_FVNO_3:
667 case KRB5_FCC_FVNO_2:
668 case KRB5_FCC_FVNO_1:
669 break;
670 default :
671 ret = KRB5_CCACHE_BADVNO;
672 krb5_set_error_message(context, ret,
673 N_("Unknown version number (%d) in "
674 "credential cache file: %s", ""),
675 (int)tag, FILENAME(id));
676 goto out;
677 }
678 *ret_sp = sp;
679 *ret_fd = fd;
680
681 return 0;
682 out:
683 if(sp != NULL)
684 krb5_storage_free(sp);
685 fcc_unlock(context, fd);
686 close(fd);
687 return ret;
688}
689
690static krb5_error_code KRB5_CALLCONV
691fcc_get_principal(krb5_context context,
692 krb5_ccache id,
693 krb5_principal *principal)
694{
695 krb5_error_code ret;
696 int fd;
697 krb5_storage *sp;
698
699 ret = init_fcc (context, id, &sp, &fd, NULL);
700 if (ret)
701 return ret;
702 ret = krb5_ret_principal(sp, principal);
703 if (ret)
704 krb5_clear_error_message(context);
705 krb5_storage_free(sp);
706 fcc_unlock(context, fd);
707 close(fd);
708 return ret;
709}
710
711static krb5_error_code KRB5_CALLCONV
712fcc_end_get (krb5_context context,
713 krb5_ccache id,
714 krb5_cc_cursor *cursor);
715
716static krb5_error_code KRB5_CALLCONV
717fcc_get_first (krb5_context context,
718 krb5_ccache id,
719 krb5_cc_cursor *cursor)
720{
721 krb5_error_code ret;
722 krb5_principal principal;
723
724 if (FCACHE(id) == NULL)
725 return krb5_einval(context, 2);
726
727 *cursor = malloc(sizeof(struct fcc_cursor));
728 if (*cursor == NULL) {
729 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
730 return ENOMEM;
731 }
732 memset(*cursor, 0, sizeof(struct fcc_cursor));
733
734 ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
735 &FCC_CURSOR(*cursor)->fd, NULL);
736 if (ret) {
737 free(*cursor);
738 *cursor = NULL;
739 return ret;
740 }
741 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
742 if(ret) {
743 krb5_clear_error_message(context);
744 fcc_end_get(context, id, cursor);
745 return ret;
746 }
747 krb5_free_principal (context, principal);
748 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
749 return 0;
750}
751
752static krb5_error_code KRB5_CALLCONV
753fcc_get_next (krb5_context context,
754 krb5_ccache id,
755 krb5_cc_cursor *cursor,
756 krb5_creds *creds)
757{
758 krb5_error_code ret;
759
760 if (FCACHE(id) == NULL)
761 return krb5_einval(context, 2);
762
763 if (FCC_CURSOR(*cursor) == NULL)
764 return krb5_einval(context, 3);
765
766 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
767 return ret;
768
769 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
770 if (ret)
771 krb5_clear_error_message(context);
772
773 fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
774 return ret;
775}
776
777static krb5_error_code KRB5_CALLCONV
778fcc_end_get (krb5_context context,
779 krb5_ccache id,
780 krb5_cc_cursor *cursor)
781{
782
783 if (FCACHE(id) == NULL)
784 return krb5_einval(context, 2);
785
786 if (FCC_CURSOR(*cursor) == NULL)
787 return krb5_einval(context, 3);
788
789 krb5_storage_free(FCC_CURSOR(*cursor)->sp);
790 close (FCC_CURSOR(*cursor)->fd);
791 free(*cursor);
792 *cursor = NULL;
793 return 0;
794}
795
796static krb5_error_code KRB5_CALLCONV
797fcc_remove_cred(krb5_context context,
798 krb5_ccache id,
799 krb5_flags which,
800 krb5_creds *cred)
801{
802 krb5_error_code ret;
803 krb5_ccache copy, newfile;
804 char *newname = NULL;
805 int fd;
806
807 if (FCACHE(id) == NULL)
808 return krb5_einval(context, 2);
809
810 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &copy);
811 if (ret)
812 return ret;
813
814 ret = krb5_cc_copy_cache(context, id, copy);
815 if (ret) {
816 krb5_cc_destroy(context, copy);
817 return ret;
818 }
819
820 ret = krb5_cc_remove_cred(context, copy, which, cred);
821 if (ret) {
822 krb5_cc_destroy(context, copy);
823 return ret;
824 }
825
826 ret = asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id));
827 if (ret < 0 || newname == NULL) {
828 krb5_cc_destroy(context, copy);
829 return ENOMEM;
830 }
831
832 fd = mkstemp(&newname[5]);
833 if (fd < 0) {
834 ret = errno;
835 krb5_cc_destroy(context, copy);
836 return ret;
837 }
838 close(fd);
839
840 ret = krb5_cc_resolve(context, newname, &newfile);
841 if (ret) {
842 unlink(&newname[5]);
843 free(newname);
844 krb5_cc_destroy(context, copy);
845 return ret;
846 }
847
848 ret = krb5_cc_copy_cache(context, copy, newfile);
849 krb5_cc_destroy(context, copy);
850 if (ret) {
851 free(newname);
852 krb5_cc_destroy(context, newfile);
853 return ret;
854 }
855
856 ret = rk_rename(&newname[5], FILENAME(id));
857 if (ret)
858 ret = errno;
859 free(newname);
860 krb5_cc_close(context, newfile);
861
862 return ret;
863}
864
865static krb5_error_code KRB5_CALLCONV
866fcc_set_flags(krb5_context context,
867 krb5_ccache id,
868 krb5_flags flags)
869{
870 if (FCACHE(id) == NULL)
871 return krb5_einval(context, 2);
872
873 return 0; /* XXX */
874}
875
876static int KRB5_CALLCONV
877fcc_get_version(krb5_context context,
878 krb5_ccache id)
879{
880 if (FCACHE(id) == NULL)
881 return -1;
882
883 return FCACHE(id)->version;
884}
885
886struct fcache_iter {
887 int first;
888};
889
890static krb5_error_code KRB5_CALLCONV
891fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
892{
893 struct fcache_iter *iter;
894
895 iter = calloc(1, sizeof(*iter));
896 if (iter == NULL) {
897 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
898 return ENOMEM;
899 }
900 iter->first = 1;
901 *cursor = iter;
902 return 0;
903}
904
905static krb5_error_code KRB5_CALLCONV
906fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
907{
908 struct fcache_iter *iter = cursor;
909 krb5_error_code ret;
910 const char *fn;
911 char *expandedfn = NULL;
912
913 if (iter == NULL)
914 return krb5_einval(context, 2);
915
916 if (!iter->first) {
917 krb5_clear_error_message(context);
918 return KRB5_CC_END;
919 }
920 iter->first = 0;
921
922 fn = krb5_cc_default_name(context);
923 if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) {
924 ret = _krb5_expand_default_cc_name(context,
925 KRB5_DEFAULT_CCNAME_FILE,
926 &expandedfn);
927 if (ret)
928 return ret;
929 fn = expandedfn;
930 }
931 /* check if file exists, don't return a non existant "next" */
932 if (strncasecmp(fn, "FILE:", 5) == 0) {
933 struct stat sb;
934 ret = stat(fn + 5, &sb);
935 if (ret) {
936 ret = KRB5_CC_END;
937 goto out;
938 }
939 }
940 ret = krb5_cc_resolve(context, fn, id);
941 out:
942 if (expandedfn)
943 free(expandedfn);
944
945 return ret;
946}
947
948static krb5_error_code KRB5_CALLCONV
949fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
950{
951 struct fcache_iter *iter = cursor;
952
953 if (iter == NULL)
954 return krb5_einval(context, 2);
955
956 free(iter);
957 return 0;
958}
959
960static krb5_error_code KRB5_CALLCONV
961fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
962{
963 krb5_error_code ret = 0;
964
965 ret = rk_rename(FILENAME(from), FILENAME(to));
966
967 if (ret && errno != EXDEV) {
968 char buf[128];
969 ret = errno;
970 rk_strerror_r(ret, buf, sizeof(buf));
971 krb5_set_error_message(context, ret,
972 N_("Rename of file from %s "
973 "to %s failed: %s", ""),
974 FILENAME(from), FILENAME(to), buf);
975 return ret;
976 } else if (ret && errno == EXDEV) {
977 /* make a copy and delete the orignal */
978 krb5_ssize_t sz1, sz2;
979 int fd1, fd2;
980 char buf[BUFSIZ];
981
982 ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
983 if(ret)
984 return ret;
985
986 unlink(FILENAME(to));
987
988 ret = fcc_open(context, to, &fd2,
989 O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
990 if(ret)
991 goto out1;
992
993 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
994 sz2 = write(fd2, buf, sz1);
995 if (sz1 != sz2) {
996 ret = EIO;
997 krb5_set_error_message(context, ret,
998 N_("Failed to write data from one file "
999 "credential cache to the other", ""));
1000 goto out2;
1001 }
1002 }
1003 if (sz1 < 0) {
1004 ret = EIO;
1005 krb5_set_error_message(context, ret,
1006 N_("Failed to read data from one file "
1007 "credential cache to the other", ""));
1008 goto out2;
1009 }
1010 out2:
1011 fcc_unlock(context, fd2);
1012 close(fd2);
1013
1014 out1:
1015 fcc_unlock(context, fd1);
1016 close(fd1);
1017
1018 _krb5_erase_file(context, FILENAME(from));
1019
1020 if (ret) {
1021 _krb5_erase_file(context, FILENAME(to));
1022 return ret;
1023 }
1024 }
1025
1026 /* make sure ->version is uptodate */
1027 {
1028 krb5_storage *sp;
1029 int fd;
1030 if ((ret = init_fcc (context, to, &sp, &fd, NULL)) == 0) {
1031 if (sp)
1032 krb5_storage_free(sp);
1033 fcc_unlock(context, fd);
1034 close(fd);
1035 }
1036 }
1037
1038 fcc_close(context, from);
1039
1040 return ret;
1041}
1042
1043static krb5_error_code KRB5_CALLCONV
1044fcc_get_default_name(krb5_context context, char **str)
1045{
1046 return _krb5_expand_default_cc_name(context,
1047 KRB5_DEFAULT_CCNAME_FILE,
1048 str);
1049}
1050
1051static krb5_error_code KRB5_CALLCONV
1052fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1053{
1054 krb5_error_code ret;
1055 struct stat sb;
1056 int fd;
1057
1058 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
1059 if(ret)
1060 return ret;
1061 ret = fstat(fd, &sb);
1062 close(fd);
1063 if (ret) {
1064 ret = errno;
1065 krb5_set_error_message(context, ret, N_("Failed to stat cache file", ""));
1066 return ret;
1067 }
1068 *mtime = sb.st_mtime;
1069 return 0;
1070}
1071
1072static krb5_error_code KRB5_CALLCONV
1073fcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
1074{
1075 return 0;
1076}
1077
1078static krb5_error_code KRB5_CALLCONV
1079fcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
1080{
1081 krb5_error_code ret;
1082 krb5_storage *sp = NULL;
1083 int fd;
1084 ret = init_fcc(context, id, &sp, &fd, kdc_offset);
1085 if (sp)
1086 krb5_storage_free(sp);
1087 fcc_unlock(context, fd);
1088 close(fd);
1089
1090 return ret;
1091}
1092
1093
1094/**
1095 * Variable containing the FILE based credential cache implemention.
1096 *
1097 * @ingroup krb5_ccache
1098 */
1099
1100KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = {
1101 KRB5_CC_OPS_VERSION,
1102 "FILE",
1103 fcc_get_name,
1104 fcc_resolve,
1105 fcc_gen_new,
1106 fcc_initialize,
1107 fcc_destroy,
1108 fcc_close,
1109 fcc_store_cred,
1110 NULL, /* fcc_retrieve */
1111 fcc_get_principal,
1112 fcc_get_first,
1113 fcc_get_next,
1114 fcc_end_get,
1115 fcc_remove_cred,
1116 fcc_set_flags,
1117 fcc_get_version,
1118 fcc_get_cache_first,
1119 fcc_get_cache_next,
1120 fcc_end_cache_get,
1121 fcc_move,
1122 fcc_get_default_name,
1123 NULL,
1124 fcc_lastchange,
1125 fcc_set_kdc_offset,
1126 fcc_get_kdc_offset
1127};
Note: See TracBrowser for help on using the repository browser.