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.
Joseph Richey [Mon, 12 Feb 2018 05:22:53 +0000 (21:22 -0800)]
travis: use multiple build stages
This change rewrites .travis.yml to use many build stages/jobs. This
allows our build to run faster, as almost all jobs run in containers.
Stage 1: Run on all pushes to all branches
- Job Build: just runs "make" to make sure everything is OK
Stage 2: Run on all PRs and pushes to master
- Job Lint: Makes sure dep, "make gen", "make format", and "make lint"
are all happy.
- Job Build 1: Make sure "go get" and "make" will work. This job will
later run unit tests.
- Job Build 2: Same as Job Build 1, except with another go version.
- Job Integeration: Run integration tests (needs sudo, so needs VM)
Stage 3: Run on releases (if other stages pass)
- Job Deploy: Publishes amd64 binaries to GitHub.
Joseph Richey [Mon, 12 Feb 2018 05:06:32 +0000 (21:06 -0800)]
Makefile: completly rewrite
This change is a complete rewrite of fscrypt's Makefile.
The new build rules can be roughly divided into secions:
Build - bin/fscrypt and bin/pam_fscrypt
Linting - gen (for .proto files), format, lint
Test - test, test-{setup|teardown}, coverage.out
Install - install, uninstall, install-{bin|pam}
Tools - tools and other bin/* needed for the other rules
As before, "make" builds the binary and pam_module, while "make all"
builds and tests everything (except for integration tests), and "make
clean" removes any generated files.
Also note that this new build system:
- Doesn't require input_fail.py
- Properly falis on linter errors
- Builds everything into the bin/ directory (customizable)
- Builds all the vendored tools
Joseph Richey [Mon, 12 Feb 2018 04:39:12 +0000 (20:39 -0800)]
golint: Use fork that respects vendor directory
Ideally, we would just use "golint ./..." to check all our our source
files for lint error. However, this does not work because it will
include all packages in the vendor directory. The pull request at:
https://github.com/golang/lint/pull/325
fixes this issue, so we will use it until the PR has been merged.
Joseph Richey [Mon, 12 Feb 2018 04:31:27 +0000 (20:31 -0800)]
dep: require tools to be vendored
This change ot Gopkg.toml will make it easier to build the linting and
formatting tools. Vendoring their source also makes sure that updates to
these tools do not break the build.