Commit Diff


commit - 485796a93055f3ab431b76d802404ee1f07f5c5a
commit + 7be09703f7d201cafeb2dc434f8d126475d39384
blob - e31ec2aab2c8e9ee7fb99c308ad6a6f3174affe0
blob + 18faddf332d53538a16d8ceeb2b47af6ba6be5ca
--- sandbox.c
+++ sandbox.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -80,8 +81,6 @@ sandbox_ui_process(void)
  * have the struct declarations, libc wrappers are missing.  The
  * sample landlock code provided by the authors includes these "shims"
  * in their example for the landlock API until libc provides them.
- *
- * Linux is such a mess sometimes.  /rant
  */
 
 #ifndef landlock_create_ruleset
@@ -110,96 +109,149 @@ landlock_restrict_self(int ruleset_fd, __u32 flags)
 }
 #endif
 
-static int
-open_landlock(void)
-{
-	int fd;
-	struct landlock_ruleset_attr attr = {
-		.handled_access_fs =	LANDLOCK_ACCESS_FS_EXECUTE |
-					LANDLOCK_ACCESS_FS_READ_FILE |
-					LANDLOCK_ACCESS_FS_READ_DIR |
-					LANDLOCK_ACCESS_FS_WRITE_FILE |
-					LANDLOCK_ACCESS_FS_REMOVE_DIR |
-					LANDLOCK_ACCESS_FS_REMOVE_FILE |
-					LANDLOCK_ACCESS_FS_MAKE_CHAR |
-					LANDLOCK_ACCESS_FS_MAKE_DIR |
-					LANDLOCK_ACCESS_FS_MAKE_REG |
-					LANDLOCK_ACCESS_FS_MAKE_SOCK |
-					LANDLOCK_ACCESS_FS_MAKE_FIFO |
-					LANDLOCK_ACCESS_FS_MAKE_BLOCK |
-					LANDLOCK_ACCESS_FS_MAKE_SYM,
-	};
+/*
+ * Maybe we should ship with a full copy of the linux headers because
+ * you never know...
+ */
 
-	fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
-	if (fd == -1) {
-		switch (errno) {
-		case ENOSYS:
-		case EOPNOTSUPP:
-			return -1;
-		default:
-			err(1, "can't create landlock ruleset");
-		}
-	}
-	return fd;
-}
+#ifndef LANDLOCK_ACCESS_FS_REFER
+#define LANDLOCK_ACCESS_FS_REFER	(1ULL << 13)
+#endif
 
+#ifndef LANDLOCK_ACCESS_FS_TRUNCATE
+#define LANDLOCK_ACCESS_FS_TRUNCATE	(1ULL << 14)
+#endif
+
+static int landlock_state;
+static int landlock_fd;
+
+/*
+ * Initialize landlock, which is stupidly complicated.
+ */
 static int
-landlock_unveil(int landlock_fd, const char *path, int perms)
+landlock_init(void)
 {
-	struct landlock_path_beneath_attr pb;
-	int err, saved_errno;
+	struct landlock_ruleset_attr rattr = {
+		/*
+		 * List all capabilities currently defined by landlock.
+		 * Failure in doing so will implicitly allow those actions
+		 * (i.e. omitting READ_FILE will allow to read _any_ file.)
+		 */
+		.handled_access_fs =
+		LANDLOCK_ACCESS_FS_EXECUTE |
+		LANDLOCK_ACCESS_FS_READ_FILE |
+		LANDLOCK_ACCESS_FS_READ_DIR |
+		LANDLOCK_ACCESS_FS_WRITE_FILE |
+		LANDLOCK_ACCESS_FS_REMOVE_DIR |
+		LANDLOCK_ACCESS_FS_REMOVE_FILE |
+		LANDLOCK_ACCESS_FS_MAKE_CHAR |
+		LANDLOCK_ACCESS_FS_MAKE_DIR |
+		LANDLOCK_ACCESS_FS_MAKE_REG |
+		LANDLOCK_ACCESS_FS_MAKE_SOCK |
+		LANDLOCK_ACCESS_FS_MAKE_FIFO |
+		LANDLOCK_ACCESS_FS_MAKE_BLOCK |
+		LANDLOCK_ACCESS_FS_MAKE_SYM |
+		LANDLOCK_ACCESS_FS_REFER |
+		LANDLOCK_ACCESS_FS_TRUNCATE,
+	};
+	int abi;
 
-	pb.allowed_access = perms;
+	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
+		return -1;
 
-	if ((pb.parent_fd = open(path, O_PATH)) == -1)
+	abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
+	if (abi == -1)
 		return -1;
+	if (abi < 2)
+		rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
+	if (abi < 3)
+		rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
 
-	err = landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH,
-	    &pb, 0);
-	saved_errno = errno;
-	close(pb.parent_fd);
-	errno = saved_errno;
-	return err ? -1 : 0;
+	landlock_state = 1;
+	return landlock_create_ruleset(&rattr, sizeof(rattr), 0);
 }
 
 static int
-landlock_apply(int fd)
+landlock_lock(void)
 {
-	int r, saved_errno;
+	int saved_errno;
 
-	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
-		err(1, "%s: prctl(PR_SET_NO_NEW_PRIVS)", __func__);
+	if (landlock_restrict_self(landlock_fd, 0)) {
+		saved_errno = errno;
+		close(landlock_fd);
+		errno = saved_errno;
+		landlock_state = -1;
+		return -1;
+	}
 
-	r = landlock_restrict_self(fd, 0);
-	saved_errno = errno;
-	close(fd);
-	errno = saved_errno;
-	return r ? -1 : 0;
+	landlock_state = 2;
+	close(landlock_fd);
+	return 0;
 }
-
 static int
-landlock_no_fs(void)
+landlock_unveil(const char *path, const char *permissions)
 {
-	int fd;
+	struct landlock_path_beneath_attr lpba;
+	int fd, saved_errno;
 
-	/*
-	 * XXX: landlock disabled at runtime, pretend everything's
-	 * good.
-	 */
-	if ((fd = open_landlock()) == -1)
+	if (landlock_state == 0) {
+		if ((landlock_fd = landlock_init()) == -1) {
+			landlock_state = -1;
+			/* this kernel doesn't have landlock built in */
+			if (errno == ENOSYS || errno == EOPNOTSUPP)
+				return 0;
+			return -1;
+		}
+	}
+
+	/* no landlock available */
+	if (landlock_state == -1)
 		return 0;
 
-	return landlock_apply(fd);
+	if (path == NULL && permissions == NULL)
+		return landlock_lock();
+
+	if (path == NULL || permissions == NULL || landlock_state != 1) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!strcmp(permissions, "r")) {
+		fd = open(path, O_PATH | O_CLOEXEC);
+		if (fd == -1)
+			return -1;
+		lpba = (struct landlock_path_beneath_attr){
+			.allowed_access =
+			    LANDLOCK_ACCESS_FS_READ_FILE |
+			    LANDLOCK_ACCESS_FS_READ_DIR,
+			.parent_fd = fd,
+		};
+		if (landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH,
+		    &lpba, 0) == -1) {
+			saved_errno = errno;
+			close(fd);
+			errno = saved_errno;
+			return -1;
+		}
+		close(fd);
+	}
+
+	return 0;
 }
 
 void
 sandbox_net_process(void)
 {
 	/*
-	 * We don't know what paths are required for the TLS stack.
+	 * We don't know what paths are required for the TLS stack and
+	 * DNS, so allow accessing read-only the whole system.
 	 * Yes, it sucks.
 	 */
-	return;
+
+	if (landlock_unveil("/", "r") == -1)
+		err(1, "landlock_unveil(/, r)");
+	if (landlock_unveil(NULL, NULL) == -1)
+		err(1, "landlock_unveil(NULL, NULL)");
 }
 
 void