Protection from the dreaded "rm -fr /"
Giorgos Keramidas
keramida at freebsd.org
Sat Oct 2 03:19:46 PDT 2004
On 2004-10-02 03:52, Ryan Sommers <ryans at gamersimpact.com> wrote:
> On Sat, Oct 02, 2004 at 11:19:28AM +0300, Giorgos Keramidas wrote:
> >about "rm -fr /" protection, which I liked a lot:
> >http://blogs.sun.com/roller/page/jbeck/20041001#rm_rf_protection
> >
> >His idea was remarkably simple, so I went ahead and wrote this patch
> >for
> >rm(1) of FreeBSD:
>
> As for adding this kind of oops-proofing. I'm not sure I like the idea
> of completely removing the ability to use / as an argument. How about
> prompting and needing 'yes' as input?
This might break things because the user hasn't specified -i and will
suddenly get a prompt. Unexpected prompts might never get an answer.
I liked what Max Laier proposed though, about making this tunable and
defaulting to off. See below for the behavior of what I've come up with:
On 2004-10-02 11:23, Max Laier <max at love2party.net> wrote:
> [ Sorry to be so negative ... ]
>
> At very least you should consider to error out silently as POSIX
> requires "-f" to be silent. Other than that you should really look
> into the standards and what they way about rm and friends.
Agreed. Thanks for the feedback. Positive replies are not the only
sort that are worth a lot :-)
How does the following look instead of forcing stuff to the user?
1. Silently erroring out:
chroot# export RM_PROTECT_ROOT=1
chroot# /bin/rm -fr /
chroot# echo $?
1
chroot# /bin/rm -fr .././
chroot# echo $?
1
2. Warning with an error message because RM_PROTECT_ROOT is set:
chroot# export RM_PROTECT_ROOT=1
chroot# /bin/rm -r /
rm: recursive rm of / not allowed
chroot# /bin/rm -r .././
rm: recursive rm of / not allowed
3. The current behavior as a default when RM_PROTECT_ROOT is unset:
chroot# unset RM_PROTECT_ROOT
chroot# /bin/rm -r /
override rwxr-xr-x 0/0 for /bin/rm? ^Cchroot#
chroot#
chroot#
chroot# /bin/rm -fr /
rm: /libexec/ld-elf.so.1: Operation not permitted
rm: /libexec: Directory not empty
rm: /lib/libc.so.5: Operation not permitted
rm: /lib/libcrypt.so.2: Operation not permitted
rm: /lib: Directory not empty
rm: /: Is a directory
chroot# ls -l
ls: not found
chroot# echo *
lib libexec
chroot# cd lib
chroot# echo *
libc.so.5 libcrypt.so.2
chroot# exit
Here's the updated diff:
%%%
Index: rm.c
===================================================================
RCS file: /home/ncvs/src/bin/rm/rm.c,v
retrieving revision 1.47
diff -u -r1.47 rm.c
--- rm.c 6 Apr 2004 20:06:50 -0000 1.47
+++ rm.c 2 Oct 2004 10:06:59 -0000
@@ -57,7 +57,7 @@
#include <sysexits.h>
#include <unistd.h>
-int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
+int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok, protect_root;
uid_t uid;
int check(char *, char *, struct stat *);
@@ -100,6 +100,10 @@
exit(eval);
}
+ protect_root = 0;
+ if (getenv("RM_PROTECT_ROOT") != NULL)
+ protect_root = 1;
+
Pflag = rflag = 0;
while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1)
switch(ch) {
@@ -157,6 +161,8 @@
void
rm_tree(char **argv)
{
+ static char *rpath = NULL;
+ char **argv_tmp;
FTS *fts;
FTSENT *p;
int needstat;
@@ -164,6 +170,25 @@
int rval;
/*
+ * If enabled in the environment with RM_PROTECT_ROOT disable the
+ * ability to recursively remove the root directory.
+ */
+ if (protect_root) {
+ if (rpath == NULL &&
+ (rpath = malloc(PATH_MAX * sizeof(char))) == NULL)
+ err(1, "malloc");
+ for (argv_tmp = argv; *argv_tmp != NULL; argv_tmp++) {
+ if (realpath(*argv_tmp, rpath) == NULL &&
+ strcmp(rpath, "/") != 0)
+ continue;
+ if (fflag != 0)
+ exit (1);
+ else
+ errx(1, "recursive rm of / not allowed");
+ }
+ }
+
+ /*
* Remove a file hierarchy. If forcing removal (-f), or interactive
* (-i) or can't ask anyway (stdin_ok), don't stat the file.
*/
%%%
More information about the freebsd-hackers
mailing list