Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 2 Oct 2004 13:18:42 +0300
From:      Giorgos Keramidas <keramida@freebsd.org>
To:        Ryan Sommers <ryans@gamersimpact.com>, Max Laier <max@love2party.net>
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: Protection from the dreaded "rm -fr /"
Message-ID:  <20041002101842.GA23272@gothmog.gr>
In-Reply-To: <200410021123.59811.max@love2party.net> <415E6C4A.1010804@gamersimpact.com>
References:  <20041002081928.GA21439@gothmog.gr> <200410021123.59811.max@love2party.net> <20041002081928.GA21439@gothmog.gr> <20041002083336.GA10355@k7.mavetju> <415E6C4A.1010804@gamersimpact.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On 2004-10-02 03:52, Ryan Sommers <ryans@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@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.
 	 */
%%%



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20041002101842.GA23272>