#define _GNU_SOURCE /* For O_NOFOLLOW. */ #include #include #include #include #include #include #include #include #include "filecopy.h" #include "crc32.h" char untrusted_namebuf[MAX_PATH_LENGTH]; void notify_progress(int p1, int p2) { } unsigned long crc32_sum = 0; int read_all_with_crc(int fd, void *buf, int size) { int ret; ret = read_all(fd, buf, size); if (ret) crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size); return ret; } int global_status_fd; void do_exit(int code) { int codebuf = code; write(global_status_fd, &codebuf, sizeof codebuf); exit(0); } void fix_times_and_perms(struct file_header *untrusted_hdr, char *untrusted_name) { struct timeval times[2] = { {untrusted_hdr->atime, untrusted_hdr->atime_nsec / 1000}, {untrusted_hdr->mtime, untrusted_hdr->mtime_nsec / 1000} }; if (chmod(untrusted_name, untrusted_hdr->mode & 07777)) /* safe because of chroot */ do_exit(errno); if (utimes(untrusted_name, times)) /* as above */ do_exit(errno); } void process_one_file_reg(struct file_header *untrusted_hdr, char *untrusted_name) { int ret; int fdout = open(untrusted_name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0700); /* safe because of chroot */ if (fdout < 0) do_exit(errno); ret = copy_file(fdout, 0, untrusted_hdr->filelen, &crc32_sum); 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); } close(fdout); fix_times_and_perms(untrusted_hdr, untrusted_name); } void process_one_file_dir(struct file_header *untrusted_hdr, char *untrusted_name) { // 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 if (!mkdir(untrusted_name, 0700)) /* safe because of chroot */ return; if (errno != EEXIST) do_exit(errno); fix_times_and_perms(untrusted_hdr, untrusted_name); } void process_one_file_link(struct file_header *untrusted_hdr, char *untrusted_name) { char untrusted_content[MAX_PATH_LENGTH]; unsigned int filelen; if (untrusted_hdr->filelen > MAX_PATH_LENGTH - 1) do_exit(ENAMETOOLONG); filelen = untrusted_hdr->filelen; /* sanitized above */ if (!read_all_with_crc(0, untrusted_content, filelen)) do_exit(LEGAL_EOF); // hopefully remote has produced error message untrusted_content[filelen] = 0; if (symlink(untrusted_content, untrusted_name)) /* safe because of chroot */ do_exit(errno); } void process_one_file(struct file_header *untrusted_hdr) { unsigned int namelen; if (untrusted_hdr->namelen > MAX_PATH_LENGTH - 1) do_exit(ENAMETOOLONG); namelen = untrusted_hdr->namelen; /* sanitized above */ if (!read_all_with_crc(0, untrusted_namebuf, namelen)) do_exit(LEGAL_EOF); // hopefully remote has produced error message 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); else do_exit(EINVAL); } void send_status_and_crc() { struct result_header hdr; int saved_errno; saved_errno = errno; hdr.error_code = errno; hdr.crc32 = crc32_sum; write_all(1, &hdr, sizeof(hdr)); errno = saved_errno; } void do_unpack(int fd) { global_status_fd = fd; struct file_header untrusted_hdr; /* initialize checksum */ crc32_sum = 0; while (read_all_with_crc(0, &untrusted_hdr, sizeof untrusted_hdr)) { /* check for end of transfer marker */ if (untrusted_hdr.namelen == 0) { errno = 0; break; } process_one_file(&untrusted_hdr); } send_status_and_crc(); if (errno) do_exit(errno); else do_exit(LEGAL_EOF); }