1 /* 2 * Redistribution and use in source and binary forms, with or without 3 * modification, are permitted provided that the following conditions 4 * are met: 5 * 1. Redistributions of source code must retain the above copyright 6 * notice immediately at the beginning of the file, without modification, 7 * this list of conditions, and the following disclaimer. 8 * 2. Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 16 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 * SUCH DAMAGE. 23 */ 24 /* Landlock sandbox: read anywhere, write only in $TMPDIR. */ 25 #include "file.h" 26 27 #if HAVE_LINUX_LANDLOCK_H 28 #include "magic.h" 29 #include <linux/landlock.h> 30 #include <sys/prctl.h> 31 #include <sys/syscall.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <errno.h> 37 #include <unistd.h> 38 39 /* glibc only got Landlock wrappers in 2.40, so call via syscall(2). */ 40 #ifndef landlock_create_ruleset 41 static inline int 42 landlock_create_ruleset(const struct landlock_ruleset_attr *attr, 43 size_t size, uint32_t flags) 44 { 45 return CAST(int, syscall(__NR_landlock_create_ruleset, attr, size, 46 flags)); 47 } 48 #endif 49 50 #ifndef landlock_add_rule 51 static inline int 52 landlock_add_rule(int ruleset_fd, enum landlock_rule_type rule_type, 53 const void *rule_attr, uint32_t flags) 54 { 55 return CAST(int, syscall(__NR_landlock_add_rule, ruleset_fd, 56 rule_type, rule_attr, flags)); 57 } 58 #endif 59 60 #ifndef landlock_restrict_self 61 static inline int 62 landlock_restrict_self(int ruleset_fd, uint32_t flags) 63 { 64 return CAST(int, syscall(__NR_landlock_restrict_self, ruleset_fd, 65 flags)); 66 } 67 #endif 68 69 /* A missing path (e.g. unset $TMPDIR) is not fatal, just skipped. */ 70 static int 71 landlock_allow_path(int ruleset_fd, const char *path, uint64_t allowed) 72 { 73 struct landlock_path_beneath_attr pb; 74 int rv; 75 76 pb.allowed_access = allowed; 77 pb.parent_fd = open(path, O_PATH | O_CLOEXEC); 78 if (pb.parent_fd == -1) 79 return errno == ENOENT ? 0 : -1; 80 rv = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &pb, 0); 81 (void)close(pb.parent_fd); 82 return rv; 83 } 84 85 int 86 enable_landlock(int flags, int action) 87 { 88 struct landlock_ruleset_attr attr; 89 struct stat sb; 90 int ruleset_fd, abi, needs_write; 91 const char *tmpdir; 92 93 (void)flags; 94 95 /* Magic build modes write outside /tmp; just skip Landlock 96 for those. seccomp still applies. */ 97 if (action == FILE_COMPILE || action == FILE_CHECK || 98 action == FILE_LIST) 99 return 0; 100 101 /* Pipe input gets copied to a tempfile in /tmp. */ 102 needs_write = fstat(STDIN_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode); 103 104 abi = CAST(int, syscall(__NR_landlock_create_ruleset, NULL, 0, 105 LANDLOCK_CREATE_RULESET_VERSION)); 106 if (abi < 1) 107 return 0; 108 109 (void)memset(&attr, 0, sizeof(attr)); 110 attr.handled_access_fs = 111 LANDLOCK_ACCESS_FS_EXECUTE | 112 LANDLOCK_ACCESS_FS_WRITE_FILE | 113 LANDLOCK_ACCESS_FS_READ_FILE | 114 LANDLOCK_ACCESS_FS_READ_DIR | 115 LANDLOCK_ACCESS_FS_REMOVE_DIR | 116 LANDLOCK_ACCESS_FS_REMOVE_FILE | 117 LANDLOCK_ACCESS_FS_MAKE_CHAR | 118 LANDLOCK_ACCESS_FS_MAKE_DIR | 119 LANDLOCK_ACCESS_FS_MAKE_REG | 120 LANDLOCK_ACCESS_FS_MAKE_SOCK | 121 LANDLOCK_ACCESS_FS_MAKE_FIFO | 122 LANDLOCK_ACCESS_FS_MAKE_BLOCK | 123 LANDLOCK_ACCESS_FS_MAKE_SYM; 124 #ifdef LANDLOCK_ACCESS_FS_REFER 125 if (abi >= 2) 126 attr.handled_access_fs |= LANDLOCK_ACCESS_FS_REFER; 127 #endif 128 #ifdef LANDLOCK_ACCESS_FS_TRUNCATE 129 if (abi >= 3) 130 attr.handled_access_fs |= LANDLOCK_ACCESS_FS_TRUNCATE; 131 #endif 132 #ifdef LANDLOCK_ACCESS_NET_BIND_TCP 133 if (abi >= 4) { 134 attr.handled_access_net = 135 LANDLOCK_ACCESS_NET_BIND_TCP | 136 LANDLOCK_ACCESS_NET_CONNECT_TCP; 137 } 138 #endif 139 140 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 141 if (ruleset_fd == -1) 142 return -1; 143 144 if (landlock_allow_path(ruleset_fd, "/", 145 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR) == -1) 146 goto fail; 147 148 if (needs_write) { 149 uint64_t tmp_access = 150 LANDLOCK_ACCESS_FS_READ_FILE | 151 LANDLOCK_ACCESS_FS_READ_DIR | 152 LANDLOCK_ACCESS_FS_WRITE_FILE | 153 LANDLOCK_ACCESS_FS_MAKE_REG | 154 LANDLOCK_ACCESS_FS_REMOVE_FILE; 155 #ifdef LANDLOCK_ACCESS_FS_TRUNCATE 156 if (abi >= 3) 157 tmp_access |= LANDLOCK_ACCESS_FS_TRUNCATE; 158 #endif 159 tmpdir = getenv("TMPDIR"); 160 if (tmpdir == NULL || *tmpdir == '\0') 161 tmpdir = "/tmp"; 162 if (landlock_allow_path(ruleset_fd, tmpdir, tmp_access) == -1) 163 goto fail; 164 } 165 166 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) 167 goto fail; 168 if (landlock_restrict_self(ruleset_fd, 0) == -1) 169 goto fail; 170 171 (void)close(ruleset_fd); 172 return 0; 173 fail: 174 (void)close(ruleset_fd); 175 return -1; 176 } 177 178 #endif /* HAVE_LINUX_LANDLOCK_H */ 179