Surely, Flatpak is safe!

Or… is it?

After packaging wechat-uos-qt for quite some time (that duplicated package wechat-universal-bwrap is not deleted, great AUR moderation), I decided to see how Flatpak handles sandboxing (We are looking for ways to support URL opening). As usual, you search for WeChat in GNOME Software, click install, and it should be done, right? Well, I glad there was one more thing done by me: scroll down and see this:

Permissions

Whoa… Offering a proprietary app Downloads/ access? That doesn’t even make any sense. wechat-uos-qt already resolved file transfer using a Open WeChat Data action that’ll take you to the WeChat sandbox home. This isolates file and offers a convenient way to share files to whoever still using the proprietary messenger app. This seems inconsistent when compared with Flatpak Documentation:

One of Flatpak’s main goals is to increase the security of desktop systems by isolating applications from one another. This is achieved using sandboxing

While application developers have control over the sandbox permissions they wish to configure, good practice is encouraged and can be enforced.

Yes, if Flatpak does somehow enforce sandbox and good practices, then I guess allowing proprietary apps to read, write, modify your Downloads folder is a good practice. I mean, the entire permissions guideline is pretty good. That is, when these guidelines are enforced.

While the above example is a bad access rule set, see this portion of code in GPU Screen Recorder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if(geteuid() == 0) {
has_perm = true;
} else {
cap_t kms_server_cap = cap_get_file(server_filepath);
if(kms_server_cap) {
cap_flag_value_t res = CAP_CLEAR;
cap_get_flag(kms_server_cap, CAP_SYS_ADMIN, CAP_PERMITTED, &res);
if(res == CAP_SET) {
//fprintf(stderr, "has permission!\n");
has_perm = true;
} else {
//fprintf(stderr, "No permission:(\n");
}
cap_free(kms_server_cap);
} else if(!inside_flatpak) {
if(errno == ENODATA)
fprintf(stderr, "gsr info: gsr_kms_client_init: gsr-kms-server is missing sys_admin cap and will require root authentication. To bypass this automatically, run: sudo setcap cap_sys_admin+ep '%s'\n", server_filepath);
else
fprintf(stderr, "gsr info: gsr_kms_client_init: failed to get cap\n");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if(pid == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_init: fork failed, error: %s\n", strerror(errno));
goto err;
} else if(pid == 0) { /* child */
if(inside_flatpak) {
const char *args[] = { "flatpak-spawn", "--host", "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy", self->initial_socket_path, card_path, home, NULL };
execvp(args[0], (char *const*)args);
} else if(has_perm) {
const char *args[] = { server_filepath, self->initial_socket_path, card_path, NULL };
execvp(args[0], (char *const*)args);
} else {
const char *args[] = { "pkexec", server_filepath, self->initial_socket_path, card_path, NULL };
execvp(args[0], (char *const*)args);
}
fprintf(stderr, "gsr error: gsr_kms_client_init: execvp failed, error: %s\n", strerror(errno));
_exit(127);
} else { /* parent */
self->kms_server_pid = pid;
}

I am no C programmer, but what these code, or should I say GPU Screen Recorder do in Flatpak, is that if CAP_SYS_ADMIN doesn’t exist, it’ll try pkexec to acquire root permission and give itself CAP_SYS_ADMIN, the new root (which, according to the manual is overloaded as hell). What you get is one more attack surface. What we know is Flatpak has serious security holes. This can probably be mitigated using bubblewrap-suid and a --cap-drop ALL (Though it opens another security hole as suid binaries are involved), but applications and Flathub itself shouldn’t accept this insecure operations by default. It is okay to provide a good out-of-the-box experience, but compromising security doesn’t line up with what Flatpak promises. This is a combination of lacking moderation and guidelines not being enforced. And it is why you shouldn’t trust bold claims.

The conclusion

Flatpak is insecure by default, and confusingly misleading. Check what permissions an app might have before launching. Running untrusted code is never safe, even in a sandbox.