diff options
| author | Zach Riggle <zachriggle@users.noreply.github.com> | 2018-09-17 04:33:11 -0500 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-09-17 11:33:11 +0200 |
| commit | 0eca949a6c271b879d582e01c3d1d79dc704172c (patch) | |
| tree | ff6c780fbf4993aaa11036de414a74c28ac0fb5d /executor | |
| parent | fd85ed48854729938fad986fc81e1c57a667fb36 (diff) | |
RFC: android: Add support for untrusted_app sandboxing (#697)
executor: add support for android_untrusted_app sandbox
This adds a new sandbox type, 'android_untrusted_app', which restricts
syz-executor to the privileges which are available to third-party applications,
e.g. those installed from the Google Play store.
In particular, this uses the UID space reserved for applications (instead of
the 'setuid' sandbox, which uses the traditional 'nobody' user / 65534)
as well as a set of groups which the Android-specific kernels are aware of,
and finally ensures that the SELinux context is set appropriately.
Dependencies on libselinux are avoided by manually implementing the few
functions that are needed to change the context of the current process,
and arbitrary files. The underlying mechanisms are relatively simple.
Fixes google/syzkaller#643
Test: make presubmit
Bug: http://b/112900774
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common.h | 2 | ||||
| -rw-r--r-- | executor/common_akaros.h | 1 | ||||
| -rw-r--r-- | executor/common_bsd.h | 1 | ||||
| -rw-r--r-- | executor/common_fuchsia.h | 1 | ||||
| -rw-r--r-- | executor/common_linux.h | 143 | ||||
| -rw-r--r-- | executor/common_test.h | 1 | ||||
| -rw-r--r-- | executor/common_windows.h | 1 | ||||
| -rw-r--r-- | executor/executor.cc | 13 |
8 files changed, 155 insertions, 8 deletions
diff --git a/executor/common.h b/executor/common.h index 15b279956..fdc35436c 100644 --- a/executor/common.h +++ b/executor/common.h @@ -628,7 +628,7 @@ static void loop(void) [[RESULTS]] -#if SYZ_THREADED || SYZ_REPEAT || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE +#if SYZ_THREADED || SYZ_REPEAT || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP #if SYZ_THREADED void execute_call(int call) #elif SYZ_REPEAT diff --git a/executor/common_akaros.h b/executor/common_akaros.h index a0e42088a..8d83749dc 100644 --- a/executor/common_akaros.h +++ b/executor/common_akaros.h @@ -37,4 +37,5 @@ void child() #if SYZ_EXECUTOR #define do_sandbox_setuid() 0 #define do_sandbox_namespace() 0 +#define do_sandbox_android_untrusted_app() 0 #endif diff --git a/executor/common_bsd.h b/executor/common_bsd.h index 004009861..49c6b36de 100644 --- a/executor/common_bsd.h +++ b/executor/common_bsd.h @@ -17,6 +17,7 @@ static int do_sandbox_none(void) #if SYZ_EXECUTOR #define do_sandbox_setuid() 0 #define do_sandbox_namespace() 0 +#define do_sandbox_android_untrusted_app() 0 #endif #if GOOS_openbsd diff --git a/executor/common_fuchsia.h b/executor/common_fuchsia.h index 219aee865..fb08aa2a0 100644 --- a/executor/common_fuchsia.h +++ b/executor/common_fuchsia.h @@ -244,6 +244,7 @@ static int do_sandbox_none(void) #if SYZ_EXECUTOR #define do_sandbox_setuid() 0 #define do_sandbox_namespace() 0 +#define do_sandbox_android_untrusted_app() 0 #endif // Ugly way to work around gcc's "error: function called through a non-compatible type". diff --git a/executor/common_linux.h b/executor/common_linux.h index 2f5692105..5ae770800 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -545,7 +545,7 @@ static long syz_open_pts(long a0, long a1) #endif #if SYZ_EXECUTOR || __NR_syz_init_net_socket -#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP #include <fcntl.h> #include <sched.h> #include <sys/stat.h> @@ -1438,7 +1438,7 @@ static void setup_binfmt_misc() } #endif -#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP #include <errno.h> #include <sys/mount.h> @@ -1452,9 +1452,7 @@ static void setup_common() setup_binfmt_misc(); #endif } -#endif -#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE #include <sched.h> #include <sys/prctl.h> #include <sys/resource.h> @@ -1738,6 +1736,143 @@ static int do_sandbox_namespace(void) } #endif +#if SYZ_EXECUTOR || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP +#include <fcntl.h> // open(2) +#include <grp.h> // setgroups +#include <sys/xattr.h> // setxattr, getxattr + +#define AID_NET_BT_ADMIN 3001 +#define AID_NET_BT 3002 +#define AID_INET 3003 +#define AID_EVERYBODY 9997 +#define AID_APP 10000 + +#define UNTRUSTED_APP_UID AID_APP + 999 +#define UNTRUSTED_APP_GID AID_APP + 999 + +const char* SELINUX_CONTEXT_UNTRUSTED_APP = "u:r:untrusted_app:s0:c512,c768"; +const char* SELINUX_LABEL_APP_DATA_FILE = "u:object_r:app_data_file:s0:c512,c768"; +const char* SELINUX_CONTEXT_FILE = "/proc/thread-self/attr/current"; +const char* SELINUX_XATTR_NAME = "security.selinux"; + +const gid_t UNTRUSTED_APP_GROUPS[] = {UNTRUSTED_APP_GID, AID_NET_BT_ADMIN, AID_NET_BT, AID_INET, AID_EVERYBODY}; +const size_t UNTRUSTED_APP_NUM_GROUPS = sizeof(UNTRUSTED_APP_GROUPS) / sizeof(UNTRUSTED_APP_GROUPS[0]); + +// Similar to libselinux getcon(3), but: +// - No library dependency +// - No dynamic memory allocation +// - Uses fail() instead of returning an error code +static void syz_getcon(char* context, size_t context_size) +{ + int fd = open(SELINUX_CONTEXT_FILE, O_RDONLY); + + if (fd < 0) + fail("getcon: Couldn't open %s", SELINUX_CONTEXT_FILE); + + ssize_t nread = read(fd, context, context_size); + + close(fd); + + if (nread <= 0) + fail("getcon: Failed to read from %s", SELINUX_CONTEXT_FILE); + + // The contents of the context file MAY end with a newline + // and MAY not have a null terminator. Handle this here. + if (context[nread - 1] == '\n') + context[nread - 1] = '\0'; +} + +// Similar to libselinux setcon(3), but: +// - No library dependency +// - No dynamic memory allocation +// - Uses fail() instead of returning an error code +static void syz_setcon(const char* context) +{ + char new_context[512]; + + // Attempt to write the new context + int fd = open(SELINUX_CONTEXT_FILE, O_WRONLY); + + if (fd < 0) + fail("setcon: Could not open %s", SELINUX_CONTEXT_FILE); + + ssize_t bytes_written = write(fd, context, strlen(context)); + + // N.B.: We cannot reuse this file descriptor, since the target SELinux context + // may not be able to read from it. + close(fd); + + if (bytes_written != (ssize_t)strlen(context)) + fail("setcon: Could not write entire context. Wrote %zi, expected %zu", bytes_written, strlen(context)); + + // Validate the transition by checking the context + syz_getcon(new_context, sizeof(new_context)); + + if (strcmp(context, new_context) != 0) + fail("setcon: Failed to change to %s, context is %s", context, new_context); +} + +// Similar to libselinux getfilecon(3), but: +// - No library dependency +// - No dynamic memory allocation +// - Uses fail() instead of returning an error code +static int syz_getfilecon(const char* path, char* context, size_t context_size) +{ + int length = getxattr(path, SELINUX_XATTR_NAME, context, context_size); + + if (length == -1) + fail("getfilecon: getxattr failed"); + + return length; +} + +// Similar to libselinux setfilecon(3), but: +// - No library dependency +// - No dynamic memory allocation +// - Uses fail() instead of returning an error code +static void syz_setfilecon(const char* path, const char* context) +{ + char new_context[512]; + + if (setxattr(path, SELINUX_XATTR_NAME, context, strlen(context) + 1, 0) != 0) + fail("setfilecon: setxattr failed"); + + if (syz_getfilecon(path, new_context, sizeof(new_context)) != 0) + fail("setfilecon: getfilecon failed"); + + if (strcmp(context, new_context) != 0) + fail("setfilecon: could not set context to %s, currently %s", context, new_context); +} + +static int do_sandbox_android_untrusted_app(void) +{ + setup_common(); + sandbox_common(); + + if (setgroups(UNTRUSTED_APP_NUM_GROUPS, UNTRUSTED_APP_GROUPS) != 0) + fail("setgroups failed"); + + if (setresgid(UNTRUSTED_APP_GID, UNTRUSTED_APP_GID, UNTRUSTED_APP_GID) != 0) + fail("setresgid failed"); + + if (setresuid(UNTRUSTED_APP_UID, UNTRUSTED_APP_UID, UNTRUSTED_APP_UID) != 0) + fail("setresuid failed"); + + syz_setfilecon(".", SELINUX_LABEL_APP_DATA_FILE); + syz_setcon(SELINUX_CONTEXT_UNTRUSTED_APP); + +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE + initialize_tun(); +#endif +#if SYZ_EXECUTOR || SYZ_ENABLE_NETDEV + initialize_netdevices(); +#endif + + loop(); + doexit(1); +} +#endif + #if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_USE_TMP_DIR #include <dirent.h> #include <errno.h> diff --git a/executor/common_test.h b/executor/common_test.h index 75ec81721..dc162a833 100644 --- a/executor/common_test.h +++ b/executor/common_test.h @@ -60,4 +60,5 @@ static int do_sandbox_none(void) #if SYZ_EXECUTOR #define do_sandbox_setuid() 0 #define do_sandbox_namespace() 0 +#define do_sandbox_android_untrusted_app() 0 #endif diff --git a/executor/common_windows.h b/executor/common_windows.h index b5520197f..2a89ea469 100644 --- a/executor/common_windows.h +++ b/executor/common_windows.h @@ -115,4 +115,5 @@ static int do_sandbox_none(void) #if SYZ_EXECUTOR #define do_sandbox_setuid() 0 #define do_sandbox_namespace() 0 +#define do_sandbox_android_untrusted_app() 0 #endif diff --git a/executor/executor.cc b/executor/executor.cc index 22fe8d50e..2244cc797 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -101,6 +101,7 @@ enum sandbox_type { sandbox_none, sandbox_setuid, sandbox_namespace, + sandbox_android_untrusted_app }; bool flag_debug; @@ -369,6 +370,9 @@ int main(int argc, char** argv) case sandbox_namespace: status = do_sandbox_namespace(); break; + case sandbox_android_untrusted_app: + status = do_sandbox_android_untrusted_app(); + break; default: fail("unknown sandbox type"); } @@ -415,6 +419,7 @@ void setup_control_pipes() void parse_env_flags(uint64 flags) { + // Note: Values correspond to ordering in pkg/ipc/ipc.go, e.g. FlagSandboxNamespace flag_debug = flags & (1 << 0); flag_cover = flags & (1 << 1); flag_sandbox = sandbox_none; @@ -422,9 +427,11 @@ void parse_env_flags(uint64 flags) flag_sandbox = sandbox_setuid; else if (flags & (1 << 3)) flag_sandbox = sandbox_namespace; - flag_enable_tun = flags & (1 << 4); - flag_enable_net_dev = flags & (1 << 5); - flag_enable_fault_injection = flags & (1 << 6); + else if (flags & (1 << 4)) + flag_sandbox = sandbox_android_untrusted_app; + flag_enable_tun = flags & (1 << 5); + flag_enable_net_dev = flags & (1 << 6); + flag_enable_fault_injection = flags & (1 << 7); } #if SYZ_EXECUTOR_USES_FORK_SERVER |
