Lines 33-73
Link Here
|
33 |
#include <sys/stat.h> |
33 |
#include <sys/stat.h> |
34 |
#include <fcntl.h> |
34 |
#include <fcntl.h> |
35 |
|
35 |
|
36 |
#ifdef ENABLE_SECCOMP |
|
|
37 |
#include <errno.h> |
38 |
#include <sys/socket.h> |
39 |
#include <sys/ioctl.h> |
40 |
#include <sys/utsname.h> |
41 |
#include <seccomp.h> |
42 |
#endif |
43 |
|
44 |
#include "gnome-desktop-thumbnail-script.h" |
36 |
#include "gnome-desktop-thumbnail-script.h" |
45 |
|
37 |
|
46 |
typedef struct { |
38 |
typedef struct { |
47 |
gboolean sandbox; |
|
|
48 |
char *thumbnailer_name; |
49 |
GArray *fd_array; |
50 |
/* Input/output file paths outside the sandbox */ |
51 |
char *infile; |
39 |
char *infile; |
52 |
char *infile_tmp; /* the host version of /tmp/gnome-desktop-file-to-thumbnail.* */ |
|
|
53 |
char *outfile; |
40 |
char *outfile; |
54 |
char *outdir; /* outdir is outfile's parent dir, if it needs to be deleted */ |
|
|
55 |
/* I/O file paths inside the sandbox */ |
56 |
char *s_infile; |
57 |
char *s_outfile; |
58 |
} ScriptExec; |
41 |
} ScriptExec; |
59 |
|
42 |
|
60 |
static char * |
43 |
static char * |
61 |
expand_thumbnailing_elem (const char *elem, |
44 |
expand_thumbnailing_elem (const char *elem, |
62 |
const int size, |
45 |
const int size, |
63 |
const char *infile, |
46 |
const char *inuri, |
64 |
const char *outfile, |
47 |
const char *outfile, |
65 |
gboolean *got_input, |
48 |
gboolean *got_input, |
66 |
gboolean *got_output) |
49 |
gboolean *got_output) |
67 |
{ |
50 |
{ |
68 |
GString *str; |
51 |
GString *str; |
69 |
const char *p, *last; |
52 |
const char *p, *last; |
70 |
char *inuri; |
53 |
char *localfile; |
71 |
|
54 |
|
72 |
str = g_string_new (NULL); |
55 |
str = g_string_new (NULL); |
73 |
|
56 |
|
Lines 79-96
expand_thumbnailing_elem (const char *elem,
Link Here
|
79 |
|
62 |
|
80 |
switch (*p) { |
63 |
switch (*p) { |
81 |
case 'u': |
64 |
case 'u': |
82 |
inuri = g_filename_to_uri (infile, NULL, NULL); |
65 |
g_string_append (str, inuri); |
83 |
if (inuri) |
66 |
*got_input = TRUE; |
84 |
{ |
|
|
85 |
g_string_append (str, inuri); |
86 |
*got_input = TRUE; |
87 |
g_free (inuri); |
88 |
} |
89 |
p++; |
67 |
p++; |
90 |
break; |
68 |
break; |
91 |
case 'i': |
69 |
case 'i': |
92 |
g_string_append (str, infile); |
70 |
localfile = g_filename_from_uri (inuri, NULL, NULL); |
93 |
*got_input = TRUE; |
71 |
if (localfile) |
|
|
72 |
{ |
73 |
g_string_append (str, localfile); |
74 |
*got_input = TRUE; |
75 |
g_free (localfile); |
76 |
} |
94 |
p++; |
77 |
p++; |
95 |
break; |
78 |
break; |
96 |
case 'o': |
79 |
case 'o': |
Lines 117-610
expand_thumbnailing_elem (const char *elem,
Link Here
|
117 |
return g_string_free (str, FALSE); |
100 |
return g_string_free (str, FALSE); |
118 |
} |
101 |
} |
119 |
|
102 |
|
120 |
/* From https://github.com/flatpak/flatpak/blob/master/common/flatpak-run.c */ |
|
|
121 |
G_GNUC_NULL_TERMINATED |
122 |
static void |
123 |
add_args (GPtrArray *argv_array, ...) |
124 |
{ |
125 |
va_list args; |
126 |
const gchar *arg; |
127 |
|
128 |
va_start (args, argv_array); |
129 |
while ((arg = va_arg (args, const gchar *))) |
130 |
g_ptr_array_add (argv_array, g_strdup (arg)); |
131 |
va_end (args); |
132 |
} |
133 |
|
134 |
static void |
135 |
add_env (GPtrArray *array, |
136 |
const char *envvar) |
137 |
{ |
138 |
if (g_getenv (envvar) != NULL) |
139 |
add_args (array, |
140 |
"--setenv", envvar, g_getenv (envvar), |
141 |
NULL); |
142 |
} |
143 |
|
144 |
static char * |
145 |
get_extension (const char *path) |
146 |
{ |
147 |
g_autofree char *basename = NULL; |
148 |
char *p; |
149 |
|
150 |
basename = g_path_get_basename (path); |
151 |
p = strrchr (basename, '.'); |
152 |
if (p == NULL) |
153 |
return NULL; |
154 |
return g_strdup (p + 1); |
155 |
} |
156 |
|
157 |
#ifdef ENABLE_SECCOMP |
158 |
static gboolean |
159 |
flatpak_fail (GError **error, |
160 |
const char *msg, |
161 |
...) |
162 |
{ |
163 |
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, msg); |
164 |
return FALSE; |
165 |
} |
166 |
|
167 |
/* From https://github.com/flatpak/flatpak/blob/master/common/flatpak-utils.c */ |
168 |
#if !defined(__i386__) && !defined(__x86_64__) && !defined(__aarch64__) && !defined(__arm__) |
169 |
static const char * |
170 |
flatpak_get_kernel_arch (void) |
171 |
{ |
172 |
static struct utsname buf; |
173 |
static char *arch = NULL; |
174 |
char *m; |
175 |
|
176 |
if (arch != NULL) |
177 |
return arch; |
178 |
|
179 |
if (uname (&buf)) |
180 |
{ |
181 |
arch = "unknown"; |
182 |
return arch; |
183 |
} |
184 |
|
185 |
/* By default, just pass on machine, good enough for most arches */ |
186 |
arch = buf.machine; |
187 |
|
188 |
/* Override for some arches */ |
189 |
|
190 |
m = buf.machine; |
191 |
/* i?86 */ |
192 |
if (strlen (m) == 4 && m[0] == 'i' && m[2] == '8' && m[3] == '6') |
193 |
{ |
194 |
arch = "i386"; |
195 |
} |
196 |
else if (g_str_has_prefix (m, "arm")) |
197 |
{ |
198 |
if (g_str_has_suffix (m, "b")) |
199 |
arch = "armeb"; |
200 |
else |
201 |
arch = "arm"; |
202 |
} |
203 |
else if (strcmp (m, "mips") == 0) |
204 |
{ |
205 |
#if G_BYTE_ORDER == G_LITTLE_ENDIAN |
206 |
arch = "mipsel"; |
207 |
#endif |
208 |
} |
209 |
else if (strcmp (m, "mips64") == 0) |
210 |
{ |
211 |
#if G_BYTE_ORDER == G_LITTLE_ENDIAN |
212 |
arch = "mips64el"; |
213 |
#endif |
214 |
} |
215 |
|
216 |
return arch; |
217 |
} |
218 |
#endif |
219 |
|
220 |
/* This maps the kernel-reported uname to a single string representing |
221 |
* the cpu family, in the sense that all members of this family would |
222 |
* be able to understand and link to a binary file with such cpu |
223 |
* opcodes. That doesn't necessarily mean that all members of the |
224 |
* family can run all opcodes, for instance for modern 32bit intel we |
225 |
* report "i386", even though they support instructions that the |
226 |
* original i386 cpu cannot run. Still, such an executable would |
227 |
* at least try to execute a 386, whereas an arm binary would not. |
228 |
*/ |
229 |
static const char * |
230 |
flatpak_get_arch (void) |
231 |
{ |
232 |
/* Avoid using uname on multiarch machines, because uname reports the kernels |
233 |
* arch, and that may be different from userspace. If e.g. the kernel is 64bit and |
234 |
* the userspace is 32bit we want to use 32bit by default. So, we take the current build |
235 |
* arch as the default. */ |
236 |
#if defined(__i386__) |
237 |
return "i386"; |
238 |
#elif defined(__x86_64__) |
239 |
return "x86_64"; |
240 |
#elif defined(__aarch64__) |
241 |
return "aarch64"; |
242 |
#elif defined(__arm__) |
243 |
#if G_BYTE_ORDER == G_LITTLE_ENDIAN |
244 |
return "arm"; |
245 |
#else |
246 |
return "armeb"; |
247 |
#endif |
248 |
#else |
249 |
return flatpak_get_kernel_arch (); |
250 |
#endif |
251 |
} |
252 |
|
253 |
/* From https://github.com/flatpak/flatpak/blob/master/common/flatpak-run.c */ |
254 |
static const uint32_t seccomp_x86_64_extra_arches[] = { SCMP_ARCH_X86, 0, }; |
255 |
|
256 |
#ifdef SCMP_ARCH_AARCH64 |
257 |
static const uint32_t seccomp_aarch64_extra_arches[] = { SCMP_ARCH_ARM, 0 }; |
258 |
#endif |
259 |
|
260 |
static inline void |
261 |
cleanup_seccomp (void *p) |
262 |
{ |
263 |
scmp_filter_ctx *pp = (scmp_filter_ctx *) p; |
264 |
|
265 |
if (*pp) |
266 |
seccomp_release (*pp); |
267 |
} |
268 |
|
269 |
static gboolean |
270 |
setup_seccomp (GPtrArray *argv_array, |
271 |
GArray *fd_array, |
272 |
const char *arch, |
273 |
gboolean multiarch, |
274 |
gboolean devel, |
275 |
GError **error) |
276 |
{ |
277 |
__attribute__((cleanup (cleanup_seccomp))) scmp_filter_ctx seccomp = NULL; |
278 |
|
279 |
/**** BEGIN NOTE ON CODE SHARING |
280 |
* |
281 |
* There are today a number of different Linux container |
282 |
* implementations. That will likely continue for long into the |
283 |
* future. But we can still try to share code, and it's important |
284 |
* to do so because it affects what library and application writers |
285 |
* can do, and we should support code portability between different |
286 |
* container tools. |
287 |
* |
288 |
* This syscall blacklist is copied from linux-user-chroot, which was in turn |
289 |
* clearly influenced by the Sandstorm.io blacklist. |
290 |
* |
291 |
* If you make any changes here, I suggest sending the changes along |
292 |
* to other sandbox maintainers. Using the libseccomp list is also |
293 |
* an appropriate venue: |
294 |
* https://groups.google.com/forum/#!topic/libseccomp |
295 |
* |
296 |
* A non-exhaustive list of links to container tooling that might |
297 |
* want to share this blacklist: |
298 |
* |
299 |
* https://github.com/sandstorm-io/sandstorm |
300 |
* in src/sandstorm/supervisor.c++ |
301 |
* http://cgit.freedesktop.org/xdg-app/xdg-app/ |
302 |
* in common/flatpak-run.c |
303 |
* https://git.gnome.org/browse/linux-user-chroot |
304 |
* in src/setup-seccomp.c |
305 |
* |
306 |
**** END NOTE ON CODE SHARING |
307 |
*/ |
308 |
struct |
309 |
{ |
310 |
int scall; |
311 |
struct scmp_arg_cmp *arg; |
312 |
} syscall_blacklist[] = { |
313 |
/* Block dmesg */ |
314 |
{SCMP_SYS (syslog)}, |
315 |
/* Useless old syscall */ |
316 |
{SCMP_SYS (uselib)}, |
317 |
/* Don't allow you to switch to bsd emulation or whatnot */ |
318 |
{SCMP_SYS (personality)}, |
319 |
/* Don't allow disabling accounting */ |
320 |
{SCMP_SYS (acct)}, |
321 |
/* 16-bit code is unnecessary in the sandbox, and modify_ldt is a |
322 |
historic source of interesting information leaks. */ |
323 |
{SCMP_SYS (modify_ldt)}, |
324 |
/* Don't allow reading current quota use */ |
325 |
{SCMP_SYS (quotactl)}, |
326 |
|
327 |
/* Don't allow access to the kernel keyring */ |
328 |
{SCMP_SYS (add_key)}, |
329 |
{SCMP_SYS (keyctl)}, |
330 |
{SCMP_SYS (request_key)}, |
331 |
|
332 |
/* Scary VM/NUMA ops */ |
333 |
{SCMP_SYS (move_pages)}, |
334 |
{SCMP_SYS (mbind)}, |
335 |
{SCMP_SYS (get_mempolicy)}, |
336 |
{SCMP_SYS (set_mempolicy)}, |
337 |
{SCMP_SYS (migrate_pages)}, |
338 |
|
339 |
/* Don't allow subnamespace setups: */ |
340 |
{SCMP_SYS (unshare)}, |
341 |
{SCMP_SYS (mount)}, |
342 |
{SCMP_SYS (pivot_root)}, |
343 |
{SCMP_SYS (clone), &SCMP_A0 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)}, |
344 |
|
345 |
/* Don't allow faking input to the controlling tty (CVE-2017-5226) */ |
346 |
{SCMP_SYS (ioctl), &SCMP_A1(SCMP_CMP_EQ, (int)TIOCSTI)}, |
347 |
}; |
348 |
|
349 |
struct |
350 |
{ |
351 |
int scall; |
352 |
struct scmp_arg_cmp *arg; |
353 |
} syscall_nondevel_blacklist[] = { |
354 |
/* Profiling operations; we expect these to be done by tools from outside |
355 |
* the sandbox. In particular perf has been the source of many CVEs. |
356 |
*/ |
357 |
{SCMP_SYS (perf_event_open)}, |
358 |
{SCMP_SYS (ptrace)} |
359 |
}; |
360 |
/* Blacklist all but unix, inet, inet6 and netlink */ |
361 |
int socket_family_blacklist[] = { |
362 |
AF_AX25, |
363 |
AF_IPX, |
364 |
AF_APPLETALK, |
365 |
AF_NETROM, |
366 |
AF_BRIDGE, |
367 |
AF_ATMPVC, |
368 |
AF_X25, |
369 |
AF_ROSE, |
370 |
AF_DECnet, |
371 |
AF_NETBEUI, |
372 |
AF_SECURITY, |
373 |
AF_KEY, |
374 |
AF_NETLINK + 1, /* Last gets CMP_GE, so order is important */ |
375 |
}; |
376 |
guint i; |
377 |
int r; |
378 |
int fd = -1; |
379 |
g_autofree char *fd_str = NULL; |
380 |
g_autofree char *path = NULL; |
381 |
|
382 |
seccomp = seccomp_init (SCMP_ACT_ALLOW); |
383 |
if (!seccomp) |
384 |
return flatpak_fail (error, "Initialize seccomp failed"); |
385 |
|
386 |
if (arch != NULL) |
387 |
{ |
388 |
uint32_t arch_id = 0; |
389 |
const uint32_t *extra_arches = NULL; |
390 |
|
391 |
if (strcmp (arch, "i386") == 0) |
392 |
{ |
393 |
arch_id = SCMP_ARCH_X86; |
394 |
} |
395 |
else if (strcmp (arch, "x86_64") == 0) |
396 |
{ |
397 |
arch_id = SCMP_ARCH_X86_64; |
398 |
extra_arches = seccomp_x86_64_extra_arches; |
399 |
} |
400 |
else if (strcmp (arch, "arm") == 0) |
401 |
{ |
402 |
arch_id = SCMP_ARCH_ARM; |
403 |
} |
404 |
#ifdef SCMP_ARCH_AARCH64 |
405 |
else if (strcmp (arch, "aarch64") == 0) |
406 |
{ |
407 |
arch_id = SCMP_ARCH_AARCH64; |
408 |
extra_arches = seccomp_aarch64_extra_arches; |
409 |
} |
410 |
#endif |
411 |
|
412 |
/* We only really need to handle arches on multiarch systems. |
413 |
* If only one arch is supported the default is fine */ |
414 |
if (arch_id != 0) |
415 |
{ |
416 |
/* This *adds* the target arch, instead of replacing the |
417 |
native one. This is not ideal, because we'd like to only |
418 |
allow the target arch, but we can't really disallow the |
419 |
native arch at this point, because then bubblewrap |
420 |
couldn't continue running. */ |
421 |
r = seccomp_arch_add (seccomp, arch_id); |
422 |
if (r < 0 && r != -EEXIST) |
423 |
return flatpak_fail (error, "Failed to add architecture to seccomp filter"); |
424 |
|
425 |
if (multiarch && extra_arches != NULL) |
426 |
{ |
427 |
for (i = 0; extra_arches[i] != 0; i++) |
428 |
{ |
429 |
r = seccomp_arch_add (seccomp, extra_arches[i]); |
430 |
if (r < 0 && r != -EEXIST) |
431 |
return flatpak_fail (error, "Failed to add multiarch architecture to seccomp filter"); |
432 |
} |
433 |
} |
434 |
} |
435 |
} |
436 |
|
437 |
/* TODO: Should we filter the kernel keyring syscalls in some way? |
438 |
* We do want them to be used by desktop apps, but they could also perhaps |
439 |
* leak system stuff or secrets from other apps. |
440 |
*/ |
441 |
|
442 |
for (i = 0; i < G_N_ELEMENTS (syscall_blacklist); i++) |
443 |
{ |
444 |
int scall = syscall_blacklist[i].scall; |
445 |
if (syscall_blacklist[i].arg) |
446 |
r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 1, *syscall_blacklist[i].arg); |
447 |
else |
448 |
r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 0); |
449 |
if (r < 0 && r == -EFAULT /* unknown syscall */) |
450 |
return flatpak_fail (error, "Failed to block syscall %d", scall); |
451 |
} |
452 |
|
453 |
if (!devel) |
454 |
{ |
455 |
for (i = 0; i < G_N_ELEMENTS (syscall_nondevel_blacklist); i++) |
456 |
{ |
457 |
int scall = syscall_nondevel_blacklist[i].scall; |
458 |
if (syscall_nondevel_blacklist[i].arg) |
459 |
r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 1, *syscall_nondevel_blacklist[i].arg); |
460 |
else |
461 |
r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 0); |
462 |
|
463 |
if (r < 0 && r == -EFAULT /* unknown syscall */) |
464 |
return flatpak_fail (error, "Failed to block syscall %d", scall); |
465 |
} |
466 |
} |
467 |
|
468 |
/* Socket filtering doesn't work on e.g. i386, so ignore failures here |
469 |
* However, we need to user seccomp_rule_add_exact to avoid libseccomp doing |
470 |
* something else: https://github.com/seccomp/libseccomp/issues/8 */ |
471 |
for (i = 0; i < G_N_ELEMENTS (socket_family_blacklist); i++) |
472 |
{ |
473 |
int family = socket_family_blacklist[i]; |
474 |
if (i == G_N_ELEMENTS (socket_family_blacklist) - 1) |
475 |
seccomp_rule_add_exact (seccomp, SCMP_ACT_ERRNO (EAFNOSUPPORT), SCMP_SYS (socket), 1, SCMP_A0 (SCMP_CMP_GE, family)); |
476 |
else |
477 |
seccomp_rule_add_exact (seccomp, SCMP_ACT_ERRNO (EAFNOSUPPORT), SCMP_SYS (socket), 1, SCMP_A0 (SCMP_CMP_EQ, family)); |
478 |
} |
479 |
|
480 |
fd = g_file_open_tmp ("flatpak-seccomp-XXXXXX", &path, error); |
481 |
if (fd == -1) |
482 |
return FALSE; |
483 |
|
484 |
unlink (path); |
485 |
|
486 |
if (seccomp_export_bpf (seccomp, fd) != 0) |
487 |
{ |
488 |
close (fd); |
489 |
return flatpak_fail (error, "Failed to export bpf"); |
490 |
} |
491 |
|
492 |
lseek (fd, 0, SEEK_SET); |
493 |
|
494 |
fd_str = g_strdup_printf ("%d", fd); |
495 |
if (fd_array) |
496 |
g_array_append_val (fd_array, fd); |
497 |
|
498 |
add_args (argv_array, |
499 |
"--seccomp", fd_str, |
500 |
NULL); |
501 |
|
502 |
fd = -1; /* Don't close on success */ |
503 |
|
504 |
return TRUE; |
505 |
} |
506 |
#endif |
507 |
|
508 |
#ifdef HAVE_BWRAP |
509 |
static gboolean |
510 |
add_bwrap (GPtrArray *array, |
511 |
ScriptExec *script) |
512 |
{ |
513 |
g_return_val_if_fail (script->outdir != NULL, FALSE); |
514 |
g_return_val_if_fail (script->s_infile != NULL, FALSE); |
515 |
|
516 |
add_args (array, |
517 |
"bwrap", |
518 |
"--ro-bind", "/usr", "/usr", |
519 |
"--ro-bind", "/lib", "/lib", |
520 |
"--ro-bind", "/lib64", "/lib64", |
521 |
"--proc", "/proc", |
522 |
"--dev", "/dev", |
523 |
"--symlink", "usr/bin", "/bin", |
524 |
"--symlink", "usr/sbin", "/sbin", |
525 |
"--chdir", "/", |
526 |
"--setenv", "GIO_USE_VFS", "local", |
527 |
"--unshare-all", |
528 |
"--die-with-parent", |
529 |
NULL); |
530 |
|
531 |
add_env (array, "G_MESSAGES_DEBUG"); |
532 |
add_env (array, "G_MESSAGES_PREFIXED"); |
533 |
|
534 |
/* Add gnome-desktop's install prefix if needed */ |
535 |
if (g_strcmp0 (INSTALL_PREFIX, "") != 0 && |
536 |
g_strcmp0 (INSTALL_PREFIX, "/usr") != 0 && |
537 |
g_strcmp0 (INSTALL_PREFIX, "/usr/") != 0) |
538 |
{ |
539 |
add_args (array, |
540 |
"--ro-bind", INSTALL_PREFIX, INSTALL_PREFIX, |
541 |
NULL); |
542 |
} |
543 |
|
544 |
g_ptr_array_add (array, g_strdup ("--bind")); |
545 |
g_ptr_array_add (array, g_strdup (script->outdir)); |
546 |
g_ptr_array_add (array, g_strdup ("/tmp")); |
547 |
|
548 |
/* We make sure to also re-use the original file's original |
549 |
* extension in case it's useful for the thumbnailer to |
550 |
* identify the file type */ |
551 |
g_ptr_array_add (array, g_strdup ("--ro-bind")); |
552 |
g_ptr_array_add (array, g_strdup (script->infile)); |
553 |
g_ptr_array_add (array, g_strdup (script->s_infile)); |
554 |
|
555 |
return TRUE; |
556 |
} |
557 |
#endif /* HAVE_BWRAP */ |
558 |
|
559 |
static char ** |
103 |
static char ** |
560 |
expand_thumbnailing_cmd (const char *cmd, |
104 |
expand_thumbnailing_script (const char *cmd, |
561 |
ScriptExec *script, |
105 |
ScriptExec *script, |
562 |
int size, |
106 |
int size, |
563 |
GError **error) |
107 |
GError **error) |
564 |
{ |
108 |
{ |
565 |
GPtrArray *array; |
109 |
GPtrArray *array; |
566 |
g_auto(GStrv) cmd_elems = NULL; |
110 |
g_auto(GStrv) cmd_elems = NULL; |
567 |
guint i; |
111 |
guint i; |
568 |
gboolean got_in, got_out; |
112 |
gboolean got_in, got_out; |
|
|
113 |
g_autofree char *sandboxed_path = NULL; |
569 |
|
114 |
|
570 |
if (!g_shell_parse_argv (cmd, NULL, &cmd_elems, error)) |
115 |
if (!g_shell_parse_argv (cmd, NULL, &cmd_elems, error)) |
571 |
return NULL; |
116 |
return NULL; |
572 |
|
117 |
|
573 |
script->thumbnailer_name = g_strdup (cmd_elems[0]); |
|
|
574 |
|
575 |
array = g_ptr_array_new_with_free_func (g_free); |
118 |
array = g_ptr_array_new_with_free_func (g_free); |
576 |
|
119 |
|
577 |
#ifdef HAVE_BWRAP |
|
|
578 |
if (script->sandbox) |
579 |
{ |
580 |
if (!add_bwrap (array, script)) |
581 |
{ |
582 |
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, |
583 |
"Bubblewrap setup failed"); |
584 |
goto bail; |
585 |
} |
586 |
} |
587 |
#endif |
588 |
|
589 |
#ifdef ENABLE_SECCOMP |
590 |
if (script->sandbox) |
591 |
{ |
592 |
const char *arch; |
593 |
|
594 |
arch = flatpak_get_arch (); |
595 |
g_assert (arch); |
596 |
if (!setup_seccomp (array, |
597 |
script->fd_array, |
598 |
arch, |
599 |
FALSE, |
600 |
FALSE, |
601 |
error)) |
602 |
{ |
603 |
goto bail; |
604 |
} |
605 |
} |
606 |
#endif |
607 |
|
608 |
got_in = got_out = FALSE; |
120 |
got_in = got_out = FALSE; |
609 |
for (i = 0; cmd_elems[i] != NULL; i++) |
121 |
for (i = 0; cmd_elems[i] != NULL; i++) |
610 |
{ |
122 |
{ |
Lines 612-619
expand_thumbnailing_cmd (const char *cmd,
Link Here
|
612 |
|
124 |
|
613 |
expanded = expand_thumbnailing_elem (cmd_elems[i], |
125 |
expanded = expand_thumbnailing_elem (cmd_elems[i], |
614 |
size, |
126 |
size, |
615 |
script->s_infile ? script->s_infile : script->infile, |
127 |
script->infile, |
616 |
script->s_outfile ? script->s_outfile : script->outfile, |
128 |
script->outfile, |
617 |
&got_in, |
129 |
&got_in, |
618 |
&got_out); |
130 |
&got_out); |
619 |
|
131 |
|
Lines 642-768
bail:
Link Here
|
642 |
return NULL; |
154 |
return NULL; |
643 |
} |
155 |
} |
644 |
|
156 |
|
645 |
static void |
|
|
646 |
child_setup (gpointer user_data) |
647 |
{ |
648 |
GArray *fd_array = user_data; |
649 |
guint i; |
650 |
|
651 |
/* If no fd_array was specified, don't care. */ |
652 |
if (fd_array == NULL) |
653 |
return; |
654 |
|
655 |
/* Otherwise, mark not - close-on-exec all the fds in the array */ |
656 |
for (i = 0; i < fd_array->len; i++) |
657 |
fcntl (g_array_index (fd_array, int, i), F_SETFD, 0); |
658 |
} |
659 |
|
660 |
static void |
157 |
static void |
661 |
script_exec_free (ScriptExec *exec) |
158 |
script_exec_free (ScriptExec *exec) |
662 |
{ |
159 |
{ |
663 |
if (exec == NULL) |
|
|
664 |
return; |
665 |
|
666 |
g_free (exec->thumbnailer_name); |
667 |
g_free (exec->infile); |
160 |
g_free (exec->infile); |
668 |
if (exec->infile_tmp) |
|
|
669 |
{ |
670 |
g_unlink (exec->infile_tmp); |
671 |
g_free (exec->infile_tmp); |
672 |
} |
673 |
if (exec->outfile) |
161 |
if (exec->outfile) |
674 |
{ |
162 |
{ |
675 |
g_unlink (exec->outfile); |
163 |
g_unlink (exec->outfile); |
676 |
g_free (exec->outfile); |
164 |
g_free (exec->outfile); |
677 |
} |
165 |
} |
678 |
if (exec->outdir) |
|
|
679 |
{ |
680 |
if (g_rmdir (exec->outdir) < 0) |
681 |
{ |
682 |
g_warning ("Could not remove %s, thumbnailer %s left files in directory", |
683 |
exec->outdir, exec->thumbnailer_name); |
684 |
} |
685 |
g_free (exec->outdir); |
686 |
} |
687 |
g_free (exec->s_infile); |
688 |
g_free (exec->s_outfile); |
689 |
if (exec->fd_array) |
690 |
g_array_free (exec->fd_array, TRUE); |
691 |
g_free (exec); |
166 |
g_free (exec); |
692 |
} |
167 |
} |
693 |
|
168 |
|
694 |
static void |
|
|
695 |
clear_fd (gpointer data) |
696 |
{ |
697 |
int *fd_p = data; |
698 |
if (fd_p != NULL && *fd_p != -1) |
699 |
close (*fd_p); |
700 |
} |
701 |
|
702 |
static ScriptExec * |
169 |
static ScriptExec * |
703 |
script_exec_new (const char *uri, |
170 |
script_exec_new (const char *uri) |
704 |
GError **error) |
|
|
705 |
{ |
171 |
{ |
706 |
ScriptExec *exec; |
172 |
ScriptExec *exec; |
707 |
g_autoptr(GFile) file = NULL; |
173 |
g_autoptr(GFile) file = NULL; |
|
|
174 |
int fd; |
175 |
g_autofree char *tmpname = NULL; |
708 |
|
176 |
|
709 |
exec = g_new0 (ScriptExec, 1); |
177 |
exec = g_new0 (ScriptExec, 1); |
710 |
#ifdef HAVE_BWRAP |
|
|
711 |
/* Bubblewrap is not used if the application is already sandboxed in |
712 |
* Flatpak as all privileges to create a new namespace are dropped when |
713 |
* the initial one is created. */ |
714 |
if (!g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR)) |
715 |
exec->sandbox = TRUE; |
716 |
#endif |
717 |
|
718 |
file = g_file_new_for_uri (uri); |
178 |
file = g_file_new_for_uri (uri); |
719 |
|
179 |
|
720 |
exec->infile = g_file_get_path (file); |
180 |
exec->infile = g_file_get_path (file); |
721 |
if (!exec->infile) |
181 |
if (!exec->infile) |
722 |
{ |
182 |
goto bail; |
723 |
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, |
|
|
724 |
"Could not get path for URI '%s'", uri); |
725 |
goto bail; |
726 |
} |
727 |
|
183 |
|
728 |
#ifdef HAVE_BWRAP |
184 |
fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, NULL); |
729 |
if (exec->sandbox) |
185 |
if (fd == -1) |
730 |
{ |
186 |
goto bail; |
731 |
char *tmpl; |
187 |
close (fd); |
732 |
g_autofree char *ext = NULL; |
188 |
exec->outfile = g_steal_pointer (&tmpname); |
733 |
g_autofree char *infile = NULL; |
|
|
734 |
|
735 |
exec->fd_array = g_array_new (FALSE, TRUE, sizeof (int)); |
736 |
g_array_set_clear_func (exec->fd_array, clear_fd); |
737 |
|
738 |
tmpl = g_strdup ("/tmp/gnome-desktop-thumbnailer-XXXXXX"); |
739 |
exec->outdir = g_mkdtemp (tmpl); |
740 |
if (!exec->outdir) |
741 |
{ |
742 |
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, |
743 |
"Could not create temporary sandbox directory"); |
744 |
goto bail; |
745 |
} |
746 |
exec->outfile = g_build_filename (exec->outdir, "gnome-desktop-thumbnailer.png", NULL); |
747 |
ext = get_extension (exec->infile); |
748 |
infile = g_strdup_printf ("gnome-desktop-file-to-thumbnail.%s", ext); |
749 |
exec->infile_tmp = g_build_filename (exec->outdir, infile, NULL); |
750 |
|
751 |
exec->s_infile = g_build_filename ("/tmp/", infile, NULL); |
752 |
exec->s_outfile = g_build_filename ("/tmp/", "gnome-desktop-thumbnailer.png", NULL); |
753 |
} |
754 |
else |
755 |
#endif |
756 |
{ |
757 |
int fd; |
758 |
g_autofree char *tmpname = NULL; |
759 |
|
760 |
fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, error); |
761 |
if (fd == -1) |
762 |
goto bail; |
763 |
close (fd); |
764 |
exec->outfile = g_steal_pointer (&tmpname); |
765 |
} |
766 |
|
189 |
|
767 |
return exec; |
190 |
return exec; |
768 |
|
191 |
|
Lines 771-792
bail:
Link Here
|
771 |
return NULL; |
194 |
return NULL; |
772 |
} |
195 |
} |
773 |
|
196 |
|
774 |
static void |
|
|
775 |
print_script_debug (GStrv expanded_script) |
776 |
{ |
777 |
GString *out; |
778 |
guint i; |
779 |
|
780 |
out = g_string_new (NULL); |
781 |
|
782 |
for (i = 0; expanded_script[i]; i++) |
783 |
g_string_append_printf (out, "%s ", expanded_script[i]); |
784 |
g_string_append_printf (out, "\n"); |
785 |
|
786 |
g_debug ("About to launch script: %s", out->str); |
787 |
g_string_free (out, TRUE); |
788 |
} |
789 |
|
790 |
GBytes * |
197 |
GBytes * |
791 |
gnome_desktop_thumbnail_script_exec (const char *cmd, |
198 |
gnome_desktop_thumbnail_script_exec (const char *cmd, |
792 |
int size, |
199 |
int size, |
Lines 800-816
gnome_desktop_thumbnail_script_exec (const char *cmd,
Link Here
|
800 |
GBytes *image = NULL; |
207 |
GBytes *image = NULL; |
801 |
ScriptExec *exec; |
208 |
ScriptExec *exec; |
802 |
|
209 |
|
803 |
exec = script_exec_new (uri, error); |
210 |
exec = script_exec_new (uri); |
804 |
if (!exec) |
211 |
expanded_script = expand_thumbnailing_script (cmd, exec, size, error); |
805 |
goto out; |
|
|
806 |
expanded_script = expand_thumbnailing_cmd (cmd, exec, size, error); |
807 |
if (expanded_script == NULL) |
212 |
if (expanded_script == NULL) |
808 |
goto out; |
213 |
goto out; |
809 |
|
214 |
|
810 |
print_script_debug (expanded_script); |
215 |
#if 0 |
|
|
216 |
guint i; |
217 |
|
218 |
g_print ("About to launch script: "); |
219 |
for (i = 0; expanded_script[i]; i++) |
220 |
g_print ("%s ", expanded_script[i]); |
221 |
g_print ("\n"); |
222 |
#endif |
811 |
|
223 |
|
812 |
ret = g_spawn_sync (NULL, expanded_script, NULL, G_SPAWN_SEARCH_PATH, |
224 |
ret = g_spawn_sync (NULL, expanded_script, NULL, G_SPAWN_SEARCH_PATH, |
813 |
child_setup, exec->fd_array, NULL, &error_out, |
225 |
NULL, NULL, NULL, &error_out, |
814 |
&exit_status, error); |
226 |
&exit_status, error); |
815 |
if (ret && g_spawn_check_exit_status (exit_status, error)) |
227 |
if (ret && g_spawn_check_exit_status (exit_status, error)) |
816 |
{ |
228 |
{ |
Lines 830-832
out:
Link Here
|
830 |
return image; |
242 |
return image; |
831 |
} |
243 |
} |
832 |
|
244 |
|
|
|
245 |
|