@@ -11,6 +11,9 @@ environment, allowing ordinary users to run commands which give the illusion
of creating device nodes, changing file ownership, and otherwise doing
things necessary for creating distribution packages or filesystems.
+pseudo is not a security boundary and must not be used in security
+sensitive applications. See README.security for details.
+
Building pseudo requires GCC 5 or later (or clang), which the configure
script checks for.
new file mode 100644
@@ -0,0 +1,152 @@
+pseudo -- security considerations
+
+SUMMARY:
+
+pseudo is a build tool. It exists so that ordinary users can produce
+packages and filesystem images containing files that appear to be owned
+by root, device nodes, special permissions and extended attributes.
+
+pseudo is NOT a security boundary and must not be used in security
+sensitive applications. In particular, do not use pseudo:
+
+* to contain, confine or sandbox untrusted programs;
+* as a substitute for real privileges, a real chroot, a container or a
+ virtual machine;
+* to process filesystem trees that untrusted or concurrent processes can
+ modify while pseudo is running;
+* as the only guarantee that the ownership, permissions, SELinux labels
+ or file capabilities in a security critical image are correct.
+
+
+1. INTERCEPTION IS COOPERATIVE
+
+pseudo works by loading libpseudo with LD_PRELOAD (DYLD_INSERT_LIBRARIES
+on Darwin) and wrapping libc functions. Anything that does not go
+through those wrappers reaches the real filesystem directly, with the
+real privileges of the user who started pseudo. This includes:
+
+* statically linked programs;
+* programs that make system calls directly, including language runtimes
+ that do not use libc for file operations, and interfaces such as
+ io_uring;
+* setuid/setgid programs, for which the dynamic linker ignores
+ LD_PRELOAD;
+* any program that clears LD_PRELOAD, or that sets PSEUDO_UNLOAD or
+ PSEUDO_DISABLED (see pseudo_client.c), both of which are deliberately
+ supported ways to turn pseudo off.
+
+A program running under pseudo can therefore always step outside of it.
+pseudo cannot be used to restrict what a program does.
+
+The Darwin port is described as implemented, but it has not worked for
+a long time: System Integrity Protection and the hardened runtime block
+DYLD_INSERT_LIBRARIES, so it is unlikely to be useful.
+
+
+2. "ROOT" EXISTS ONLY IN THE DATABASE
+
+pseudo grants no privileges. Files on disk remain owned by the real
+user, and the ownership and modes that programs see under pseudo are
+read back from pseudo's database. The kernel does not enforce any of
+the emulated ownership or permissions.
+
+The real on-disk modes are also deliberately different from the
+emulated ones. PSEUDO_FS_MODE (pseudo_client.h) always adds owner read
+and write permission (and owner execute for directories) so that the
+emulated root can access everything, and removes group/other write,
+setuid and setgid bits from the real file. A file that is meant to be
+mode 0600 or 0000 and owned by root, for example /etc/shadow or a
+private key, is readable and writable on disk by the build user and by
+any process running as that user, whether or not it is under pseudo.
+
+Treat everything pseudo writes as accessible to the build user.
+
+
+3. FILE DESCRIPTORS BECOME PATHS (TOCTOU)
+
+Many security sensitive programs rely on the kernel resolving a file
+descriptor, or a path relative to a directory descriptor, to a specific
+inode. This is how they avoid races where a path component is replaced
+with a symlink between checking a file and using it. pseudo removes
+that protection in several ways:
+
+* Paths are canonicalized in user space. pseudo_fix_path()
+ (pseudo_util.c) walks each path component with lstat() and readlink()
+ and builds an absolute path, and the real function is then called
+ with that absolute path (see doc/chroot). A call such as
+ openat(dirfd, "file", ...) is no longer anchored to dirfd; it becomes
+ an operation on "<recorded path of dirfd>/file". Kernel guarantees
+ such as O_NOFOLLOW on intermediate components or RESOLVE_BENEATH are
+ not reproduced by this resolution, except in the few wrappers marked
+ preserve_paths in wrapfuncs.in, which handle paths themselves.
+
+* The path pseudo recorded for a descriptor (fd_path() in
+ pseudo_client.c) is used to resolve paths relative to it. That
+ record can be stale: the file or directory may have been renamed, or
+ the descriptor may have been inherited, received over a socket, or
+ opened by a call pseudo did not see.
+
+* Several wrappers check a path and then act on it by path in a second
+ step. For example, fchmodat (ports/unix/guts/fchmodat.c) stats the
+ path and later calls the real fchmodat() on the same path, and mknodat
+ (ports/linux/guts/__xmknodat.c) removes the path again by name if the
+ database update fails.
+
+If anything else can change the tree while pseudo is working on it, an
+operation may be applied to a different file than the one that was
+checked, both in pseudo's database and on the real filesystem. Only
+run pseudo on trees that are fully under the control of the build.
+
+
+4. THE CHROOT IS EMULATED
+
+pseudo's chroot() support only prepends a stored prefix to absolute
+paths and strips it from some results (see doc/chroot). No real
+chroot() is performed, and the README notes the support is incomplete.
+Any of the bypasses in section 1, or any path pseudo resolves
+differently from the kernel, reaches the host filesystem. pseudo's
+chroot must not be relied on to keep a program inside a directory.
+
+
+5. METADATA LIVES IN A DATABASE THAT CAN DRIFT
+
+Emulated ownership, modes, device nodes and extended attributes,
+including security.selinux and security.capability, exist only in
+pseudo's database until a packaging or image step copies them out.
+Entries are keyed by path and inode number. When files are changed
+outside of pseudo, for example deleted or recreated without pseudo
+loaded, an inode number can be reused and the database can attach the
+old file's ownership, mode or attributes to a new file. The server
+reports such cases as "inode mismatch" or "path mismatch" (pseudo.c),
+but cannot always know which side is correct.
+
+Accesses to paths listed in PSEUDO_IGNORE_PATHS are deliberately not
+tracked at all.
+
+Generated images should be verified independently if their ownership,
+permissions, labels or capabilities matter for security. Do not
+ignore mismatch errors in pseudo's log.
+
+
+6. THE SERVER TRUSTS ITS CLIENTS
+
+The pseudo server listens on a Unix socket in its local state directory
+and does not authenticate clients beyond the filesystem permissions of
+that socket. Any process running as the build user can connect and
+change the database, and the database files themselves are writable by
+that user. The resulting metadata can only be trusted as much as every
+process running as the build user.
+
+
+RECOMMENDATIONS:
+
+* Run pseudo only on build output that is private to the build and is
+ not modified concurrently by anything outside of it.
+* Run untrusted build steps under a real isolation mechanism (a
+ container, a virtual machine, or a dedicated user), with pseudo inside
+ it if needed, never pseudo alone.
+* Do not store secrets in a pseudo-managed tree on a shared system;
+ they are readable by the build user regardless of their emulated
+ ownership and mode.
+* Verify the ownership, permissions, labels and capabilities of
+ security relevant files in the final image.