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