Description of problem: If the 'rm' command is executed while the current working directory is not searchable (+x permission) by the effective user, then rm will fail on a call to lstat() of the current directroy and the files specified on the command line will not be removed, even if the current user has the right to delete those files. Version-Release number of selected component (if applicable): 4.0x-3.1 How reproducible: Repeatable. Steps to Reproduce: 1. Create a directory. 2. CD in to that directory. 3. After CD'ing, remove search permissions from that directory (chmod -x). 4. Then while in that directory try to 'rm' a file. You'll get this error: rm: cannot lstat `.': Permission denied and the file will not be removed. Actual results: Files not removed. Expected results: Files removed. Additional info:
Nothing fileutils can do about it; that's just how permissions work on directories I think. $ strace -feunlink perl -e 'unlink ("file")' unlink("file") = -1 EACCES (Permission denied)
Well the previous version of fileutils didn't have this problem. So me thinks that it is not unavoidable.
Which version, specifically?
As there appears to be no older fileutils RPMs on my Red Hat update mirror, the previous version must have been the one from the Red Hat 7.0 CD-ROM which is fileutils-4.0x-3. The reason I know the previous version worked is that I had a /etc/procmailrc invoked spam-checking script that broke when fileutils was upgraded last Thursday. The script was doing an "rm -f" on a lock file created by procmail's 'lockfile' utility. In some instances the script's PWD happened to be one that was not searchable. But since the script never actually does anything in PWD this never mattered before the upgrade. I could imagine lots of system scripts breaking in this fashion, where before now they never cared about the current PWD because they didn't use it for anything. Now "rm" creates an unnecessary dependency on PWD's permissions.
Do you mean that the file that you're trying to remove isn't in the directory in question? Please supply a real test case that I can try.
Yes, the file I'm trying to delete is *not* in the current directory. It's elsewhere on the file system. The procedure is described in the original bug report. But I now see that step #4 is not clear. It should be: ------------- 4. Then while in that directory, 'rm' a file that resides elsewhere on the file system. You'll get this error: rm: cannot lstat `.': Permission denied and the file will not be removed. ------------- Sorry about that. The things a little lack of clarity will lead to.
I am unable to replicate this with the version mentioned: fileutils-4.0x-3.1 I used the following script: #!/bin/bash file=$(mktemp /tmp/file.XXXXXX) dir=$(mktemp -d /tmp/dir.XXXXXX) relpath="../$(basename $file)" echo "Using file $file and directory $dir" ls -ld $dir ls -l $file cd $dir touch $(seq 10) echo Changing permissions on $dir chmod a-rwx $dir ls -ld $dir echo "Removing $relpath (cwd is $PWD)" rm $relpath && ! [ -e $file ] || echo rm failed echo "Cleaning up" chmod u+rwx $dir rm -rf $dir
Running your script on my system gives this output: Using file /tmp/file.qPHPcS and directory /tmp/dir.Jt050R drwx------ 2 terryg terryg 4096 Feb 18 17:13 /tmp/dir.Jt050R -rw------- 1 terryg terryg 0 Feb 18 17:13 /tmp/file.qPHPcS Changing permissions on /tmp/dir.Jt050R d--------- 2 terryg terryg 4096 Feb 18 17:13 /tmp/dir.Jt050R Removing ../file.qPHPcS (cwd is /tmp/dir.Jt050R) rm: cannot lstat `.': Permission denied rm failed Cleaning up
I get the same result as Mike.
Did you run the script as root or as a regular user? For me the script works fine as root but fails as a regular user.
Okay, I see it now.
*** Bug 123071 has been marked as a duplicate of this bug. ***
If the current directory does not exist, rm fails immediately. This is a result of a bad security fix for fileutils 4.1 and is fixed in 4.5 and later. The problem is only on AS2.1 A trivial testcase to reproduce this problem is: [0] gmarsden@ca-build1:/tmp$ mkdir rmtest [0] gmarsden@ca-build1:/tmp$ cd rmtest/ [0] gmarsden@ca-build1:/tmp/rmtest$ touch /tmp/file1 [0] gmarsden@ca-build1:/tmp/rmtest$ chmod 0 . [0] gmarsden@ca-build1:/tmp/rmtest$ rm -f /tmp/file1 rm: cannot lstat `.': Permission denied [1] gmarsden@ca-build1:/tmp/rmtest$ ls -l /tmp/file1 -rw-rw-r-- 1 gmarsden gmarsden 0 May 7 18:52 /tmp/file1 The offending code is actually flagged with a FIXME in the AS2.1 fileutils package and results from attempting to do an lstat on the current directory to prevent a security exploit. However, this check should not happen when not removing recursive directories.
Created attachment 100278 [details] patch to fix rm behavior. Patch to fix rm for file-delete
The rm.c part looks like it is reversed to me. Before: ==> if (lstat (...)) error; carry on; <== After: ==> if (lstat (...)) carry on; else error; <== Shouldn't the if/else clauses be the other way around? In the first hunk from remove.c, 'sb' is initialised from a checked lstat() called after a successful chdir(), so there's no reason I can see that sb->st_dev or sb->st_ino would be NULL. Did you mean (*fs)->st_dev/ino? Similarly for hunk #2 from remove.c: why would sb have NULL st_dev/st_ino here?
Created attachment 100350 [details] rmfix patch, take 2 Ok, that was silly, thanks for noticing the reversed call path. Here's a new, cleaner, patch. The only parameter which must be checked for null is cwd_dev_ino,which is passed from the initial lstat into the remove_dir function, and could be set to null by the new code in rm.c. As you noticed, only one null check is required in remove.c There's no need to check *fs, as that lstat is being done from the present directory.
Yes, this patch looks good -- thanks.
An errata has been issued which should help the problem described in this bug report. This report is therefore being closed with a resolution of ERRATA. For more information on the solution and/or where to find the updated files, please follow the link below. You may reopen this bug report if the solution does not work for you. http://rhn.redhat.com/errata/RHBA-2004-230.html