commit: 83a9997e35618e884044c769960a794986990cf3
parent 436a3dd3efeb509446a7ea27cb26cdbb5fd42382
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Thu, 11 Apr 2024 10:02:22 +0200
cmd/mkdir: Fix TOCTOU issues with -p
For once flawfinder was right…
Diffstat:
M | cmd/mkdir.c | 83 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
1 file changed, 52 insertions(+), 31 deletions(-)
diff --git a/cmd/mkdir.c b/cmd/mkdir.c
@@ -16,50 +16,62 @@
#include <sys/stat.h> // mkdir
#include <unistd.h> // getopt
-bool parents = false, verbose = false;
+bool verbose = false;
mode_t filemask;
static int
-do_mkdir(char *path, mode_t mode)
+mkdir_simple(char *path, mode_t mode)
{
- for(int i = strlen(path) - 1; i >= 0; i--)
+ assert(errno == 0);
+ if(mkdir(path, mode) < 0)
{
- if(path[i] != '/')
- break;
- else
- path[i] = 0;
+ fprintf(stderr, "mkdir: Failed making directory '%s': %s\n", path, strerror(errno));
+ errno = 0;
+ return -1;
}
- if(parents)
+ if(verbose) fprintf(stderr, "mkdir: Made directory: %s\n", path);
+
+ return 0;
+}
+
+static int
+mkdir_parents(char *path, mode_t mode)
+{
+ assert(errno == 0);
+
+ for(int i = strlen(path) - 1; i >= 0; i--)
{
- assert(errno == 0);
- /* flawfinder: ignore */
- if(access(path, F_OK) == 0) return 0;
+ if(path[i] != '/') break;
- errno = 0;
+ path[i] = 0;
+ }
- char parent[PATH_MAX] = "";
- strncpy(parent, path, PATH_MAX);
+ char parent[PATH_MAX] = "";
+ strncpy(parent, path, PATH_MAX);
- for(int i = strlen(parent)-1; i >= 0; i--)
- {
- char c = parent[i];
- parent[i] = 0;
- if(c == '/') break;
- }
+ for(int i = strlen(parent) - 1; i >= 0; i--)
+ {
+ if(parent[i] == '/') break;
- /* flawfinder: ignore */
- if(parent[0] != 0 && access(parent, F_OK) != 0)
- {
- errno = 0;
- mode_t parent_mode = (S_IWUSR | S_IXUSR | ~filemask) & 0777;
- if(do_mkdir(parent, parent_mode) < 0) return -1;
- }
+ parent[i] = 0;
}
+ if(path[0] == 0) return 0;
+
+ mode_t parent_mode = (S_IWUSR | S_IXUSR | ~filemask) & 0777;
+
+ if(mkdir_parents(parent, parent_mode) < 0) return -1;
+
assert(errno == 0);
if(mkdir(path, mode) < 0)
{
+ if(errno == EEXIST)
+ {
+ errno = 0;
+ return 0;
+ }
+
fprintf(stderr, "mkdir: Failed making directory '%s': %s\n", path, strerror(errno));
errno = 0;
return -1;
@@ -79,20 +91,22 @@ usage()
int
main(int argc, char *argv[])
{
+ // clang-format off
+ mode_t mode = (S_IRWXU | S_IRWXG | S_IRWXO | ~filemask) & 0777;
+ bool opt_p = false;
const char *errstr = NULL;
+ // clang-format on
filemask = umask(0);
umask(filemask);
- mode_t mode = (S_IRWXU | S_IRWXG | S_IRWXO | ~filemask) & 0777;
-
int c = -1;
while((c = getopt(argc, argv, ":pvm:")) != -1)
{
switch(c)
{
case 'p':
- parents = true;
+ opt_p = true;
break;
case 'v':
verbose = true;
@@ -132,7 +146,14 @@ main(int argc, char *argv[])
for(int i = 0; i < argc; i++)
{
- if(do_mkdir(argv[i], mode) < 0) return 1;
+ int ret = 0;
+
+ if(opt_p)
+ ret = mkdir_parents(argv[i], mode);
+ else
+ ret = mkdir_simple(argv[i], mode);
+
+ if(ret < 0) return 1;
}
return 0;