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