rm: Use basenames to protect against . and .. - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit b27871013b1debede7a5177f915be3f7b75aaee3 | |
parent d4dfd42d3580ad36bedfdc5b8924ff6683981f53 | |
Author: Roberto E. Vargas Caballero <[email protected]> | |
Date: Fri, 25 Apr 2025 12:37:10 +0200 | |
rm: Use basenames to protect against . and .. | |
POSIX mandates that the protection should care about the basename, | |
and we cannot use basename because it can modify the input string | |
and it would make harder later operations. Also, it would put a limit | |
in the length of the name of the paths and POSIX forbids limitations | |
about that regard in rm(1). | |
Diffstat: | |
M rm.c | 44 +++++++++++++++++++++++++++++… | |
1 file changed, 42 insertions(+), 2 deletions(-) | |
--- | |
diff --git a/rm.c b/rm.c | |
@@ -11,9 +11,44 @@ usage(void) | |
eprintf("usage: %s [-f] [-iRr] file ...\n", argv0); | |
} | |
+static int | |
+forbidden(char *path, struct stat *root) | |
+{ | |
+ char *s, *t; | |
+ size_t n; | |
+ struct stat st; | |
+ static int w1, w2; | |
+ | |
+ n = strlen(path); | |
+ for (t = path + n; t > path && t[-1] == '/'; --t) | |
+ ; | |
+ for (s = t; s > path && s[-1] != '/'; --s) | |
+ ; | |
+ n = t - s; | |
+ if (n == 1 && *s == '.' || n == 2 && s[0] == '.' && s[1] == '.') { | |
+ if (!w1) | |
+ weprintf("\".\" and \"..\" may not be removed\n"); | |
+ w1 = 1; | |
+ return 1; | |
+ } | |
+ | |
+ if (stat(path, &st) < 0) | |
+ eprintf("stat argument '%s':", path); | |
+ if (st.st_dev == root->st_dev && st.st_ino == root->st_ino) { | |
+ if (!w2) | |
+ weprintf("\"/\" may not be removed\n"); | |
+ w2 = 1; | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
int | |
main(int argc, char *argv[]) | |
{ | |
+ char *s; | |
+ struct stat st; | |
struct recursor r = { .fn = rm, .maxdepth = 1, .follow = 'P' }; | |
ARGBEGIN { | |
@@ -38,9 +73,14 @@ main(int argc, char *argv[]) | |
return 0; | |
} | |
+ if (stat("/", &st) < 0) | |
+ eprintf("stat root:"); | |
for (; *argv; argc--, argv++) { | |
- if (strcmp(*argv, ".") && strcmp(*argv, "..")) | |
- recurse(AT_FDCWD, *argv, NULL, &r); | |
+ if (forbidden(*argv, &st)) { | |
+ rm_status = 1; | |
+ continue; | |
+ } | |
+ recurse(AT_FDCWD, *argv, NULL, &r); | |
} | |
return rm_status || recurse_status; |