sudo_mkdir_parents: make sure the path we created is a directory

For extra paranoia, verify that the directory we created is still
a directory before we fchown() it.
This commit is contained in:
Todd C. Miller
2021-12-11 16:27:33 -07:00
parent e66e1ca383
commit c53192eb7e

View File

@@ -43,6 +43,37 @@
#include "sudo_debug.h"
#include "sudo_util.h"
#ifndef O_NOFOLLOW
# define O_NOFOLLOW 0
#endif
/*
* Returns true if fd is a directory, else false.
* Warns on failure if not quiet.
*/
static bool
is_dir(int dfd, const char *name, int namelen, bool quiet)
{
struct stat sb;
debug_decl(is_dir, SUDO_DEBUG_UTIL);
if (fstat(dfd, &sb) != 0) {
if (!quiet) {
sudo_warn(U_("unable to stat %.*s"), namelen, name);
}
debug_return_bool(false);
}
if (!S_ISDIR(sb.st_mode)) {
if (!quiet) {
sudo_warnx(U_("%.*s exists but is not a directory (0%o)"),
namelen, name, (unsigned int) sb.st_mode);
}
debug_return_bool(false);
}
debug_return_bool(true);
}
/*
* Create any parent directories needed by path (but not path itself).
*/
@@ -75,7 +106,6 @@ sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool
for (cp = sudo_strsplit(cp, pathend, "/", &ep); cp != NULL && ep != NULL;
cp = sudo_strsplit(NULL, pathend, "/", &ep)) {
struct stat sb;
char name[MAXNAMLEN + 1];
int dfd, len;
@@ -100,7 +130,7 @@ reopen:
goto done;
}
if (mkdirat(parentfd, name, mode) == 0) {
dfd = openat(parentfd, name, O_RDONLY|O_NONBLOCK, 0);
dfd = openat(parentfd, name, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0);
if (dfd == -1) {
if (!quiet) {
sudo_warn(U_("unable to open %.*s"),
@@ -108,6 +138,11 @@ reopen:
}
goto done;
}
/* Make sure the path we created is still a directory. */
if (!is_dir(dfd, path, ep - path, quiet)) {
close(dfd);
goto done;
}
if (uid != (uid_t)-1 && gid != (gid_t)-1) {
if (fchown(dfd, uid, gid) != 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
@@ -126,19 +161,7 @@ reopen:
}
} else {
/* Already exists, make sure it is a directory. */
if (fstat(dfd, &sb) != 0) {
if (!quiet) {
sudo_warn(U_("unable to stat %.*s"),
(int)(ep - path), path);
}
close(dfd);
goto done;
}
if (!S_ISDIR(sb.st_mode)) {
if (!quiet) {
sudo_warnx(U_("%.*s exists but is not a directory (0%o)"),
(int)(ep - path), path, (unsigned int) sb.st_mode);
}
if (!is_dir(dfd, path, ep - path, quiet)) {
close(dfd);
goto done;
}