minijail: add --no-fs-restrictions flag.

This is a general flag, instead of being specific to disabling
profile-fs-restrictions, because future versions of the default
runtime environment may also include Landlock.

BUG=b:264449526
TEST=security.Minijail*
TEST=minijail unit tests, including new tests

Change-Id: I12fbb87f1e2307b16057976187fe640297bab30b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/minijail/+/4136129
Tested-by: Ben Scarlato <akhna@google.com>
Reviewed-by: Allen Webb <allenwebb@google.com>
Commit-Queue: Ben Scarlato <akhna@google.com>
Auto-Submit: Ben Scarlato <akhna@google.com>
diff --git a/libminijail.c b/libminijail.c
index 38a5148..091e50b 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -165,6 +165,7 @@
 		bool forward_signals : 1;
 		bool setsid : 1;
 		bool using_minimalistic_mountns : 1;
+		bool enable_fs_restrictions : 1;
 		bool enable_profile_fs_restrictions : 1;
 		bool enable_default_runtime : 1;
 	} flags;
@@ -368,6 +369,7 @@
 	int remount_proc_ro = j->flags.remount_proc_ro;
 	int userns = j->flags.userns;
 	int using_minimalistic_mountns = j->flags.using_minimalistic_mountns;
+	int enable_fs_restrictions = j->flags.enable_fs_restrictions;
 	int enable_profile_fs_restrictions = j->flags.enable_profile_fs_restrictions;
 	int enable_default_runtime = j->flags.enable_default_runtime;
 	if (j->user)
@@ -391,6 +393,7 @@
 	j->flags.remount_proc_ro = remount_proc_ro;
 	j->flags.userns = userns;
 	j->flags.using_minimalistic_mountns = using_minimalistic_mountns;
+	j->flags.enable_fs_restrictions = enable_fs_restrictions;
 	j->flags.enable_profile_fs_restrictions = enable_profile_fs_restrictions;
 	j->flags.enable_default_runtime = enable_default_runtime;
 	/* Note, |pids| will already have been used before this call. */
@@ -404,6 +407,7 @@
 	if (j) {
 		j->remount_mode = MS_PRIVATE;
 		j->flags.using_minimalistic_mountns = false;
+		j->flags.enable_fs_restrictions = true;
 		j->flags.enable_profile_fs_restrictions = true;
 		j->flags.enable_default_runtime = true;
 	}
@@ -570,6 +574,10 @@
 	return j->flags.enable_default_runtime;
 }
 
+void API minijail_disable_fs_restrictions(struct minijail *j) {
+	j->flags.enable_fs_restrictions = false;
+}
+
 void API minijail_set_enable_profile_fs_restrictions(struct minijail *j)
 {
 	j->flags.enable_profile_fs_restrictions = true;
@@ -2492,6 +2500,9 @@
 {
 	struct fs_rule *r;
 	attribute_cleanup_fd int ruleset_fd = -1;
+	if (!j->flags.enable_fs_restrictions) {
+		return;
+	}
 
 	r = j->fs_rules_head;
 	while (r) {
diff --git a/libminijail.h b/libminijail.h
index 9cba632..9c15dfc 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -117,6 +117,7 @@
  * Exposed for unit tests and allowlisting services.
  */
 bool minijail_get_enable_default_runtime(struct minijail *j);
+void minijail_disable_fs_restrictions(struct minijail *j);
 void minijail_set_enable_profile_fs_restrictions(struct minijail *j);
 void minijail_add_minimalistic_mountns_fs_rules(struct minijail *j);
 void minijail_enable_default_fs_restrictions(struct minijail *j);
diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc
index 12b790d..97bbef3 100644
--- a/libminijail_unittest.cc
+++ b/libminijail_unittest.cc
@@ -1715,6 +1715,29 @@
   EXPECT_NE(status, 0);
 }
 
+TEST_F(LandlockTest, test_fs_rules_disabled) {
+  int mj_run_ret;
+  int status;
+  char *argv[4];
+  if (!run_landlock_tests_)
+    GTEST_SKIP();
+  ScopedMinijail j(minijail_new());
+  SetupLandlockTestingNamespaces(j.get());
+  minijail_add_fs_restriction_rx(j.get(), kBinPath);
+  minijail_disable_fs_restrictions(j.get());
+
+  argv[0] = const_cast<char*>(kShellPath);
+  argv[1] = "-c";
+  argv[2] = "exec echo 'bar' > /tmp/fs-rules-test";
+  argv[3] = NULL;
+
+  mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv);
+  EXPECT_EQ(mj_run_ret, 0);
+  status = minijail_wait(j.get());
+  // Rules aren't applied, so cmd succeeds.
+  EXPECT_EQ(status, 0);
+}
+
 TEST_F(LandlockTest, test_rule_allow_symlinks_advanced_rw) {
   int mj_run_ret;
   int status;
diff --git a/minijail0_cli.c b/minijail0_cli.c
index b2cacb54..a8105b3 100644
--- a/minijail0_cli.c
+++ b/minijail0_cli.c
@@ -499,6 +499,7 @@
 	OPT_GEN_CONFIG,
 	OPT_LOGGING,
 	OPT_NO_DEFAULT_RUNTIME,
+	OPT_NO_FS_RESTRICTIONS,
 	OPT_PRELOAD_LIBRARY,
 	OPT_PROFILE,
 	OPT_SECCOMP_BPF_BINARY,
@@ -542,6 +543,8 @@
     {"fs-path-advanced-rw", required_argument, 0, OPT_FS_PATH_ADVANCED_RW},
     {"no-default-runtime-environment", no_argument, 0,
       OPT_NO_DEFAULT_RUNTIME},
+    {"no-fs-restrictions", no_argument, 0,
+      OPT_NO_FS_RESTRICTIONS},
     {0, 0, 0, 0},
 };
 
@@ -674,6 +677,8 @@
 "               Adds an allowed read-write path.\n"
 "  --fs-path-advanced-rw\n"
 "               Adds an allowed advanced read-write path.\n"
+"  --no-fs-restrictions\n"
+"               Disables path-based filesystem restrictions.\n"
 "  --no-default-runtime-environment\n"
 "               Disables default seccomp policy and setting of no_new_privs.\n"
 "  --preload-library=<file>\n"
@@ -858,6 +863,8 @@
 	struct option_entry *opt_entry_head = NULL;
 	struct option_entry *opt_entry_tail = NULL;
 	char* config_path = NULL;
+	bool fs_path_flag_used = false;
+	bool fs_path_rules_enabled = true;
 
 	while ((opt = getopt_conf_or_cli(argc, argv, &conf_entry_list,
 					 &conf_index)) != -1) {
@@ -1128,18 +1135,27 @@
 			break;
 		case OPT_FS_DEFAULT_PATHS:
 			minijail_enable_default_fs_restrictions(j);
+			fs_path_flag_used = true;
 			break;
 		case OPT_FS_PATH_RX:
 			minijail_add_fs_restriction_rx(j, optarg);
+			fs_path_flag_used = true;
 			break;
 		case OPT_FS_PATH_RO:
 			minijail_add_fs_restriction_ro(j, optarg);
+			fs_path_flag_used = true;
 			break;
 		case OPT_FS_PATH_RW:
 			minijail_add_fs_restriction_rw(j, optarg);
+			fs_path_flag_used = true;
 			break;
 		case OPT_FS_PATH_ADVANCED_RW:
 			minijail_add_fs_restriction_advanced_rw(j, optarg);
+			fs_path_flag_used = true;
+			break;
+		case OPT_NO_FS_RESTRICTIONS:
+			minijail_disable_fs_restrictions(j);
+			fs_path_rules_enabled = false;
 			break;
 		case OPT_NO_DEFAULT_RUNTIME:
 			minijail_set_enable_default_runtime(j, false);
@@ -1262,6 +1278,11 @@
 		}
 	}
 
+	if (fs_path_flag_used && !fs_path_rules_enabled) {
+		errx(1, "Can't combine --no-fs-restrictions "
+			"with directly using fs path flags");
+	}
+
 	/* Handle config file generation. */
 	if (gen_config) {
 		struct option_entry *r = opt_entry_head;
diff --git a/minijail0_cli_unittest.cc b/minijail0_cli_unittest.cc
index 7a93537..d6299f3 100644
--- a/minijail0_cli_unittest.cc
+++ b/minijail0_cli_unittest.cc
@@ -338,6 +338,23 @@
   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
 }
 
+// Valid usage of the no-fs-restrictions option.
+TEST_F(CliTest, valid_no_fs_restrictions) {
+  std::vector<std::string> argv = {"--profile", "minimalistic-mountns",
+                                   "--no-fs-restrictions", "/bin/sh"};
+
+  ASSERT_TRUE(parse_args_(argv));
+}
+
+// Invalid usage of the no-fs-restrictions option.
+TEST_F(CliTest, invalid_no_fs_restrictions) {
+  // Using an fs-path-* flag at the same time shouldn't be allowed.
+  std::vector<std::string> argv = {"--fs-path-rx", "/",
+                                   "--no-fs-restrictions", "/bin/sh"};
+
+   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
+}
+
 // Valid calls to the chroot option.
 TEST_F(CliTest, valid_chroot) {
   std::vector<std::string> argv = {"-C", "/", "/bin/sh"};