xref: /freebsd-src/contrib/xz/src/xz/sandbox.c (revision 3b35e7ee8de9b0260149a2b77e87a2b9c7a36244)
1 // SPDX-License-Identifier: 0BSD
2 
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file       sandbox.c
6 /// \brief      Sandbox support
7 //
8 //  Author:     Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
11 
12 #include "private.h"
13 
14 
15 #ifndef ENABLE_SANDBOX
16 
17 // Prevent an empty translation unit when no sandboxing is supported.
18 typedef int dummy;
19 
20 #else
21 
22 /// If the conditions for strict sandboxing (described in main())
23 /// have been met, sandbox_allow_strict() can be called to set this
24 /// variable to true.
25 static bool strict_sandbox_allowed = false;
26 
27 
28 extern void
sandbox_allow_strict(void)29 sandbox_allow_strict(void)
30 {
31 	strict_sandbox_allowed = true;
32 	return;
33 }
34 
35 
36 // Strict sandboxing prevents opening any files. This *tries* to ensure
37 // that any auxiliary files that might be required are already open.
38 //
39 // Returns true if strict sandboxing is allowed, false otherwise.
40 static bool
prepare_for_strict_sandbox(void)41 prepare_for_strict_sandbox(void)
42 {
43 	if (!strict_sandbox_allowed)
44 		return false;
45 
46 	const char dummy_str[] = "x";
47 
48 	// Try to ensure that both libc and xz locale files have been
49 	// loaded when NLS is enabled.
50 	snprintf(NULL, 0, "%s%s", _(dummy_str), strerror(EINVAL));
51 
52 	// Try to ensure that iconv data files needed for handling multibyte
53 	// characters have been loaded. This is needed at least with glibc.
54 	tuklib_mbstr_width(dummy_str, NULL);
55 
56 	return true;
57 }
58 
59 #endif
60 
61 
62 #if defined(HAVE_PLEDGE)
63 
64 ///////////////
65 // pledge(2) //
66 ///////////////
67 
68 #include <unistd.h>
69 
70 
71 extern void
sandbox_init(void)72 sandbox_init(void)
73 {
74 	if (pledge("stdio rpath wpath cpath fattr", "")) {
75 		// gettext hasn't been initialized yet so
76 		// there's no point to call it here.
77 		message_fatal("Failed to enable the sandbox");
78 	}
79 
80 	return;
81 }
82 
83 
84 extern void
sandbox_enable_read_only(void)85 sandbox_enable_read_only(void)
86 {
87 	// We will be opening files for reading but
88 	// won't create or remove any files.
89 	if (pledge("stdio rpath", ""))
90 		message_fatal(_("Failed to enable the sandbox"));
91 
92 	return;
93 }
94 
95 
96 extern void
sandbox_enable_strict_if_allowed(int src_fd lzma_attribute ((__unused__)),int pipe_event_fd lzma_attribute ((__unused__)),int pipe_write_fd lzma_attribute ((__unused__)))97 sandbox_enable_strict_if_allowed(int src_fd lzma_attribute((__unused__)),
98 		int pipe_event_fd lzma_attribute((__unused__)),
99 		int pipe_write_fd lzma_attribute((__unused__)))
100 {
101 	if (!prepare_for_strict_sandbox())
102 		return;
103 
104 	// All files that need to be opened have already been opened.
105 	if (pledge("stdio", ""))
106 		message_fatal(_("Failed to enable the sandbox"));
107 
108 	return;
109 }
110 
111 
112 #elif defined(HAVE_LINUX_LANDLOCK)
113 
114 //////////////
115 // Landlock //
116 //////////////
117 
118 #include <linux/landlock.h>
119 #include <sys/syscall.h>
120 #include <sys/prctl.h>
121 
122 
123 // Highest Landlock ABI version supported by this file:
124 //   - For ABI versions 1-3 we don't need anything from <linux/landlock.h>
125 //     that isn't part of version 1.
126 //   - For ABI version 4 we need the larger struct landlock_ruleset_attr
127 //     with the handled_access_net member. That is bundled with the macros
128 //     LANDLOCK_ACCESS_NET_BIND_TCP and LANDLOCK_ACCESS_NET_CONNECT_TCP.
129 #ifdef LANDLOCK_ACCESS_NET_BIND_TCP
130 #	define LANDLOCK_ABI_MAX 4
131 #else
132 #	define LANDLOCK_ABI_MAX 3
133 #endif
134 
135 
136 /// Landlock ABI version supported by the kernel
137 static int landlock_abi;
138 
139 
140 // The required_rights should have those bits set that must not be restricted.
141 // This function will then bitwise-and ~required_rights with a mask matching
142 // the Landlock ABI version, leaving only those bits set that are supported
143 // by the ABI and allowed to be restricted by the function argument.
144 static void
enable_landlock(uint64_t required_rights)145 enable_landlock(uint64_t required_rights)
146 {
147 	assert(landlock_abi <= LANDLOCK_ABI_MAX);
148 
149 	if (landlock_abi <= 0)
150 		return;
151 
152 	// We want to set all supported flags in handled_access_fs.
153 	// This way the ruleset will initially forbid access to all
154 	// actions that the available Landlock ABI version supports.
155 	// Exceptions can be added using landlock_add_rule(2) to
156 	// allow certain actions on certain files or directories.
157 	//
158 	// The same flag values are used on all archs. ABI v2 and v3
159 	// both add one new flag.
160 	//
161 	// First in ABI v1: LANDLOCK_ACCESS_FS_EXECUTE = 1ULL << 0
162 	// Last in ABI v1: LANDLOCK_ACCESS_FS_MAKE_SYM = 1ULL << 12
163 	// Last in ABI v2: LANDLOCK_ACCESS_FS_REFER = 1ULL << 13
164 	// Last in ABI v3: LANDLOCK_ACCESS_FS_TRUNCATE = 1ULL << 14
165 	//
166 	// This makes it simple to set the mask based on the ABI
167 	// version and we don't need to care which flags are #defined
168 	// in the installed <linux/landlock.h> for ABI versions 1-3.
169 	const struct landlock_ruleset_attr attr = {
170 		.handled_access_fs = ~required_rights
171 			& ((1ULL << (12 + my_min(3, landlock_abi))) - 1),
172 #if LANDLOCK_ABI_MAX >= 4
173 		.handled_access_net = landlock_abi < 4 ? 0 :
174 				(LANDLOCK_ACCESS_NET_BIND_TCP
175 				| LANDLOCK_ACCESS_NET_CONNECT_TCP),
176 #endif
177 	};
178 
179 	const int ruleset_fd = syscall(SYS_landlock_create_ruleset,
180 			&attr, sizeof(attr), 0U);
181 	if (ruleset_fd < 0)
182 		message_fatal(_("Failed to enable the sandbox"));
183 
184 	// All files we need should have already been opened. Thus,
185 	// we don't need to add any rules using landlock_add_rule(2)
186 	// before activating the sandbox.
187 	//
188 	// NOTE: It's possible that the hack prepare_for_strict_sandbox()
189 	// isn't be good enough. It tries to get translations and
190 	// libc-specific files loaded but if it's not good enough
191 	// then perhaps a Landlock rule to allow reading from /usr
192 	// and/or the xz installation prefix would be needed.
193 	//
194 	// prctl(PR_SET_NO_NEW_PRIVS, ...) was already called in
195 	// sandbox_init() so we don't do it here again.
196 	if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0U) != 0)
197 		message_fatal(_("Failed to enable the sandbox"));
198 
199 	return;
200 }
201 
202 
203 extern void
sandbox_init(void)204 sandbox_init(void)
205 {
206 	// Prevent the process from gaining new privileges. This must be done
207 	// before landlock_restrict_self(2) but since we will never need new
208 	// privileges, this call can be done here already.
209 	//
210 	// This is supported since Linux 3.5. Ignore the return value to
211 	// keep compatibility with old kernels. landlock_restrict_self(2)
212 	// will fail if the no_new_privs attribute isn't set, thus if prctl()
213 	// fails here the error will still be detected when it matters.
214 	(void)prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
215 
216 	// Get the highest Landlock ABI version supported by the kernel.
217 	landlock_abi = syscall(SYS_landlock_create_ruleset,
218 			(void *)NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
219 
220 	// The kernel might support a newer ABI than this file.
221 	if (landlock_abi > LANDLOCK_ABI_MAX)
222 		landlock_abi = LANDLOCK_ABI_MAX;
223 
224 	// These are all in ABI version 1 already. We don't need truncate
225 	// rights because files are created with open() using O_EXCL and
226 	// without O_TRUNC.
227 	//
228 	// LANDLOCK_ACCESS_FS_READ_DIR is included here to get a clear error
229 	// message if xz is given a directory name. Without this permission
230 	// the message would be "Permission denied" but with this permission
231 	// it's "Is a directory, skipping". It could be worked around with
232 	// stat()/lstat() but just giving this permission is simpler and
233 	// shouldn't make the sandbox much weaker in practice.
234 	const uint64_t required_rights
235 			= LANDLOCK_ACCESS_FS_WRITE_FILE
236 			| LANDLOCK_ACCESS_FS_READ_FILE
237 			| LANDLOCK_ACCESS_FS_READ_DIR
238 			| LANDLOCK_ACCESS_FS_REMOVE_FILE
239 			| LANDLOCK_ACCESS_FS_MAKE_REG;
240 
241 	enable_landlock(required_rights);
242 	return;
243 }
244 
245 
246 extern void
sandbox_enable_read_only(void)247 sandbox_enable_read_only(void)
248 {
249 	// We will be opening files for reading but
250 	// won't create or remove any files.
251 	const uint64_t required_rights
252 			= LANDLOCK_ACCESS_FS_READ_FILE
253 			| LANDLOCK_ACCESS_FS_READ_DIR;
254 	enable_landlock(required_rights);
255 	return;
256 }
257 
258 
259 extern void
sandbox_enable_strict_if_allowed(int src_fd lzma_attribute ((__unused__)),int pipe_event_fd lzma_attribute ((__unused__)),int pipe_write_fd lzma_attribute ((__unused__)))260 sandbox_enable_strict_if_allowed(int src_fd lzma_attribute((__unused__)),
261 		int pipe_event_fd lzma_attribute((__unused__)),
262 		int pipe_write_fd lzma_attribute((__unused__)))
263 {
264 	if (!prepare_for_strict_sandbox())
265 		return;
266 
267 	// Allow all restrictions that the kernel supports with the
268 	// highest Landlock ABI version that the kernel or xz supports.
269 	//
270 	// NOTE: LANDLOCK_ACCESS_FS_READ_DIR isn't needed here because
271 	// the only input file has already been opened.
272 	enable_landlock(0);
273 	return;
274 }
275 
276 
277 #elif defined(HAVE_CAP_RIGHTS_LIMIT)
278 
279 //////////////
280 // Capsicum //
281 //////////////
282 
283 #include <sys/capsicum.h>
284 
285 
286 extern void
sandbox_init(void)287 sandbox_init(void)
288 {
289 	// Nothing to do.
290 	return;
291 }
292 
293 
294 extern void
sandbox_enable_read_only(void)295 sandbox_enable_read_only(void)
296 {
297 	// Nothing to do.
298 	return;
299 }
300 
301 
302 extern void
sandbox_enable_strict_if_allowed(int src_fd,int pipe_event_fd,int pipe_write_fd)303 sandbox_enable_strict_if_allowed(
304 		int src_fd, int pipe_event_fd, int pipe_write_fd)
305 {
306 	if (!prepare_for_strict_sandbox())
307 		return;
308 
309 	// Capsicum needs FreeBSD 10.2 or later.
310 	cap_rights_t rights;
311 
312 	if (cap_enter())
313 		goto error;
314 
315 	if (cap_rights_limit(src_fd, cap_rights_init(&rights,
316 			CAP_EVENT, CAP_FCNTL, CAP_LOOKUP, CAP_READ, CAP_SEEK)))
317 		goto error;
318 
319 	// If not reading from stdin, remove all capabilities from it.
320 	if (src_fd != STDIN_FILENO && cap_rights_limit(
321 			STDIN_FILENO, cap_rights_clear(&rights)))
322 		goto error;
323 
324 	if (cap_rights_limit(STDOUT_FILENO, cap_rights_init(&rights,
325 			CAP_EVENT, CAP_FCNTL, CAP_FSTAT, CAP_LOOKUP,
326 			CAP_WRITE, CAP_SEEK)))
327 		goto error;
328 
329 	if (cap_rights_limit(STDERR_FILENO, cap_rights_init(&rights,
330 			CAP_WRITE)))
331 		goto error;
332 
333 	if (cap_rights_limit(pipe_event_fd, cap_rights_init(&rights,
334 			CAP_EVENT)))
335 		goto error;
336 
337 	if (cap_rights_limit(pipe_write_fd, cap_rights_init(&rights,
338 			CAP_WRITE)))
339 		goto error;
340 
341 	return;
342 
343 error:
344 	// If a kernel is configured without capability mode support or
345 	// used in an emulator that does not implement the capability
346 	// system calls, then the Capsicum system calls will fail and set
347 	// errno to ENOSYS. In that case xz will silently run without
348 	// the sandbox.
349 	if (errno == ENOSYS)
350 		return;
351 
352 	message_fatal(_("Failed to enable the sandbox"));
353 }
354 
355 #endif
356