filecopy: prevent files/dirs movement outside incoming directory during transfer

Otherwise, when the user moves directory, which is still in transfer,
somewhere else, it could allow malicious source domain to escape chroot
and place a file in arbitrary location.

It looks like bind mount is just enough - simple rename fails with
EXDEV, so tools are forced to perform copy+delete, which is enough to
keep unpacker process away from new file location.

One inconvenient detail is that we must clean the mount after transfer
finishes, so root perms cannot be dropped completely. We keep separate
process for only that reason.
This commit is contained in:
Marek Marczykowski-Górecki 2015-01-11 05:42:15 +01:00
parent 50b536bee3
commit efc7d4d1f2

View File

@ -5,6 +5,9 @@
#include <stdlib.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/fsuid.h>
@ -34,8 +37,11 @@ int prepare_creds_return_uid(const char *username)
int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unused__)))
{
char *incoming_dir;
int uid;
int uid, ret;
pid_t pid;
const char *remote_domain;
char *procdir_path;
int procfs_fd;
uid = prepare_creds_return_uid("user");
@ -50,9 +56,36 @@ int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unuse
mkdir(incoming_dir, 0700);
if (chdir(incoming_dir))
gui_fatal("Error chdir to %s", incoming_dir);
if (chroot(incoming_dir)) //impossible
if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0)
gui_fatal("Failed to mount a directory %s", incoming_dir);
/* parse the input in unprivileged child process, parent will hold root
* access to unmount incoming dir */
switch (pid=fork()) {
case -1:
gui_fatal("Failed to create new process");
case 0:
asprintf(&procdir_path, "/proc/%d/fd", getpid());
procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY);
if (procfs_fd < 0)
gui_fatal("Failed to open /proc");
free(procdir_path);
set_procfs_fd(procfs_fd);
if (chroot("."))
gui_fatal("Error chroot to %s", incoming_dir);
if (setuid(uid) < 0)
gui_fatal("Error changing permissions to '%s'", "user");
if (setuid(uid) < 0) {
/* no kdialog inside chroot */
perror("setuid");
exit(1);
}
return do_unpack();
}
if (waitpid(pid, &ret, 0) < 0) {
gui_fatal("Failed to wait for child process");
}
if (umount2(".", MNT_DETACH) < 0)
gui_fatal("Cannot umount incoming directory");
return ret;
}