Eric Biggers [Mon, 16 Dec 2019 03:31:39 +0000 (19:31 -0800)]
keyring: support filesystem keyring with v1 encryption policies
Linux v5.4 and later allows fscrypt keys to be added/removed directly
to/from the filesystem via the new ioctls FS_IOC_ADD_ENCRYPTION_KEY and
FS_IOC_REMOVE_ENCRYPTION_KEY. Among other benefits, these fix the key
visibility problems that many users have been running into, where system
services and containers can't access encrypted files.
Allow the user to opt-in to using these new ioctls for their existing
encrypted directories by setting in their /etc/fscrypt.conf:
"use_fs_keyring_for_v1_policies": true
Note that it can't really be on by default, since for v1 policies the
ioctls require root, whereas user keyrings don't. I.e., setting this to
true means that users will need to use 'sudo fscrypt unlock', not
'fscrypt unlock'. v2 policies won't have this restriction.
Eric Biggers [Mon, 16 Dec 2019 03:31:39 +0000 (19:31 -0800)]
Add keyring package
In preparation for introducing support for the new filesystem-level
keyrings, move the existing user keyring management code from
security/keyring.go and crypto/crypto.go into a new package, 'keyring'.
This package provides functions AddEncryptionKey, RemoveEncryptionKey,
and GetEncryptionKeyStatus which delegate to either the filesystem
keyring (added by a later patch) or to the user keyring. This provides
a common interface to both types of keyrings, to the extent possible.
ebiggers [Mon, 16 Dec 2019 03:10:41 +0000 (19:10 -0800)]
keyring: fix permission denied accessing user keyring (#177)
When userKeyringIDLookup() looks up a user keyring, it links it into the
process keyring to ensure that the process retains the "possessor
privileges" over the user keyring, then caches the user keyring's ID.
Unfortunately, this use of the process keyring randomly fails because Go
creates threads before even init() and main() are run, and then can run
code on them later. Since the kernel doesn't create the process keyring
until userspace requests it and the process keyring is actually a
per-thread property that's only inherited by new threads, different
threads in a Go process may see different process keyrings.
Fix this by removing the user keyring cache, switching from the process
keyring to the thread keyring, and using LockOSThread() to pin the
goroutine to an OS thread while needed to perform a keyring operation.
Eric Biggers [Thu, 28 Nov 2019 18:06:32 +0000 (10:06 -0800)]
README.md: update output to match reality
Update the example output in the README to match reality.
Also make a few other updates to the examples to take into account that
'fscrypt purge' now drops caches by default, and that the root
filesystem doesn't need to support encryption if the encrypted
directories are being created on a different filesystem.
Eric Biggers [Thu, 28 Nov 2019 05:00:32 +0000 (21:00 -0800)]
README.md: remove obsolete warning about changing login passphrase
For some time now, fscrypt actually does re-wrap a user's login
protector when their login passphrase changes, provided that the PAM
configuration is correct. Remove the obsolete paragraph.
Eric Biggers [Wed, 27 Nov 2019 19:55:28 +0000 (11:55 -0800)]
cmd/fscrypt: preserve paragraphs in wrapText()
Preserve empty lines rather than squashing them into a single space.
This allows having command descriptions that contain multiple
paragraphs. This also eliminates the need to have a special case for
ordered lists.
Eric Biggers [Wed, 27 Nov 2019 19:40:47 +0000 (11:40 -0800)]
Rename some variables from 'target' to 'targetUser'
Refer to the target User as 'targetUser' rather than simply 'target'.
This will help avoid confusion when we add support for the filesystem
keyring, since then the Mount will also be a "target".
Eric Biggers [Wed, 27 Nov 2019 19:20:19 +0000 (11:20 -0800)]
cmd/fscrypt: adjust message when listing protector sources
Saying "Your data can be protected with one of the following sources" is
ambiguous because it could be interpreted to mean that an encrypted
directory can only have one type of protector. In fact, an encrypted
directory can have multiple protectors, and they can be of any type.
Eric Biggers [Wed, 27 Nov 2019 19:07:38 +0000 (11:07 -0800)]
Allow filesystem links to contain leading/trailing whitespace
To make manually editing linked protectors slightly more user-friendly,
automatically strip any leading or trailing whitespace. E.g. treat
"UUID=3a6d9a76-47f0-4f13-81bf-3332fbe984fb\n" the same as
"UUID=3a6d9a76-47f0-4f13-81bf-3332fbe984fb".
Joseph Richey [Wed, 30 Oct 2019 21:49:40 +0000 (22:49 +0100)]
Merge pull request #154 from ebiggers/bind-mounts
Store fscrypt metadata in only one place per filesystem, so that bind
mounts don't get their own metadata directories (which was ambiguous,
as the same file may be accessible via multiple mounts).
Also correctly set the source device for root filesystems mounted via
the kernel command line, and fix creating linked protectors to such
filesystems.
Eric Biggers [Tue, 29 Oct 2019 07:33:54 +0000 (00:33 -0700)]
filesystem: handle bind mounts properly
Currently, fscrypt treats bind mounts as separate filesystems. This is
broken because fscrypt will look for a directory's encryption policy in
different places depending on which mount it's accessed through. This
forces users to create an fscrypt metadata directory at every bind
mount, and to copy fscrypt metadata around between mounts.
Fix this by storing fscrypt metadata only at the root of the filesystem.
To accomplish this:
- Make mountsByDevice store only a single Mount per filesystem, rather
than multiple. For this Mount, choose a mount of the full filesystem
if available, preferably a read-write mount. If the filesystem has
only bind mounts, store a nil entry in mountsByDevice so we can show a
proper error message later.
- Change FindMount() and GetMount() to look up the Mount by device
number rather than by path, so that they don't return different Mounts
depending on which path is used.
- Change AllFilesystems() to not return bind mounts.
- Due to the above changes, the mountsByPath map is no longer needed
outside of loadMountInfo(). So make it a local variable there.
Eric Biggers [Tue, 29 Oct 2019 07:04:39 +0000 (00:04 -0700)]
filesystem: make link handling more robust
The previous patch fixed making linked protectors to /dev/root, by
setting Mount.Device to the real device node rather than /dev/root.
That's good, but it also hints that the linked protector handling is
unnecessarily fragile, as it relies on the device node name matching
exactly. The Linux kernel allows the same device to have multiple
device nodes, and path comparisons are slow and error-prone in general.
Eric Biggers [Tue, 29 Oct 2019 07:04:39 +0000 (00:04 -0700)]
filesystem: get correct device for kernel-mounted rootfs
A root filesystem mounted via the kernel command line always has a
source of "/dev/root", which isn't a real device node. This makes
fscrypt think this filesystem doesn't have a source device, which breaks
creating login passphrase-protected directories on other filesystems:
fscrypt encrypt: filesystem /: no device for mount "/": system error: cannot create filesystem link
This also makes 'fscrypt status' show a blank source device:
Eric Biggers [Tue, 29 Oct 2019 07:04:39 +0000 (00:04 -0700)]
filesystem: switch to using /proc/self/mountinfo
Change loadMountInfo() to load the mounts directly from
/proc/self/mountinfo, rather than use the mntent.h C library calls.
This is needed for correct handling of bind mounts and of "/dev/root",
since /proc/self/mountinfo has extra fields which show the mounted
subtree and the filesystem's device number. /proc/mounts lacks these
fields, and the C library calls can't provide them.
To start, this patch just switches to using /proc/self/mountinfo,
without doing anything with the extra fields yet.
As a bonus, this eliminates all C code in mountpoint.go.
Joseph Richey [Sat, 26 Oct 2019 00:15:27 +0000 (17:15 -0700)]
Delete vendored code and update CI to Go 1.13 (#158)
As the Go community transitions to using the modules ecosystem,
we want to only support one way of managing dependencies.
So this change moves to only using Go modules for dependency management.
This means that our effective minimum Go version increases to Go 1.11.
To account for this, we also update:
- the documentation
- Makefile
- CI scripts
ebiggers [Thu, 24 Oct 2019 05:18:45 +0000 (22:18 -0700)]
actions/config: ensure config file is created with mode 0644 (#152)
If the user has set a restrictive umask, e.g. 0077, then
/etc/fscrypt.conf would be created without the world-readable bit set.
Fix it by overriding the umask when creating the file.
Eric Biggers [Tue, 1 Oct 2019 16:43:36 +0000 (09:43 -0700)]
filesystem: allow .fscrypt to be a symlink
Support the case where the user has a read-only root filesystem (e.g.
with OSTree) and had previously created a symlink /.fscrypt pointing to
a writable location, so that login protectors can be created there.
cmd/fscrypt: make 'fscrypt setup' create /.fscrypt (#149)
Make the global setup command also create the metadata directory at
/.fscrypt, since that's where login protectors are placed, even when the
actual encrypted directories are on a different filesystem.
Joe Richey [Sun, 20 Jan 2019 03:27:30 +0000 (19:27 -0800)]
Install pam modules/configs to the right location
Per the FHS, manually installed programs should go under /usr/local.
This change also makes it easier to change the global installation
prefix. For example, package managers should set PREFIX=/usr
Eric Biggers [Tue, 15 Jan 2019 02:43:25 +0000 (18:43 -0800)]
Add support for the Adiantum encryption mode
Add Adiantum support to the fscrypt userspace tool. Supported in the
kernel since v5.0-rc1, Adiantum is a length-preserving encryption mode
based primarily on XChaCha12. It is fast even on CPUs without AES
instructions. Unlike XTS it is also a wide-block encryption mode.
Adiantum is supported for both contents and filenames encryption.
For Adiantum encryption policies, also make the fscrypt tool provide the
new DIRECT_KEY flag, which further improves performance by requesting
that all files be encrypted directly with the policy key. This takes
advantage of Adiantum's support for long tweaks.
See the kernel commit "fscrypt: add Adiantum support"
(https://git.kernel.org/torvalds/c/8094c3ceb21ad938) for more details.
Eric Biggers [Tue, 4 Dec 2018 22:31:20 +0000 (14:31 -0800)]
Makefile: use a specific protoc-gen-go version
'make gen' no longer works because it uses the git version of
protoc-gen-go, which is no longer compatible with the latest released
version of github.com/golang/protobuf/proto, which we're using. Freeze
the protoc-gen-go version so that it keeps working.
This change makes sure that, when we set the ruid and euid in order to
get the user keyring linked into the current process keyring, we will
always be able to reverse these changes (using a suid of 0).
This fixes an issue where "su <user>" would result in a system error
when called by an unprivileged user. It also explains exactly how and
why we are making these privilege changes.
This change makes sure after dropping then elevating privileges for a
process, the euid, guid, and groups are all the same as they were
originally. This significantly simplifies the privilege logic.
This fixes CVE-2018-6558, which allowed an unprivleged user to gain
membership in the root group (gid 0) due to the groups not being
properly reset in the process.
Eric Biggers [Sun, 25 Mar 2018 17:13:26 +0000 (10:13 -0700)]
security: drop and regain privileges in all threads
After enabling pam_fscrypt for "session" and creating a directory
protected with a login protector, I was no longer able to log in as that
user. The problem is that the Go runtime is creating threads after
pam_fscrypt drops privileges, but pam_fscrypt is not re-acquiring
privileges on those threads because the Go wrappers for setreuid(),
setregid(), and setgroups() in the "sys/unix" package are using the raw
syscalls which operate on the calling thread only.
This violates glibc's assumption that all threads have the same uids and
gids, causing it to abort() the process when a later module in the PAM
stack (pam_mail in my case) tries to drop privileges using the glibc
functions.
Fix it by dropping and regaining privileges using the glibc functions
rather than the "sys/unix" functions.
This also avoids any possibility that privileges could be changed in a
thread other than the "main" one for pam_fscrypt, since the Go runtime
does not guarantee which OS-level thread runs what.
It would be nice to also exit all Go worker threads before returning
from pam_fscrypt, but the Go runtime doesn't seem to support that.
Eric Biggers [Sun, 25 Mar 2018 06:21:29 +0000 (23:21 -0700)]
pam: return error when PAM info item is unset
pam_fscrypt is crashing with a segfault in copyIntoSecret() when using
Ctrl-C to interrupt a 'sudo' prompt. It is dereferencing a NULL pointer
that is supposed point to the PAM_AUTHTOK item. The problem is that the
Go code assumes pam_get_item() returns a non-success status if the item
is unset, when actually it sets the data pointer to NULL and returns
PAM_SUCCESS.
Fix it by making pam.Handle.GetItem() return an error in that case.