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:
@@ -43,6 +43,37 @@
|
|||||||
#include "sudo_debug.h"
|
#include "sudo_debug.h"
|
||||||
#include "sudo_util.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).
|
* 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;
|
for (cp = sudo_strsplit(cp, pathend, "/", &ep); cp != NULL && ep != NULL;
|
||||||
cp = sudo_strsplit(NULL, pathend, "/", &ep)) {
|
cp = sudo_strsplit(NULL, pathend, "/", &ep)) {
|
||||||
|
|
||||||
struct stat sb;
|
|
||||||
char name[MAXNAMLEN + 1];
|
char name[MAXNAMLEN + 1];
|
||||||
int dfd, len;
|
int dfd, len;
|
||||||
|
|
||||||
@@ -100,7 +130,7 @@ reopen:
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (mkdirat(parentfd, name, mode) == 0) {
|
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 (dfd == -1) {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
sudo_warn(U_("unable to open %.*s"),
|
sudo_warn(U_("unable to open %.*s"),
|
||||||
@@ -108,6 +138,11 @@ reopen:
|
|||||||
}
|
}
|
||||||
goto done;
|
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 (uid != (uid_t)-1 && gid != (gid_t)-1) {
|
||||||
if (fchown(dfd, uid, gid) != 0) {
|
if (fchown(dfd, uid, gid) != 0) {
|
||||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
|
||||||
@@ -126,19 +161,7 @@ reopen:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Already exists, make sure it is a directory. */
|
/* Already exists, make sure it is a directory. */
|
||||||
if (fstat(dfd, &sb) != 0) {
|
if (!is_dir(dfd, path, ep - path, quiet)) {
|
||||||
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);
|
|
||||||
}
|
|
||||||
close(dfd);
|
close(dfd);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user