Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2 encryption policy support #16

Merged
merged 28 commits into from Feb 3, 2021
Merged

Conversation

ebiggers
Copy link
Collaborator

Update fscryptctl to support v2 encryption policies, which are supported by Linux 5.4 and later.

This primarily involved updating the existing get_policy and set_policy commands to support v2 policies, and adding new commands add_key, remove_key, and key_status which manage encryption keys in the new filesystem-level keyring.

This is basically the fscryptctl equivalent of google/fscrypt#148.

I also cleaned up a bunch of other things.

See the individual commits for details.

@ebiggers
Copy link
Collaborator Author

Note, I haven't added tests for the new functionality yet. I'll do that next, but @josephlr please let me know if you have any early comments.

Also FYI, I plan to migrate fscryptctl from Travis CI to GitHub Actions like I'm doing for fscrypt.

@ebiggers ebiggers force-pushed the v2-policy-support branch 3 times, most recently from 3b5e089 to da56824 Compare November 26, 2020 21:28
This is useful for developers using ctags or cscope.
Move the keyutils definitions to a header keyutils.h so that they don't
clutter up fscryptctl.c.

The Makefile doesn't know to pick up new headers as dependencies, so fix
it to do so.
Add a macro ARRAY_SIZE() like the Linux kernel has, so that we don't
have to hard-code array sizes.  Also add two missing static keywords.
It is best practice to always use O_CLOEXEC.
FS_IOC_SET_ENCRYPTION_POLICY will fail with ENOTDIR on a non-directory
anyway, so we don't need to bother with O_DIRECTORY.
Dispatch the commands via a table so that we don't need to hard-code a
series of string comparisons.
Instead of hard-coding the fscrypt UAPI definitions in fscryptctl.c,
just add and use the latest fscrypt.h UAPI header from the Linux kernel.

This provides a lot of new definitions, which later patches will use.
Various FS_* constants have been renamed to FSCRYPT_* in the latest UAPI
header (though there are still compatibility definitions).  Also,
fscrypt_policy is now fscrypt_policy_v1.

Upgrade to using the new names.
Update the list of available encryption modes:

- Add the Adiantum encryption mode (supported since Linux v5.0).

- Remove AES-256-GCM and AES-256-CBC, since the kernel has never
  actually suported those, and their constants have been re-reserved.

- Stop using the special value "FSCRYPT_MODE_INVALID"; it's not needed.
It's not worthwhile bothering to work around
FS_IOC_GET_ENCRYPTION_POLICY returning a different error code for "not
encrypted" in old kernels, since most users of fscryptctl won't care,
and also because the fix has been backported to the 4.9 LTS kernels.
Always recognize the standard "--" argument to indicate "end of
options".  Otherwise fscryptctl can't operate on files that are
literally named "-h", "-v", "--help", or "--version".

Recognize "--" even for commands that don't currently take options, and
moreover, reject unknown options in such commands.  Otherwise the
fscryptctl commands work inconsistently, and later adding options to
commands that don't currently take options would be a breaking change.
get_policy uses the FS_IOC_GET_ENCRYPTION_POLICY ioctl to retrieve the
specified file or directory's encryption policy, then displays it.  But
that only works for v1 encryption policies.  Linux 5.4 added a new ioctl
FS_IOC_GET_ENCRYPTION_POLICY_EX which is more flexible and can retrieve
both v1 and v2 encryption policies.

Make get_policy use the new ioctl if the kernel supports it and display
the resulting the v1 or v2 encryption policy.  Otherwise, fall back to
the old ioctl and display the v1 policy.

Also adjust the output for v1 policies slightly.
Extend the set_policy command to support setting v2 encryption policies,
in addition to v1 encryption policies which it currently supports.  This
uses the same ioctl FS_IOC_SET_ENCRYPTION_POLICY, where the 'version'
field at the beginning of the struct is used to determine whether the
struct is fscrypt_policy_v1 or fscrypt_policy_v2.

The command sets a v2 policy when the user gives the longer key
specification used in such policies (a 32 hex-char master_key_identifier
rather than a 16 hex-char master_key_descriptor).

Also add support for specifying the DIRECT_KEY, IV_INO_LBLK_64, and
IV_INO_LBLK_32 flags in the policy.  These are mostly for v2 policies,
though DIRECT_KEY can technically be used with v1 too.
Split policy_error() into describe_get_policy_error() and
describe_set_policy_error() so that the error codes for
FS_IOC_GET_ENCRYPTION_POLICY{,_EX} and FS_IOC_SET_ENCRYPTION_POLICY
aren't mixed up.

Move the ENOTTY and EOPNOTSUPP handling into a new function
describe_fscrypt_error(), which will be useful for the other fscrypt
ioctls too.
Add a command which wraps the FS_IOC_ADD_ENCRYPTION_KEY ioctl.  This is
the new way to add a fscrypt key to the kernel, and v2 encryption
policies require it.

Although FS_IOC_ADD_ENCRYPTION_KEY also supports adding keys for v1
policies, that works a bit differently from adding keys for v2 policies.
For simplicity, don't support adding v1 policy keys in add_key yet.
People can use the legacy insert_key command if they want to continue to
use fscryptctl with v1 policies.

Also for simplicity, don't support using the fscrypt_add_key_arg::key_id
field yet.  For now the raw key is always required on stdin, like it was
with insert_key.
Add a command which wraps the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl (or
FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS).  This basically undoes the
effect of FS_IOC_ADD_ENCRYPTION_KEY (i.e. 'fscryptctl add_key').  The
exact semantics are a bit complicated, though; see the kernel
documentation for details.
Add a command which wraps the FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl.
This gets the status of an encryption key on a mounted filesystem.
@ebiggers
Copy link
Collaborator Author

I've updated the tests and switched to GitHub Actions now.

I might make a few more updates, but I think this pull request is mostly complete now.

@ebiggers
Copy link
Collaborator Author

ebiggers commented Dec 7, 2020

@josephlr please let me know if you're planning to review this -- thanks!

@ebiggers ebiggers force-pushed the v2-policy-support branch 5 times, most recently from 5c6576a to c3540e6 Compare December 14, 2020 22:19
@ebiggers
Copy link
Collaborator Author

Ping.

@ebiggers ebiggers force-pushed the v2-policy-support branch 2 times, most recently from 5e8a2f9 to 2abb15b Compare February 2, 2021 02:18
Copy link
Member

@josephlr josephlr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks soooooo much for doing all of this. I added some additional changes on top of your patchset. Mostly minor stuff, let me know what you think. It's mostly:

  • Formatting nits
  • Changing how VERSION is handled
  • Fixing tests
  • Making sure -std=c99 works

Merge this PR if you think they all look good (or feel free to change them if you don't).

fscryptctl.c Show resolved Hide resolved
# the License.
#

name: CI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be running on this PR, do we need to merge this PR first? Or do we need to enable Github Actions somewhere else?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it has to be merged first; that's what happened for fscrypt, right? I'm not sure why, though. I was able to test the actions by making a pull request into my own repository (https://github.com/ebiggers/fscryptctl/pull/1/checks), so that worked somehow.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, let's see what happens

Makefile Outdated Show resolved Hide resolved
fscryptctl.c Outdated Show resolved Hide resolved
@ebiggers
Copy link
Collaborator Author

ebiggers commented Feb 2, 2021

Thanks soooooo much for doing all of this. I added some additional changes on top of your patchset. Mostly minor stuff, let me know what you think. It's mostly:

  • Formatting nits
  • Changing how VERSION is handled
  • Fixing tests
  • Making sure -std=c99 works

Merge this PR if you think they all look good (or feel free to change them if you don't).

Thanks for the review! Your changes generally look good, but I left some minor comments.

Also, some of your changes are just fixing up mine. Should I go ahead and fold those in, so we keep a cleaner history?

@josephlr
Copy link
Member

josephlr commented Feb 2, 2021

Should I go ahead and fold those in, so we keep a cleaner history?

Ya, you should fold these in. Having a clearer history is nice.

ebiggers and others added 11 commits February 2, 2021 15:41
The get_descriptor and insert_key commands are only useful for v1
encryption policies, so mark them as deprecated, move them to a
dedicated section of the source file, and update the help output.

(The replacement for insert_key is add_key.  The replacement for
get_descriptor is also add_key, as it doesn't seem that useful to have
the key identifier computation as a separate command.  We could add a
compute_identifier command later if it's needed, though.)
fscryptctl currently only supports 64-byte master keys.  However the
kernel supports shorter master keys if AES-256-XTS isn't being used.
(And there's also a chance that at some point the kernel will be changed
to allow 32-byte master keys even for AES-256-XTS, as 256 bits of
entropy is enough.)

So change fscryptctl to allow variable-length master keys.

While doing this, clean up read_key() to use read() instead of fread()
to avoid a temporary buffer that may not get cleared, improve some
variable names, and change insert_logon_key() to return bool.
Rewrite the README to get it fully up-to-date.

Describe the purpose of fscryptctl more accurately.  Since it's an
advanced tool, generally prefer to refer to the kernel documentation or
fscrypt documentation instead of documenting everything yet again.

Document the new commands, briefly explain the difference between v1 and
v2 encryption policies insofar as fscryptctl is concerned, and update
the examples to show using a v2 policy rather than v1.
Arrange the Makefile targets into sections so that the Makefile is
easier to understand.  Document all the overridable variables at the
top, and add support for PREFIX and BINDIR.

Also clean up a few other things such as removing the unnecessary NAME
variable and the 'default' target.
- Add tests for all new functionality.  That primarily includes v2
  policy support in get_policy and set_policy; the new commands add_key,
  remove_key, and key_status; support for more encryption modes; and
  support for key sizes other than 64 bytes.

- Update to Python 3, as Python 2 is no longer supported by python.org.

- Change 'make test' to no longer require root.  It now just
  accepts an empty directory on a filesystem that supports encryption.
  It no longer requires that it be a mountpoint, and it no longer
  unmount and remounts the filesystem.  (Unmounting was used to test
  fully removing encryption keys for v1 policies, but that wasn't
  testing fscryptctl itself anyway, so having it in the fscryptctl test
  suite didn't add too much.  And it's not needed for v2 policies.)

- Change 'make test-setup' and 'make test-teardown' run sudo themselves,
  like they do in the Makefile for 'fscrypt', so that the user doesn't
  need to explicitly run them under 'sudo'.

- Add 'make test-all' as shorthand for test-setup + test +
  test-teardown.

- Support wrapping the fscryptctl binary with valgrind.

- Bump the kernel requirement to run the tests up to 5.4 or later.
travis-ci.org is being shut down.  Moreover, travis.yml isn't working
with the updated tests, as the tests now require Python 3 and a newer
Linux kernel version.

Switch to GitHub Actions and add a new CI configuration file which
provides much better test coverage.
- Allow VERSION to be defined outside the Makefile
- Just modify CPPFLAGS instead of manually adding to make rule
- Define default VERSION in the source file
- Switch to using v1.2.3 style versions

These changes (especially the last), make it easier to use an
alternative build system (like bazel) while keeping the same behavior.

Signed-off-by: Joe Richey <joerichey@google.com>
These functions generally don't exist on Linux, and even if they did,
we would need to define __STDC_WANT_LIB_EXT1__ to get them.

Signed-off-by: Joe Richey <joerichey@google.com>
This is necessary when compiling against a specific C version, as
O_CLOEXEC was added in a later POSIX standard.

We could technically do:
  #define _POSIX_C_SOURCE 200809L

But defining _GNU_SOURCE seems easier

Signed-off-by: Joe Richey <joerichey@google.com>
We only use C99 for this program, so we should say so and test for it.

Signed-off-by: Joe Richey <joerichey@google.com>
Since not many people are using fscryptctl yet, and v1 encryption
policies have some annoying limitations which people keep running into,
and all major users of filesystem encryption have already switched to v2
by default, it's better to just drop the fscryptctl support for v1
policies and only support v2.  This keeps things much simpler going
forward, and reduces the chance that people misconfigure things.

If anyone really needs to continue to use v1-encrypted directories with
fscryptctl (despite the known limitations with key visibility and so
on), they can just use an older version of fscryptctl.
@ebiggers
Copy link
Collaborator Author

ebiggers commented Feb 3, 2021

@josephlr: I've addressed my comments, folded some of your changes, and moved the commit removing v1 policy support back to the end and added some more information to that commit message. I also opened a test pull request at https://github.com/ebiggers/fscryptctl/pull/2/checks, and all the github actions are passing. Let me know if there's anything else you think needs to be done before I merge this -- thanks!

@josephlr
Copy link
Member

josephlr commented Feb 3, 2021

@ebiggers this looks great!!! Thanks so much for all the work you did on this.

@josephlr josephlr merged commit a8fbe94 into google:master Feb 3, 2021
@josephlr
Copy link
Member

josephlr commented Feb 3, 2021

Looks like the CI is working: https://github.com/google/fscryptctl/actions/runs/535285203

@ebiggers ebiggers deleted the v2-policy-support branch February 3, 2021 22:45
@josephlr josephlr mentioned this pull request Feb 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants