167 líneas
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			167 líneas
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #define _GNU_SOURCE /* For O_NOFOLLOW. */
 | |
| #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"
 | |
| #include "crc32.h"
 | |
| 
 | |
| char untrusted_namebuf[MAX_PATH_LENGTH];
 | |
| long long bytes_limit = 0;
 | |
| long long files_limit = 0;
 | |
| long long total_bytes = 0;
 | |
| long long total_files = 0;
 | |
| 
 | |
| void notify_progress(int p1, int p2)
 | |
| {
 | |
| }
 | |
| 
 | |
| void set_size_limit(long long new_bytes_limit, long long new_files_limit)
 | |
| {
 | |
| 	bytes_limit = new_bytes_limit;
 | |
| 	files_limit = new_files_limit;
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 	total_bytes += untrusted_hdr->filelen;
 | |
| 	if (bytes_limit && total_bytes > bytes_limit)
 | |
| 		do_exit(EDQUOT);
 | |
| 	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);
 | |
| 		total_files++;
 | |
| 		if (files_limit && total_files > files_limit)
 | |
| 			do_exit(EDQUOT);
 | |
| 	}
 | |
| 	send_status_and_crc();
 | |
| 	if (errno)
 | |
| 		do_exit(errno);
 | |
| 	else
 | |
| 		do_exit(LEGAL_EOF);
 | |
| }
 | 
