aboutsummaryrefslogtreecommitdiffstats
path: root/cmds-receive.c
diff options
context:
space:
mode:
authorLauri Võsandi <lauri.vosandi@gmail.com>2015-04-19 14:46:28 +0300
committerDavid Sterba <dsterba@suse.cz>2015-04-24 15:42:05 +0200
commit9a86668071e77b2da0173b8921153de4d7ba99d8 (patch)
treeae76c78a8f776f1815a4824817a52ce64d2b8bcb /cmds-receive.c
parent7732e6ef62159cc942897df0a358b784b62658c1 (diff)
downloadbtrfs-progs-9a86668071e77b2da0173b8921153de4d7ba99d8.tar.gz
btrfs-progs-9a86668071e77b2da0173b8921153de4d7ba99d8.tar.xz
btrfs-progs-9a86668071e77b2da0173b8921153de4d7ba99d8.zip
btrfs-progs: optionally enforce chroot for btrfs receive
This patch forces btrfs receive to issue chroot before parsing the btrfs stream using command-line flag -C to confine the process and minimize damage that could be done via malicious btrfs stream. Signed-off-by: Lauri Võsandi <lauri.vosandi@gmail.com> [added long option variant, added docs] Signed-off-by: David Sterba <dsterba@suse.cz>
Diffstat (limited to 'cmds-receive.c')
-rw-r--r--cmds-receive.c49
1 files changed, 40 insertions, 9 deletions
diff --git a/cmds-receive.c b/cmds-receive.c
index 919dc9e..3505c76 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -61,6 +61,7 @@ struct btrfs_receive
char *root_path;
char *dest_dir_path; /* relative to root_path */
char *full_subvol_path;
+ int dest_dir_chroot;
struct subvol_info *cur_subvol;
@@ -858,14 +859,38 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd,
goto out;
}
- /*
- * find_mount_root returns a root_path that is a subpath of
- * dest_dir_full_path. Now get the other part of root_path,
- * which is the destination dir relative to root_path.
- */
- r->dest_dir_path = dest_dir_full_path + strlen(r->root_path);
- while (r->dest_dir_path[0] == '/')
- r->dest_dir_path++;
+ if (r->dest_dir_chroot) {
+ if (chroot(dest_dir_full_path)) {
+ ret = -errno;
+ fprintf(stderr,
+ "ERROR: failed to chroot to %s, %s\n",
+ dest_dir_full_path,
+ strerror(-ret));
+ goto out;
+ }
+ if (chdir("/")) {
+ ret = -errno;
+ fprintf(stderr,
+ "ERROR: failed to chdir to /, %s\n",
+ strerror(-ret));
+ goto out;
+ }
+ if (g_verbose >= 1) {
+ fprintf(stderr, "chrooted to %s\n",
+ dest_dir_full_path);
+ }
+ r->root_path = strdup("/");
+ r->dest_dir_path = r->root_path;
+ } else {
+ /*
+ * find_mount_root returns a root_path that is a subpath of
+ * dest_dir_full_path. Now get the other part of root_path,
+ * which is the destination dir relative to root_path.
+ */
+ r->dest_dir_path = dest_dir_full_path + strlen(r->root_path);
+ while (r->dest_dir_path[0] == '/')
+ r->dest_dir_path++;
+ }
ret = subvol_uuid_search_init(r->mnt_fd, &r->sus);
if (ret < 0)
@@ -930,15 +955,17 @@ int cmd_receive(int argc, char **argv)
r.mnt_fd = -1;
r.write_fd = -1;
r.dest_dir_fd = -1;
+ r.dest_dir_chroot = 0;
while (1) {
int c;
static const struct option long_opts[] = {
{ "max-errors", required_argument, NULL, 'E' },
+ { "chroot", no_argument, NULL, 'C' },
{ NULL, 0, NULL, 0 }
};
- c = getopt_long(argc, argv, "evf:", long_opts, NULL);
+ c = getopt_long(argc, argv, "Cevf:", long_opts, NULL);
if (c < 0)
break;
@@ -952,6 +979,9 @@ int cmd_receive(int argc, char **argv)
case 'e':
r.honor_end_cmd = 1;
break;
+ case 'C':
+ r.dest_dir_chroot = 1;
+ break;
case 'E':
max_errors = arg_strtou64(optarg);
break;
@@ -1001,6 +1031,7 @@ const char * const cmd_receive_usage[] = {
" in the data stream. Without this option,",
" the receiver terminates only if an error",
" is recognized or on EOF.",
+ "-C|--chroot confine the process to <mount> using chroot",
"--max-errors <N> Terminate as soon as N errors happened while",
" processing commands from the send stream.",
" Default value is 1. A value of 0 means no limit.",