1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/rcache/rc_io.c */
4 * This file of the Kerberos V5 software is derived from public-domain code
5 * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
10 * I/O functions for the replay cache default implementation.
14 # define PATH_SEPARATOR "\\"
16 # define PATH_SEPARATOR "/"
19 #define KRB5_RC_VNO 0x0501 /* krb5, rcache v 1 */
25 #include <stdio.h> /* for P_tmpdir */
34 #ifdef HAVE_NETINET_IN_H
35 #if !defined(_WINSOCKAPI_)
36 #include <netinet/in.h>
39 #error find some way to use net-byte-order file version numbers.
42 #define UNIQUE getpid() /* hopefully unique number */
44 #define GETDIR (dir = getdir(), dirlen = strlen(dir) + sizeof(PATH_SEPARATOR) - 1)
51 if (!(dir = getenv("KRB5RCACHEDIR"))) {
53 if (!(dir = getenv("TEMP")))
54 if (!(dir = getenv("TMP")))
57 if (!(dir = getenv("TMPDIR"))) {
70 * Called from krb5_rc_io_creat(); calls mkstemp() and does some
71 * sanity checking on the file modes in case some broken mkstemp()
72 * implementation creates the file with overly permissive modes. To
73 * avoid race conditions, do not fchmod() a file for which mkstemp set
76 static krb5_error_code
77 krb5_rc_io_mkstemp(krb5_context context, krb5_rc_iostuff *d, char *dir)
79 krb5_error_code retval = 0;
83 memset(&stbuf, 0, sizeof(stbuf));
85 if (asprintf(&d->fn, "%s%skrb5_RCXXXXXX",
86 dir, PATH_SEPARATOR) < 0) {
88 return KRB5_RC_IO_MALLOC;
90 d->fd = mkstemp(d->fn);
93 * This return value is deliberate because d->fd == -1 causes
94 * caller to go into errno interpretation code.
100 * Be paranoid and check that mkstemp made the file accessible
103 retval = fstat(d->fd, &stbuf);
105 k5_setmsg(context, retval,
106 _("Cannot fstat replay cache file %s: %s"),
107 d->fn, strerror(errno));
108 return KRB5_RC_IO_UNKNOWN;
110 if (stbuf.st_mode & 077) {
111 k5_setmsg(context, retval,
112 _("Insecure mkstemp() file mode for replay cache file %s; "
113 "try running this program with umask 077"), d->fn);
114 return KRB5_RC_IO_UNKNOWN;
120 static krb5_error_code
121 rc_map_errno (krb5_context context, int e, const char *fn,
122 const char *operation)
130 return KRB5_RC_IO_SPACE;
133 return KRB5_RC_IO_IO;
139 k5_setmsg(context, KRB5_RC_IO_PERM,
140 _("Cannot %s replay cache file %s: %s"),
141 operation, fn, strerror(e));
142 return KRB5_RC_IO_PERM;
145 k5_setmsg(context, KRB5_RC_IO_UNKNOWN, _("Cannot %s replay cache: %s"),
146 operation, strerror(e));
147 return KRB5_RC_IO_UNKNOWN;
153 krb5_rc_io_creat(krb5_context context, krb5_rc_iostuff *d, char **fn)
155 krb5_int16 rc_vno = htons(KRB5_RC_VNO);
156 krb5_error_code retval = 0;
157 int flags, do_not_unlink = 0;
163 if (asprintf(&d->fn, "%s%s%s", dir, PATH_SEPARATOR, *fn) < 0)
164 return KRB5_RC_IO_MALLOC;
167 if (unlink(d->fn) == -1 && errno != ENOENT)
169 flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY;
170 d->fd = THREEPARAMOPEN(d->fn, flags, 0600);
171 } while (d->fd == -1 && errno == EEXIST);
173 retval = krb5_rc_io_mkstemp(context, d, dir);
176 if (d->fd != -1 && fn) {
177 *fn = strdup(d->fn + dirlen);
180 return KRB5_RC_IO_MALLOC;
185 retval = rc_map_errno(context, errno, d->fn, "create");
186 if (retval == KRB5_RC_IO_PERM)
190 set_cloexec_fd(d->fd);
191 retval = krb5_rc_io_write(context, d, (krb5_pointer)&rc_vno,
196 retval = krb5_rc_io_sync(context, d);
202 (void) unlink(d->fn);
213 static krb5_error_code
214 krb5_rc_io_open_internal(krb5_context context, krb5_rc_iostuff *d, char *fn,
218 krb5_error_code retval = 0;
219 int do_not_unlink = 1;
221 struct stat sb1, sb2;
227 if (!(d->fn = strdup(full_pathname)))
228 return KRB5_RC_IO_MALLOC;
230 if (asprintf(&d->fn, "%s%s%s", dir, PATH_SEPARATOR, fn) < 0)
231 return KRB5_RC_IO_MALLOC;
235 d->fd = THREEPARAMOPEN(d->fn, O_RDWR | O_BINARY, 0600);
237 retval = rc_map_errno(context, errno, d->fn, "open");
242 retval = lstat(d->fn, &sb1);
244 retval = rc_map_errno(context, errno, d->fn, "lstat");
247 d->fd = THREEPARAMOPEN(d->fn, O_RDWR | O_BINARY, 0600);
249 retval = rc_map_errno(context, errno, d->fn, "open");
252 retval = fstat(d->fd, &sb2);
254 retval = rc_map_errno(context, errno, d->fn, "fstat");
257 /* check if someone was playing with symlinks */
258 if ((sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino)
259 || (sb1.st_mode & S_IFMT) != S_IFREG)
261 retval = KRB5_RC_IO_PERM;
262 k5_setmsg(context, retval, "rcache not a file %s", d->fn);
265 /* check that non other can read/write/execute the file */
266 if (sb1.st_mode & 077) {
267 k5_setmsg(context, retval,
268 _("Insecure file mode for replay cache file %s"), d->fn);
269 return KRB5_RC_IO_UNKNOWN;
272 if (sb1.st_uid != geteuid()) {
273 retval = KRB5_RC_IO_PERM;
274 k5_setmsg(context, retval, _("rcache not owned by %d"),
279 set_cloexec_fd(d->fd);
282 retval = krb5_rc_io_read(context, d, (krb5_pointer) &rc_vno,
287 if (ntohs(rc_vno) != KRB5_RC_VNO)
288 retval = KRB5_RCACHE_BADVNO;
293 (void) unlink(d->fn);
303 krb5_rc_io_open(krb5_context context, krb5_rc_iostuff *d, char *fn)
305 return krb5_rc_io_open_internal(context, d, fn, NULL);
309 krb5_rc_io_move(krb5_context context, krb5_rc_iostuff *new1,
310 krb5_rc_iostuff *old)
312 #if defined(_WIN32) || defined(__CYGWIN__)
316 krb5_error_code retval = 0;
318 * Initial work around provided by Tom Sanfilippo to work around
319 * poor Windows emulation of POSIX functions. Rename and dup has
320 * different semantics!
322 * Additional fixes and explanation provided by dalmeida@mit.edu:
324 * First, we save the offset of "old". Then, we close and remove
325 * the "new" file so we can do the rename. We also close "old" to
326 * make sure the rename succeeds (though that might not be
327 * necessary on some systems).
329 * Next, we do the rename. If all goes well, we seek the "new"
330 * file to the position "old" was at.
334 * Since "old" is now gone, we mourn its disappearance, but we
335 * cannot emulate that Unix behavior... THIS BEHAVIOR IS
336 * DIFFERENT FROM UNIX. However, it is ok because this function
337 * gets called such that "old" gets closed right afterwards.
339 offset = lseek(old->fd, 0, SEEK_CUR);
353 if (rename(old_fn, new_fn) == -1) { /* MUST be atomic! */
354 retval = KRB5_RC_IO_UNKNOWN;
358 retval = krb5_rc_io_open_internal(context, new1, 0, new_fn);
362 if (lseek(new1->fd, offset, SEEK_SET) == -1) {
363 retval = KRB5_RC_IO_UNKNOWN;
373 if (rename(old->fn, new1->fn) == -1) /* MUST be atomic! */
374 return KRB5_RC_IO_UNKNOWN;
376 new1->fn = NULL; /* avoid clobbering */
377 (void) krb5_rc_io_close(context, new1);
379 new1->fd = dup(old->fd);
380 set_cloexec_fd(new1->fd);
386 krb5_rc_io_write(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
389 if (write(d->fd, (char *) buf, num) == -1)
397 k5_setmsg(context, KRB5_RC_IO_SPACE,
398 _("Can't write to replay cache: %s"), strerror(errno));
399 return KRB5_RC_IO_SPACE;
401 k5_setmsg(context, KRB5_RC_IO_IO,
402 _("Can't write to replay cache: %s"), strerror(errno));
403 return KRB5_RC_IO_IO;
406 k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
407 _("Can't write to replay cache: %s"), strerror(errno));
408 return KRB5_RC_IO_UNKNOWN;
414 krb5_rc_io_sync(krb5_context context, krb5_rc_iostuff *d)
418 #define fsync _commit
421 if (fsync(d->fd) == -1) {
424 case EBADF: return KRB5_RC_IO_UNKNOWN;
425 case EIO: return KRB5_RC_IO_IO;
427 k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
428 _("Cannot sync replay cache file: %s"), strerror(errno));
429 return KRB5_RC_IO_UNKNOWN;
436 krb5_rc_io_read(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
440 if ((count = read(d->fd, (char *) buf, num)) == -1)
443 case EIO: return KRB5_RC_IO_IO;
446 k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
447 _("Can't read from replay cache: %s"), strerror(errno));
448 return KRB5_RC_IO_UNKNOWN;
450 if (count < 0 || (unsigned int)count != num)
451 return KRB5_RC_IO_EOF;
456 krb5_rc_io_close(krb5_context context, krb5_rc_iostuff *d)
463 if (close(d->fd) == -1) /* can't happen */
464 return KRB5_RC_IO_UNKNOWN;
471 krb5_rc_io_destroy(krb5_context context, krb5_rc_iostuff *d)
473 if (unlink(d->fn) == -1)
477 k5_setmsg(context, KRB5_RC_IO_IO,
478 _("Can't destroy replay cache: %s"), strerror(errno));
479 return KRB5_RC_IO_IO;
483 k5_setmsg(context, KRB5_RC_IO_PERM,
484 _("Can't destroy replay cache: %s"), strerror(errno));
485 return KRB5_RC_IO_PERM;
488 k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
489 _("Can't destroy replay cache: %s"), strerror(errno));
490 return KRB5_RC_IO_UNKNOWN;
496 krb5_rc_io_mark(krb5_context context, krb5_rc_iostuff *d)
498 d->mark = lseek(d->fd, (off_t) 0, SEEK_CUR); /* can't fail */
503 krb5_rc_io_unmark(krb5_context context, krb5_rc_iostuff *d)
505 (void) lseek(d->fd, d->mark, SEEK_SET); /* if it fails, tough luck */
510 krb5_rc_io_size(krb5_context context, krb5_rc_iostuff *d)
514 if (fstat(d->fd, &statb) == 0)
515 return statb.st_size;