openbsd-ports/misc/fileutils/patches/patch-src_remove_c
brad fa86cd9348 A race condition in the recursive use of 'rm' and 'mv' in fileutils 4.1 and
earlier could allow local users to delete files and directories (as the user
running fileutils) if the user has write access to part of the tree being
moved or deleted.

http://online.securityfocus.com/archive/1/260936
2003-02-25 14:35:43 +00:00

128 lines
4.5 KiB
Plaintext

$OpenBSD: patch-src_remove_c,v 1.1 2003/02/25 14:35:43 brad Exp $
--- src/remove.c.orig Sun Feb 18 11:17:32 2001
+++ src/remove.c Mon Feb 24 23:21:17 2003
@@ -433,10 +433,13 @@ same_file (const char *file_1, const cha
/* Recursively remove all of the entries in the current directory.
- Return an indication of the success of the operation. */
+ Return an indication of the success of the operation.
+ CWD_DEV_INO must store the device and inode numbers of the
+ current working directory. */
static enum RM_status
-remove_cwd_entries (const struct rm_options *x)
+remove_cwd_entries (const struct rm_options *x,
+ struct dev_ino const *cwd_dev_ino)
{
/* NOTE: this is static. */
static DIR *dirp = NULL;
@@ -549,7 +552,7 @@ remove_cwd_entries (const struct rm_opti
/* CAUTION: after this call to rm, DP may not be valid --
it may have been freed due to a close in a recursive call
(through rm and remove_dir) to this function. */
- tmp_status = rm (&fs, 0, x);
+ tmp_status = rm (&fs, 0, x, cwd_dev_ino);
/* Update status. */
if (tmp_status > status)
@@ -664,12 +667,14 @@ remove_file (struct File_spec *fs, const
FIXME: describe need_save_cwd parameter. */
static enum RM_status
-remove_dir (struct File_spec *fs, int need_save_cwd, const struct rm_options *x)
+remove_dir (struct File_spec *fs, int need_save_cwd,
+ struct rm_options const *x, struct dev_ino const *cwd_dev_ino)
{
enum RM_status status;
struct saved_cwd cwd;
char *dir_name = fs->filename;
const char *fmt = NULL;
+ struct dev_ino tmp_cwd_dev_ino;
if (!x->recursive)
{
@@ -738,6 +743,9 @@ was replaced with either another directo
(unsigned long)(sb.st_dev),
(unsigned long)(sb.st_ino));
}
+
+ tmp_cwd_dev_ino.st_dev = sb.st_dev;
+ tmp_cwd_dev_ino.st_ino = sb.st_ino;
}
push_dir (dir_name);
@@ -747,7 +755,7 @@ was replaced with either another directo
remove_cwd_entries may close the directory. */
ASSIGN_STRDUPA (dir_name, dir_name);
- status = remove_cwd_entries (x);
+ status = remove_cwd_entries (x, &tmp_cwd_dev_ino);
pop_dir ();
@@ -761,11 +769,34 @@ was replaced with either another directo
}
free_cwd (&cwd);
}
- else if (chdir ("..") < 0)
+ else
{
- error (0, errno, _("cannot change back to directory %s via `..'"),
- quote (full_filename (dir_name)));
- return RM_ERROR;
+ struct stat sb;
+ if (chdir ("..") < 0)
+ {
+ error (0, errno, _("cannot change back to directory %s via `..'"),
+ quote (full_filename (dir_name)));
+ return RM_ERROR;
+ }
+
+ if (lstat (".", &sb))
+ error (EXIT_FAILURE, errno,
+ _("cannot lstat `.' in %s"), quote (full_filename (".")));
+
+ if (!SAME_INODE (sb, *cwd_dev_ino))
+ {
+ error (EXIT_FAILURE, 0,
+ _("ERROR: the directory %s initially had device/inode\n\
+numbers %lu/%lu, but now (after changing into at least one subdirectory\n\
+and changing back via `..'), the numbers for `.' are %lu/%lu.\n\
+That means that while rm was running, a partially-removed subdirectory\n\
+was moved to a different position in the file system hierarchy."),
+ quote (full_filename (".")),
+ (unsigned long)(cwd_dev_ino->st_dev),
+ (unsigned long)(cwd_dev_ino->st_ino),
+ (unsigned long)(sb.st_dev),
+ (unsigned long)(sb.st_ino));
+ }
}
if (x->interactive)
@@ -814,10 +845,13 @@ was replaced with either another directo
things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
be `.', `..', or may contain slashes. Otherwise, it must be a simple file
- name (and hence must specify a file in the current directory). */
+ name (and hence must specify a file in the current directory).
+ CWD_DEV_INO must store the device and inode numbers of the
+ current working directory. */
enum RM_status
-rm (struct File_spec *fs, int user_specified_name, const struct rm_options *x)
+rm (struct File_spec *fs, int user_specified_name,
+ struct rm_options const *x, struct dev_ino const *cwd_dev_ino)
{
mode_t filetype_mode;
@@ -899,7 +933,7 @@ The following two directories have the s
if (need_save_cwd)
need_save_cwd = (strchr (fs->filename, '/') != NULL);
- status = remove_dir (fs, need_save_cwd, x);
+ status = remove_dir (fs, need_save_cwd, x, cwd_dev_ino);
#ifdef ENABLE_CYCLE_CHECK
{