diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2005-12-25 21:35:38 +0000 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2005-12-25 21:35:38 +0000 |
commit | 8b9f6b163aaae865aaf556b5fbf301ce26c26ddf (patch) | |
tree | bc1b1c049ad69278ee26fbda9380ca4d733635f9 | |
parent | 532cf2ac3314dc05fb4713a0d2544493c0551bc0 (diff) | |
download | coreutils-8b9f6b163aaae865aaf556b5fbf301ce26c26ddf.tar.xz |
(chdir_no_follow): Don't include stdio.h, assert.h,
unistd.h, fcntl--.h; not needed.
(O_DIRECTORY): Define if not already defined.
(chdir_no_follow): Revamp describing comment to match code more
closely. Redo use of internal vars to avoid lint complaints.
Work even if directory is writeable but not readable.
Open with O_DIRECTORY | O_NOCTTY, for benefit of hosts that
don't have O_NOFOLLOW. Use O_NONBLOCK (POSIX spelling) rather
than O_NDELAY. Don't bother invoking fstat if open does not
dereference symlink, since the result isn't used then.
Don't assume file descriptor is positive; it might be zero
now that we no longer include fcntl--.h (we don't need fcntl--.h
since we immediately close the descriptor).
-rw-r--r-- | lib/chdir-safer.c | 87 |
1 files changed, 44 insertions, 43 deletions
diff --git a/lib/chdir-safer.c b/lib/chdir-safer.c index 6106fb981..259a89a78 100644 --- a/lib/chdir-safer.c +++ b/lib/chdir-safer.c @@ -1,4 +1,5 @@ -/* like chdir(2), but safer, if possible +/* much like chdir(2), but safer + Copyright (C) 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify @@ -24,17 +25,16 @@ #include "chdir-safer.h" #include <stdbool.h> -#include <stdio.h> -#include <assert.h> #include <fcntl.h> #include <errno.h> -#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> -#include "fcntl--.h" /* for the open->open_safer mapping */ +#ifndef O_DIRECTORY +# define O_DIRECTORY 0 +#endif -#if !defined O_NOFOLLOW +#ifndef O_NOFOLLOW # define O_NOFOLLOW 0 #endif @@ -42,51 +42,52 @@ ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \ && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev) -/* Just like chmod, but fail if DIR is a symbolic link. - This can avoid a minor race condition between when a - directory is created or stat'd and when we chdir into it. - - Note that this function fails (while chdir would succeed) - if DIR cannot be opened with O_RDONLY. */ +/* Like chdir, but fail if DIR is a symbolic link to a directory (or + similar funny business), or if DIR is neither readable nor + writeable. This avoids a minor race condition between when a + directory is created or statted and when the process chdirs into + it. */ int chdir_no_follow (char const *dir) { - int fail = -1; - struct stat sb; - struct stat sb_init; - int saved_errno = 0; - int fd; - - bool open_dereferences_symlink = ! O_NOFOLLOW; - - /* If open follows symlinks, lstat DIR, to get its device and - inode numbers. */ - if (open_dereferences_symlink && lstat (dir, &sb_init) != 0) - return fail; - - fd = open (dir, O_NOFOLLOW | O_RDONLY | O_NDELAY); - - if (0 <= fd - && fstat (fd, &sb) == 0 - /* If DIR is a different directory, then someone is trying to do - something nasty. However, the risk of such an attack is so low - that it isn't worth a special diagnostic. Simply skip the fchdir - and set errno (to the same value that open uses for symlinks with - O_NOFOLLOW), so that the caller can report the failure. */ - && ( ! open_dereferences_symlink || SAME_INODE (sb_init, sb) - || ((errno = ELOOP), 0)) - && fchdir (fd) == 0) + int result = 0; + int saved_errno; + int open_flags = O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK; + int fd = open (dir, O_RDONLY | open_flags); + if (fd < 0) { - fail = 0; + if (errno == EACCES) + fd = open (dir, O_WRONLY | open_flags); + if (fd < 0) + return fd; } - else + + /* If open follows symlinks, lstat DIR and fstat FD to ensure that + they are the same file; if they are different files, set errno to + ELOOP (the same value that open uses for symlinks with + O_NOFOLLOW) so the caller can report a failure. */ + if (! O_NOFOLLOW) { - saved_errno = errno; + struct stat sb1; + struct stat sb2; + + result = lstat (dir, &sb1); + if (result == 0) + { + result = fstat (fd, &sb2); + if (result == 0 && ! SAME_INODE (sb1, sb2)) + { + errno = ELOOP; + result = -1; + } + } } - if (0 < fd) - close (fd); /* Ignore any failure. */ + if (result == 0) + result = fchdir (fd); + saved_errno = errno; + close (fd); errno = saved_errno; - return fail; + return result; } |