2011-03-15 16:43:43 +01:00
|
|
|
#include <errno.h>
|
|
|
|
#include <ioall.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "filecopy.h"
|
|
|
|
|
2011-05-09 11:02:40 +02:00
|
|
|
char untrusted_namebuf[MAX_PATH_LENGTH];
|
2011-03-15 16:43:43 +01:00
|
|
|
void notify_progress(int p1, int p2)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int global_status_fd;
|
|
|
|
void do_exit(int code)
|
|
|
|
{
|
|
|
|
int codebuf = code;
|
|
|
|
write(global_status_fd, &codebuf, sizeof codebuf);
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-09 11:02:40 +02:00
|
|
|
void fix_times_and_perms(struct file_header *untrusted_hdr,
|
|
|
|
char *untrusted_name)
|
2011-03-15 16:43:43 +01:00
|
|
|
{
|
|
|
|
struct timeval times[2] =
|
2011-05-09 11:02:40 +02:00
|
|
|
{ {untrusted_hdr->atime, untrusted_hdr->atime_nsec / 1000},
|
|
|
|
{untrusted_hdr->mtime,
|
|
|
|
untrusted_hdr->mtime_nsec / 1000}
|
2011-03-15 16:43:43 +01:00
|
|
|
};
|
2011-05-09 11:02:40 +02:00
|
|
|
if (chmod(untrusted_name, untrusted_hdr->mode & 07777)) /* safe because of chroot */
|
2011-03-15 16:43:43 +01:00
|
|
|
do_exit(errno);
|
2011-05-09 11:02:40 +02:00
|
|
|
if (utimes(untrusted_name, times)) /* as above */
|
2011-03-15 16:43:43 +01:00
|
|
|
do_exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-05-09 11:02:40 +02:00
|
|
|
void process_one_file_reg(struct file_header *untrusted_hdr,
|
|
|
|
char *untrusted_name)
|
2011-03-15 16:43:43 +01:00
|
|
|
{
|
2011-03-29 13:05:57 +02:00
|
|
|
int ret;
|
2011-05-09 11:02:40 +02:00
|
|
|
int fdout = open(untrusted_name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0700); /* safe because of chroot */
|
2011-03-15 16:43:43 +01:00
|
|
|
if (fdout < 0)
|
|
|
|
do_exit(errno);
|
2011-05-09 11:02:40 +02:00
|
|
|
ret = copy_file(fdout, 0, untrusted_hdr->filelen);
|
2011-03-29 13:05:57 +02:00
|
|
|
if (ret != COPY_FILE_OK) {
|
|
|
|
if (ret == COPY_FILE_READ_EOF
|
|
|
|
|| ret == COPY_FILE_READ_ERROR)
|
|
|
|
do_exit(LEGAL_EOF); // hopefully remote will produce error message
|
|
|
|
else
|
|
|
|
do_exit(errno);
|
|
|
|
}
|
2011-03-15 16:43:43 +01:00
|
|
|
close(fdout);
|
2011-05-09 11:02:40 +02:00
|
|
|
fix_times_and_perms(untrusted_hdr, untrusted_name);
|
2011-03-15 16:43:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-09 11:02:40 +02:00
|
|
|
void process_one_file_dir(struct file_header *untrusted_hdr,
|
|
|
|
char *untrusted_name)
|
2011-03-15 16:43:43 +01:00
|
|
|
{
|
2011-03-16 12:48:29 +01:00
|
|
|
// fix perms only when the directory is sent for the second time
|
|
|
|
// it allows to transfer r.x directory contents, as we create it rwx initially
|
2011-05-09 11:02:40 +02:00
|
|
|
if (!mkdir(untrusted_name, 0700)) /* safe because of chroot */
|
2011-03-16 12:48:29 +01:00
|
|
|
return;
|
|
|
|
if (errno != EEXIST)
|
2011-03-15 16:43:43 +01:00
|
|
|
do_exit(errno);
|
2011-05-09 11:02:40 +02:00
|
|
|
fix_times_and_perms(untrusted_hdr, untrusted_name);
|
2011-03-15 16:43:43 +01:00
|
|
|
}
|
|
|
|
|
2011-05-09 11:02:40 +02:00
|
|
|
void process_one_file_link(struct file_header *untrusted_hdr,
|
|
|
|
char *untrusted_name)
|
2011-03-15 16:43:43 +01:00
|
|
|
{
|
2011-05-09 11:02:40 +02:00
|
|
|
char untrusted_content[MAX_PATH_LENGTH];
|
|
|
|
unsigned int filelen;
|
|
|
|
if (untrusted_hdr->filelen > MAX_PATH_LENGTH - 1)
|
2011-03-15 16:43:43 +01:00
|
|
|
do_exit(ENAMETOOLONG);
|
2011-05-09 11:02:40 +02:00
|
|
|
filelen = untrusted_hdr->filelen; /* sanitized above */
|
|
|
|
if (!read_all(0, untrusted_content, filelen))
|
2011-03-29 13:05:57 +02:00
|
|
|
do_exit(LEGAL_EOF); // hopefully remote has produced error message
|
2011-05-09 11:02:40 +02:00
|
|
|
untrusted_content[filelen] = 0;
|
|
|
|
if (symlink(untrusted_content, untrusted_name)) /* safe because of chroot */
|
2011-03-15 16:43:43 +01:00
|
|
|
do_exit(errno);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-05-09 11:02:40 +02:00
|
|
|
void process_one_file(struct file_header *untrusted_hdr)
|
2011-03-15 16:43:43 +01:00
|
|
|
{
|
2011-05-09 11:02:40 +02:00
|
|
|
unsigned int namelen;
|
|
|
|
if (untrusted_hdr->namelen > MAX_PATH_LENGTH - 1)
|
2011-03-15 16:43:43 +01:00
|
|
|
do_exit(ENAMETOOLONG);
|
2011-05-09 11:02:40 +02:00
|
|
|
namelen = untrusted_hdr->namelen; /* sanitized above */
|
|
|
|
if (!read_all(0, untrusted_namebuf, namelen))
|
2011-03-29 13:05:57 +02:00
|
|
|
do_exit(LEGAL_EOF); // hopefully remote has produced error message
|
2011-05-09 11:02:40 +02:00
|
|
|
untrusted_namebuf[namelen] = 0;
|
|
|
|
if (S_ISREG(untrusted_hdr->mode))
|
|
|
|
process_one_file_reg(untrusted_hdr, untrusted_namebuf);
|
|
|
|
else if (S_ISLNK(untrusted_hdr->mode))
|
|
|
|
process_one_file_link(untrusted_hdr, untrusted_namebuf);
|
|
|
|
else if (S_ISDIR(untrusted_hdr->mode))
|
|
|
|
process_one_file_dir(untrusted_hdr, untrusted_namebuf);
|
2011-03-15 16:43:43 +01:00
|
|
|
else
|
|
|
|
do_exit(EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void do_unpack(int fd)
|
|
|
|
{
|
|
|
|
global_status_fd = fd;
|
2011-05-09 11:02:40 +02:00
|
|
|
struct file_header untrusted_hdr;
|
|
|
|
while (read_all(0, &untrusted_hdr, sizeof untrusted_hdr))
|
|
|
|
process_one_file(&untrusted_hdr);
|
2011-03-15 16:43:43 +01:00
|
|
|
if (errno)
|
|
|
|
do_exit(errno);
|
|
|
|
else
|
|
|
|
do_exit(LEGAL_EOF);
|
|
|
|
}
|