diff mbox

btrfs-progs: optionally enforce chroot for btrfs receive

Message ID 1429365545-2419-1-git-send-email-lauri.vosandi@gmail.com (mailing list archive)
State Superseded
Headers show

Commit Message

lauri April 18, 2015, 1:59 p.m. UTC
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>
---
 cmds-receive.c | 37 ++++++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 9 deletions(-)

Comments

Mike Fleetwood April 19, 2015, 7:25 a.m. UTC | #1
On 18 April 2015 at 14:59, Lauri Võsandi <lauri.vosandi@gmail.com> wrote:
> 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>
> ---
>  cmds-receive.c | 37 ++++++++++++++++++++++++++++---------
>  1 file changed, 28 insertions(+), 9 deletions(-)
>
> diff --git a/cmds-receive.c b/cmds-receive.c
> index 44ef27e..73bd88b 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;
>
> @@ -867,14 +868,27 @@ 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));

There appears to be a goto out missing here.

> +               }
> +               if (g_verbose >= 1) {
> +                       fprintf(stderr, "chrooted to %s\n",
> +                               dest_dir_full_path);
> +               }
> +               r->root_path = r->dest_dir_path = strdup("/");
> +       }
>
>         ret = subvol_uuid_search_init(r->mnt_fd, &r->sus);
>         if (ret < 0)
> @@ -940,6 +954,7 @@ int cmd_receive(int argc, char **argv)
>         r.write_fd = -1;
>         r.dest_dir_fd = -1;
>         r.explicit_parent = NULL;
> +       r.dest_dir_chroot = 0;
>
>         while (1) {
>                 int c;
> @@ -948,7 +963,7 @@ int cmd_receive(int argc, char **argv)
>                         { NULL, 0, NULL, 0 }
>                 };
>
> -               c = getopt_long(argc, argv, "evf:p:", long_opts, NULL);
> +               c = getopt_long(argc, argv, "Cevf:p:", long_opts, NULL);
>                 if (c < 0)
>                         break;
>
> @@ -962,6 +977,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;
> @@ -1014,6 +1032,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               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.",
> --
> 1.9.1

Mike
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/cmds-receive.c b/cmds-receive.c
index 44ef27e..73bd88b 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;
 
@@ -867,14 +868,27 @@  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));
+		}
+		if (g_verbose >= 1) {
+			fprintf(stderr, "chrooted to %s\n",
+				dest_dir_full_path);
+		}
+		r->root_path = r->dest_dir_path = strdup("/");
+	}
 
 	ret = subvol_uuid_search_init(r->mnt_fd, &r->sus);
 	if (ret < 0)
@@ -940,6 +954,7 @@  int cmd_receive(int argc, char **argv)
 	r.write_fd = -1;
 	r.dest_dir_fd = -1;
 	r.explicit_parent = NULL;
+	r.dest_dir_chroot = 0;
 
 	while (1) {
 		int c;
@@ -948,7 +963,7 @@  int cmd_receive(int argc, char **argv)
 			{ NULL, 0, NULL, 0 }
 		};
 
-		c = getopt_long(argc, argv, "evf:p:", long_opts, NULL);
+		c = getopt_long(argc, argv, "Cevf:p:", long_opts, NULL);
 		if (c < 0)
 			break;
 
@@ -962,6 +977,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;
@@ -1014,6 +1032,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               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.",