This document can now be found here:
https://chromium.googlesource.com/chromiumos/docs/+/HEAD/sandboxing.md The content below is a (possibly out-of-date) copy for easier searching. Sandboxing Chrome OS system servicesIn Chrome OS, OS-level functionality (such as configuring network interfaces) is implemented by a collection of system services, and provided to Chrome over D-Bus. These system services have greater system and hardware access than the Chrome browser. Separating functionality like this prevents an attacker exploiting the Chrome browser through a malicious website to be able to access OS-level functionality directly. If Chrome were able to directly control network interfaces, a compromise in Chrome would give the attacker almost full control over the system. For example, by having a separate network manager, we can reduce the functionality exposed to an attacker to just querying interfaces and performing pre-determined actions on them. Chrome OS uses a few different mechanisms to isolate system services from Chrome and from each other. We use a helper program called Minijail (executable Best practices for writing secure system servicesJust remember that code has bugs, and these bugs can be used to take control of the code. An attacker can then do anything the original code was allowed to do. Therefore, code should only be given the absolute minimum level of privilege needed to perform its function. Aim to keep your code lean, and your privileges low. Don‘t run your service as Use the libraries provided by the system/SDK. In Chrome OS, libchrome and libbrillo (née libchromeos) offer a lot of functionality to avoid reinventing the wheel, poorly. Don‘t reinvent IPC, use D-Bus or Mojo. Don’t open listening sockets, connect to the required service. Don't (ab)use shell scripts, shell script logic is harder to reason about and shell command-injection bugs are easy to miss. If you need functionality separated from your main service, use normal C++ binaries, not shell scripts. Moreover, when you execute them, consider further restricting their privileges (see section Minijail wrappers). Just tell me what I need to do
User idsThe first sandboxing mechanism is user ids. We try to run each service as its own user id, different from the env PERMISSION_BROKER_GRANT_GROUP=devbroker-access start on starting system-services stop on stopping system-services respawn # Run as 'devbroker' user. exec minijail0 -u devbroker -c 'cap_chown,cap_fowner+eip' -- \ /usr/bin/permission_broker --access_group=${PERMISSION_BROKER_GRANT_GROUP} Minijail's The user ( Next, the user needs to be installed on the system. An example of this (again for a different user) can be found in the following CL: https://crrev.com/c/383076 See the Chrome OS user accounts README for more details. There‘s a test in the CQ that keeps track of the users present on the system that request additional access (e.g. listing more than one user in a group). If your user does that, the test baseline has to be updated at the same time the accounts are added with another CL (e.g. https://crrev.com/c/894192). If you’re unsure whether you need this, the PreCQ/CQ will reject your CL when the test fails, so if the tests pass, you should be good to go! You can use CQ-DEPEND to land the CLs together (see How do I specify the dependencies of a change?). CapabilitiesSome programs, however, require some of the system access usually granted only to the root user. We use Linux capabilities for this. Capabilities allow us to grant a specific subset of root's privileges to an otherwise unprivileged process. The link above has the full list of capabilities that can be granted to a process. Some of them are equivalent to root, so we avoid granting those. In general, most processes need capabilities to configure network interfaces, access raw sockets, or performing specific file operations. Capabilities are passed to Minijail using the env PERMISSION_BROKER_GRANT_GROUP=devbroker-access start on starting system-services stop on stopping system-services respawn # Run as <devbroker> user. # Grant CAP_CHOWN and CAP_FOWNER. exec minijail0 -u devbroker -c 'cap_chown,cap_fowner+eip' -- \ /usr/bin/permission_broker --access_group=${PERMISSION_BROKER_GRANT_GROUP} Capabilities are expressed using the format that cap_from_text(3) accepts. NamespacesMany resources in the Linux world can be isolated now such that a process has its own view of things. For example, it has its own list of mount points, and any changes it makes (unmounting, mounting more devices, etc...) are only visible to it. This helps keep a broken process from messing up the settings of other processes. For more in-depth details, see the namespaces overview. In Chromium OS, we like to see every process/daemon run under as many unique namespaces as possible. Many are easy to enable/rationalize about: if you don't use a particular resource, then isolating it is straightforward. If you do rely on it though, it can take more effort. Here‘s a quick overview. Use the command line option if the description below matches your service (or if you don’t know what functionality it‘s talking about -- most likely you aren’t using it!).
The
Seccomp filtersRemoving access to the filesystem and to root-only functionality is not enough to completely isolate a system service. A service running as its own user id and with no capabilities has access to a big chunk of the kernel API. The kernel therefore exposes a huge attack surface to non-root processes, and we would like to restrict what kernel functionality is available for sandboxed processes. The mechanism we use is called Seccomp-BPF. Minijail can take a policy file that describes what syscalls will be allowed, what syscalls will be denied, and what syscalls will only be allowed with specific arguments. The full description of the policy file language can be found in the Abridged policy for # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. read: 1 ioctl: 1 write: 1 timerfd_settime: 1 open: 1 poll: 1 close: 1 mmap: 1 mremap: 1 munmap: 1 mprotect: 1 lseek: 1 # Allow socket(domain==PF_LOCAL) or socket(domain==PF_NETLINK) socket: arg0 == 0x1 || arg0 == 0x10 # Allow PR_SET_NAME from libchrome's base::PlatformThread::SetName() prctl: arg0 == 0xf Any syscall not explicitly mentioned, when called, results in the process being killed. The policy file can also tell the kernel to fail the system call (returning -1 and setting # execve: return EPERM execve: return 1 To write a policy file, run the target program under The policy file needs to be installed in the system, so we need to add it to the ebuild file: # Install seccomp policy file. insinto /usr/share/policy use seccomp && newins "mtpd-seccomp-${ARCH}.policy" mtpd-seccomp.policy And finally, the policy file has to be passed to Minijail, using the # use minijail (drop root, set no_new_privs, set seccomp filter). # Mount /proc, /sys, /dev, /run/udev so that USB devices can be # discovered. Also mount /run/dbus to communicate with D-Bus. exec minijail0 -i -I -p -l -r -v -t -u mtp -g mtp -G \ -P /var/empty -b / -b /proc -b /sys -b /dev \ -k tmpfs,/run,tmpfs,0xe -b /run/dbus -b /run/udev \ -n -S /usr/share/policy/mtpd-seccomp.policy -- \ /usr/sbin/mtpd -minloglevel="${MTPD_MINLOGLEVEL}" Detailed instructions for generating a seccomp policy
rt_sigaction(SIGRTMIN, {<sa_handler>, [], SA_RESTORER|SA_SIGINFO, <sa_restorer>}, NULL, 8) = 0 rt_sigaction(SIGRT_1, {<sa_handler>, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, <sa_restorer>}, NULL, 8) = 0 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 brk(NULL) = 0x7f8a0656e000 brk(<addr>) = <addr>
NOTICE kernel: [ 586.706239] audit: type=1326 audit(1484586246.124:6): ... comm="<executable>" exe="/path/to/executable" sig=31 syscall=130 compat=0 ip=0x7f4f214881d6 code=0x0
Securely mounting cryptohome daemon store foldersSome daemons store user data on the user‘s cryptohome under However, if a daemon is already running inside a mount namespace ( To set up a cryptohome daemon store folder that propagates into your daemon‘s mount namespace, add this code to the src_install section of your daemon’s ebuild: local daemon_store="/etc/daemon-store/<daemon_name>" dodir "${daemon_store}" fperms 0700 "${daemon_store}" fowners <daemon_user>:<daemon_group> "${daemon_store}" This directory is never used directly. It merely serves as a secure template for the chromeos_startup script, which picks it up and creates In your daemon's init script, mount that folder as slave in your mount namespace. Be sure not to mount all of minijail0 -Kslave \ -k 'tmpfs,/run,tmpfs,MS_NOSUID|MS_NODEV|MS_NOEXEC' \ -b /run/daemon-store/<daemon_name> \ ... During sign-in, when the user's cryptohome is mounted, Cryptohome creates Your daemon can now use Be sure not to write to the folder before cryptohome is mounted. Consider listening to Session Manager's The The following diagram illustrates the mount event propagation: |
Chromium OS > Chromium OS Developer Guide >