mirror of
https://github.com/kdave/btrfs-progs
synced 2025-04-27 05:27:56 +00:00
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>
This commit is contained in:
parent
7732e6ef62
commit
9a86668071
@ -7,7 +7,7 @@ btrfs-receive - receive subvolumes from stdin/file.
|
|||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
*btrfs receive* [-ve] [-f <infile>] [--max-errors <N>] <mount>
|
*btrfs receive* [options] <mount>
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
@ -34,6 +34,8 @@ verbose level more.
|
|||||||
-f <infile>::
|
-f <infile>::
|
||||||
By default, btrfs receive uses stdin to receive the subvolumes.
|
By default, btrfs receive uses stdin to receive the subvolumes.
|
||||||
Use this option to specify a file to use instead.
|
Use this option to specify a file to use instead.
|
||||||
|
-C|--chroot::
|
||||||
|
Confine the process to <mount> using chroot.
|
||||||
-e::
|
-e::
|
||||||
Terminate after receiving an <end cmd> in the data stream.
|
Terminate after receiving an <end cmd> in the data stream.
|
||||||
Without this option, the receiver terminates only if an error is recognized
|
Without this option, the receiver terminates only if an error is recognized
|
||||||
|
@ -61,6 +61,7 @@ struct btrfs_receive
|
|||||||
char *root_path;
|
char *root_path;
|
||||||
char *dest_dir_path; /* relative to root_path */
|
char *dest_dir_path; /* relative to root_path */
|
||||||
char *full_subvol_path;
|
char *full_subvol_path;
|
||||||
|
int dest_dir_chroot;
|
||||||
|
|
||||||
struct subvol_info *cur_subvol;
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (r->dest_dir_chroot) {
|
||||||
* find_mount_root returns a root_path that is a subpath of
|
if (chroot(dest_dir_full_path)) {
|
||||||
* dest_dir_full_path. Now get the other part of root_path,
|
ret = -errno;
|
||||||
* which is the destination dir relative to root_path.
|
fprintf(stderr,
|
||||||
*/
|
"ERROR: failed to chroot to %s, %s\n",
|
||||||
r->dest_dir_path = dest_dir_full_path + strlen(r->root_path);
|
dest_dir_full_path,
|
||||||
while (r->dest_dir_path[0] == '/')
|
strerror(-ret));
|
||||||
r->dest_dir_path++;
|
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);
|
ret = subvol_uuid_search_init(r->mnt_fd, &r->sus);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -930,15 +955,17 @@ int cmd_receive(int argc, char **argv)
|
|||||||
r.mnt_fd = -1;
|
r.mnt_fd = -1;
|
||||||
r.write_fd = -1;
|
r.write_fd = -1;
|
||||||
r.dest_dir_fd = -1;
|
r.dest_dir_fd = -1;
|
||||||
|
r.dest_dir_chroot = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int c;
|
int c;
|
||||||
static const struct option long_opts[] = {
|
static const struct option long_opts[] = {
|
||||||
{ "max-errors", required_argument, NULL, 'E' },
|
{ "max-errors", required_argument, NULL, 'E' },
|
||||||
|
{ "chroot", no_argument, NULL, 'C' },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ 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)
|
if (c < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -952,6 +979,9 @@ int cmd_receive(int argc, char **argv)
|
|||||||
case 'e':
|
case 'e':
|
||||||
r.honor_end_cmd = 1;
|
r.honor_end_cmd = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
r.dest_dir_chroot = 1;
|
||||||
|
break;
|
||||||
case 'E':
|
case 'E':
|
||||||
max_errors = arg_strtou64(optarg);
|
max_errors = arg_strtou64(optarg);
|
||||||
break;
|
break;
|
||||||
@ -1001,6 +1031,7 @@ const char * const cmd_receive_usage[] = {
|
|||||||
" in the data stream. Without this option,",
|
" in the data stream. Without this option,",
|
||||||
" the receiver terminates only if an error",
|
" the receiver terminates only if an error",
|
||||||
" is recognized or on EOF.",
|
" 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",
|
"--max-errors <N> Terminate as soon as N errors happened while",
|
||||||
" processing commands from the send stream.",
|
" processing commands from the send stream.",
|
||||||
" Default value is 1. A value of 0 means no limit.",
|
" Default value is 1. A value of 0 means no limit.",
|
||||||
|
Loading…
Reference in New Issue
Block a user