logo

utils-std

Collection of commonly available Unix tools
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:

Mcmd/mkdir.c83+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
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;