From ea4a583187c4820f0e4f37334561ffdaf36df9fb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 17 Mar 2020 21:10:58 -0700 Subject: [PATCH 1/6] Simplify choosing the key description prefix There's no real need to allow users to choose the key description prefix (a.k.a. the "service"), since on ext4 and f2fs we can just use "ext4" and "f2fs" for compatibility with all kernels both old and new, and on other filesystems we can just use "fscrypt". So, let's do that. Since this removes the point of the "--legacy" option to 'fscrypt setup' and the "compatibility" field in /etc/fscrypt.conf, remove those too. Specifically, we start ignoring the "compatibility" in existing config files and not writing it to new ones. The corresponding protobuf field number and name are reserved. We stop accepting the "--legacy" option at all, although since it was default true and there was no real reason for anyone to change it to false, probably no one will notice. If anyone does, they should just stop specifying the option. Note that this change only affects user keyrings and thus only affects v1 encryption policies, which are deprecated in favor of v2 anyway. --- README.md | 4 -- actions/config.go | 17 +----- actions/config_test.go | 2 +- actions/context.go | 19 ------ actions/context_test.go | 2 +- cmd/fscrypt/commands.go | 2 +- cmd/fscrypt/flags.go | 8 +-- cmd/fscrypt/format.go | 7 +-- cmd/fscrypt/setup.go | 2 +- keyring/keyring.go | 24 ++++++-- keyring/keyring_test.go | 26 +-------- metadata/config.go | 14 ----- metadata/config_test.go | 8 +-- metadata/metadata.pb.go | 124 +++++++++++++++++++--------------------- metadata/metadata.proto | 5 +- 15 files changed, 97 insertions(+), 167 deletions(-) diff --git a/README.md b/README.md index 7a0b9da3..944493e1 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,6 @@ that looks like the following: "memory": "131072", "parallelism": "32" }, - "compatibility": "legacy", "options": { "padding": "32", "contents": "AES_256_XTS", @@ -216,9 +215,6 @@ The fields are: and take about 1 second. The `--time` option to `fscrypt setup` can be used to customize this time when creating the configuration file. -* "compatibility" can be "legacy" to support kernels older than v4.8, - or the empty string to only support kernels v4.8 and later. - * "options" are the encryption options to use for new encrypted directories: diff --git a/actions/config.go b/actions/config.go index 6b019df2..3433438f 100644 --- a/actions/config.go +++ b/actions/config.go @@ -36,10 +36,6 @@ import ( "github.com/google/fscrypt/util" ) -// LegacyConfig indicates that keys should be inserted into the keyring with the -// legacy service prefixes. Needed for kernels before v4.8. -const LegacyConfig = "legacy" - // ConfigFileLocation is the location of fscrypt's global settings. This can be // overridden by the user of this package. var ConfigFileLocation = "/etc/fscrypt.conf" @@ -61,12 +57,9 @@ var ( ) // CreateConfigFile creates a new config file at the appropriate location with -// the appropriate hashing costs and encryption parameters. This creation is -// configurable in two ways. First, a time target must be specified. This target -// will determine the hashing costs, by picking parameters that make the hashing -// take as long as the specified target. Second, the config can include the -// legacy option, which is needed for systems with kernels older than v4.8. -func CreateConfigFile(target time.Duration, useLegacy bool) error { +// the appropriate hashing costs and encryption parameters. The hashing will be +// configured to take as long as the specified time target. +func CreateConfigFile(target time.Duration) error { // Create the config file before computing the hashing costs, so we fail // immediately if the program has insufficient permissions. configFile, err := filesystem.OpenFileOverridingUmask(ConfigFileLocation, @@ -83,10 +76,6 @@ func CreateConfigFile(target time.Duration, useLegacy bool) error { Source: metadata.DefaultSource, Options: metadata.DefaultOptions, } - if useLegacy { - config.Compatibility = LegacyConfig - log.Printf("Using %q compatibility option\n", LegacyConfig) - } if config.HashCosts, err = getHashingCosts(target); err != nil { return err diff --git a/actions/config_test.go b/actions/config_test.go index 037e4330..02c89e6b 100644 --- a/actions/config_test.go +++ b/actions/config_test.go @@ -42,7 +42,7 @@ func TestConfigFileIsCreatedWithCorrectMode(t *testing.T) { defer os.RemoveAll(tempDir) ConfigFileLocation = filepath.Join(tempDir, "test.conf") - if err = CreateConfigFile(time.Millisecond, false); err != nil { + if err = CreateConfigFile(time.Millisecond); err != nil { t.Fatal(err) } fileInfo, err := os.Stat(ConfigFileLocation) diff --git a/actions/context.go b/actions/context.go index f07f2256..0db06711 100644 --- a/actions/context.go +++ b/actions/context.go @@ -32,8 +32,6 @@ import ( "log" "os/user" - "golang.org/x/sys/unix" - "github.com/pkg/errors" "github.com/google/fscrypt/filesystem" @@ -133,27 +131,10 @@ func (ctx *Context) checkContext() error { return ctx.Mount.CheckSetup() } -// getService returns the keyring service for this context. We use the presence -// of the LegacyConfig flag to determine if we should use the legacy services. -// For ext4 systems before v4.8 and f2fs systems before v4.6, filesystem -// specific services must be used (these legacy services will still work with -// later kernels). -func (ctx *Context) getService() string { - // For legacy configurations, we may need non-standard services - if ctx.Config.HasCompatibilityOption(LegacyConfig) { - switch ctx.Mount.FilesystemType { - case "ext4", "f2fs": - return ctx.Mount.FilesystemType + ":" - } - } - return unix.FSCRYPT_KEY_DESC_PREFIX -} - func (ctx *Context) getKeyringOptions() *keyring.Options { return &keyring.Options{ Mount: ctx.Mount, User: ctx.TargetUser, - Service: ctx.getService(), UseFsKeyringForV1Policies: ctx.Config.GetUseFsKeyringForV1Policies(), } } diff --git a/actions/context_test.go b/actions/context_test.go index e8aefd78..4f93776c 100644 --- a/actions/context_test.go +++ b/actions/context_test.go @@ -52,7 +52,7 @@ func setupContext() (ctx *Context, err error) { return nil, fmt.Errorf("created context at %q without config file", badCtx.Mount.Path) } - if err = CreateConfigFile(testTime, true); err != nil { + if err = CreateConfigFile(testTime); err != nil { return nil, err } defer func() { diff --git a/cmd/fscrypt/commands.go b/cmd/fscrypt/commands.go index 4a59d302..f84102e9 100644 --- a/cmd/fscrypt/commands.go +++ b/cmd/fscrypt/commands.go @@ -62,7 +62,7 @@ var Setup = cli.Command{ the README). This may require root privileges.`, mountpointArg, actions.ConfigFileLocation, shortDisplay(timeTargetFlag)), - Flags: []cli.Flag{timeTargetFlag, legacyFlag, forceFlag}, + Flags: []cli.Flag{timeTargetFlag, forceFlag}, Action: setupAction, } diff --git a/cmd/fscrypt/flags.go b/cmd/fscrypt/flags.go index ce2f30ed..9679a8dc 100644 --- a/cmd/fscrypt/flags.go +++ b/cmd/fscrypt/flags.go @@ -114,7 +114,7 @@ var ( // UPDATE THIS ARRAY WHEN ADDING NEW FLAGS!!! // TODO(joerichey) add presubmit rule to enforce this allFlags = []prettyFlag{helpFlag, versionFlag, verboseFlag, quietFlag, - forceFlag, legacyFlag, skipUnlockFlag, timeTargetFlag, + forceFlag, skipUnlockFlag, timeTargetFlag, sourceFlag, nameFlag, keyFileFlag, protectorFlag, unlockWithFlag, policyFlag, allUsersFlag, noRecoveryFlag} // universalFlags contains flags that should be on every command @@ -148,12 +148,6 @@ var ( WARNING: This bypasses confirmations for protective operations, use with care.`), } - legacyFlag = &boolFlag{ - Name: "legacy", - Usage: `Allow for support of older kernels with ext4 (before - v4.8) and F2FS (before v4.6) filesystems.`, - Default: true, - } skipUnlockFlag = &boolFlag{ Name: "skip-unlock", Usage: `Leave the directory in a locked state after setup. diff --git a/cmd/fscrypt/format.go b/cmd/fscrypt/format.go index 48a5a86f..cc268aa9 100644 --- a/cmd/fscrypt/format.go +++ b/cmd/fscrypt/format.go @@ -98,11 +98,10 @@ func shortDisplay(f prettyFlag) string { // // --help Prints help screen for commands and subcommands. // -// If a default is specified, this if appended to the usage. Example: +// If a default is specified, then it is appended to the usage. Example: // -// --legacy Allow for support of older kernels with ext4 -// (before v4.8) and F2FS (before v4.6) filesystems. -// (default: true) +// --time=TIME Calibrate passphrase hashing to take the +// specified amount of TIME (default: 1s) // func longDisplay(f prettyFlag, defaultString ...string) string { usage := f.GetUsage() diff --git a/cmd/fscrypt/setup.go b/cmd/fscrypt/setup.go index 69787bb8..328788af 100644 --- a/cmd/fscrypt/setup.go +++ b/cmd/fscrypt/setup.go @@ -51,7 +51,7 @@ func createGlobalConfig(w io.Writer, path string) error { } fmt.Fprintln(w, "Customizing passphrase hashing difficulty for this system...") - err = actions.CreateConfigFile(timeTargetFlag.Value, legacyFlag.Value) + err = actions.CreateConfigFile(timeTargetFlag.Value) if err != nil { return err } diff --git a/keyring/keyring.go b/keyring/keyring.go index 5a751535..20bc48bf 100644 --- a/keyring/keyring.go +++ b/keyring/keyring.go @@ -62,9 +62,6 @@ type Options struct { Mount *filesystem.Mount // User is the user for whom the key should be added/removed/gotten. User *user.User - // Service is the prefix to prepend to the description of the keys in - // user keyrings. Not relevant for filesystem keyrings. - Service string // UseFsKeyringForV1Policies is true if keys for v1 encryption policies // should be put in the filesystem's keyring (if supported) rather than // in the user's keyring. Note that this makes AddEncryptionKey and @@ -84,6 +81,21 @@ func shouldUseFsKeyring(descriptor string, options *Options) bool { return true } +// buildKeyDescription builds the description for an fscrypt key of type +// "logon". For ext4 and f2fs, it uses the legacy filesystem-specific prefixes +// for compatibility with kernels before v4.8 and v4.6 respectively. For other +// filesystems it uses the generic prefix "fscrypt". +func buildKeyDescription(options *Options, descriptor string) string { + var prefix string + switch options.Mount.FilesystemType { + case "ext4", "f2fs": + prefix = options.Mount.FilesystemType + default: + prefix = unix.FSCRYPT_KEY_DESC_PREFIX + } + return prefix + ":" + descriptor +} + // AddEncryptionKey adds an encryption policy key to a kernel keyring. It uses // either the filesystem keyring for the target Mount or the user keyring for // the target User. @@ -94,7 +106,7 @@ func AddEncryptionKey(key *crypto.Key, descriptor string, options *Options) erro if shouldUseFsKeyring(descriptor, options) { return fsAddEncryptionKey(key, descriptor, options.Mount, options.User) } - return userAddKey(key, options.Service+descriptor, options.User) + return userAddKey(key, buildKeyDescription(options, descriptor), options.User) } // RemoveEncryptionKey removes an encryption policy key from a kernel keyring. @@ -108,7 +120,7 @@ func RemoveEncryptionKey(descriptor string, options *Options, allUsers bool) err } return fsRemoveEncryptionKey(descriptor, options.Mount, user) } - return userRemoveKey(options.Service+descriptor, options.User) + return userRemoveKey(buildKeyDescription(options, descriptor), options.User) } // KeyStatus is an enum that represents the status of a key in a kernel keyring. @@ -147,7 +159,7 @@ func GetEncryptionKeyStatus(descriptor string, options *Options) (KeyStatus, err if shouldUseFsKeyring(descriptor, options) { return fsGetEncryptionKeyStatus(descriptor, options.Mount, options.User) } - _, err := userFindKey(options.Service+descriptor, options.User) + _, err := userFindKey(buildKeyDescription(options, descriptor), options.User) if err != nil { return KeyAbsent, nil } diff --git a/keyring/keyring_test.go b/keyring/keyring_test.go index 8912556d..22081056 100644 --- a/keyring/keyring_test.go +++ b/keyring/keyring_test.go @@ -23,8 +23,6 @@ import ( "strconv" "testing" - "golang.org/x/sys/unix" - "github.com/google/fscrypt/crypto" "github.com/google/fscrypt/filesystem" "github.com/google/fscrypt/metadata" @@ -47,7 +45,6 @@ func makeKey(b byte, n int) (*crypto.Key, error) { } var ( - defaultService = unix.FSCRYPT_KEY_DESC_PREFIX testUser, _ = util.EffectiveUser() fakeValidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen) fakeInvalidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen-1) @@ -166,28 +163,11 @@ func testAddAndRemoveKey(t *testing.T, descriptor string, options *Options) { assertKeyStatus(t, descriptor, options, KeyAbsent) } -func TestUserKeyringDefaultService(t *testing.T) { - options := &Options{ - User: testUser, - Service: defaultService, - UseFsKeyringForV1Policies: false, - } - testAddAndRemoveKey(t, fakeV1Descriptor, options) -} - -func TestUserKeyringExt4Service(t *testing.T) { - options := &Options{ - User: testUser, - Service: "ext4:", - UseFsKeyringForV1Policies: false, - } - testAddAndRemoveKey(t, fakeV1Descriptor, options) -} - -func TestUserKeyringF2fsService(t *testing.T) { +func TestUserKeyring(t *testing.T) { + mount := getTestMount(t) options := &Options{ + Mount: mount, User: testUser, - Service: "f2fs:", UseFsKeyringForV1Policies: false, } testAddAndRemoveKey(t, fakeV1Descriptor, options) diff --git a/metadata/config.go b/metadata/config.go index 0f95fbe0..b3c87269 100644 --- a/metadata/config.go +++ b/metadata/config.go @@ -28,7 +28,6 @@ package metadata import ( "io" - "strings" "github.com/golang/protobuf/jsonpb" ) @@ -58,16 +57,3 @@ func ReadConfig(in io.Reader) (*Config, error) { } return config, u.Unmarshal(in, config) } - -// HasCompatibilityOption returns true if the specified string is in the list of -// compatibility options. This assumes the compatibility options are in a comma -// separated string. -func (c *Config) HasCompatibilityOption(option string) bool { - options := strings.Split(c.Compatibility, ",") - for _, o := range options { - if o == option { - return true - } - } - return false -} diff --git a/metadata/config_test.go b/metadata/config_test.go index 83c1eb01..52f83f2b 100644 --- a/metadata/config_test.go +++ b/metadata/config_test.go @@ -33,8 +33,7 @@ var testConfig = &Config{ Memory: 1 << 12, Parallelism: 8, }, - Compatibility: "", - Options: DefaultOptions, + Options: DefaultOptions, } var testConfigString = `{ @@ -44,7 +43,6 @@ var testConfigString = `{ "memory": "4096", "parallelism": "8" }, - "compatibility": "", "options": { "padding": "32", "contents": "AES_256_XTS", @@ -81,7 +79,7 @@ func TestRead(t *testing.T) { } // Makes sure we can parse a legacy config file that doesn't have the fields -// that were added later. +// that were added later and that has the removed "compatibility" field. func TestOptionalFields(t *testing.T) { contents := `{ "source": "custom_passphrase", @@ -90,7 +88,7 @@ func TestOptionalFields(t *testing.T) { "memory": "4096", "parallelism": "8" }, - "compatibility": "", + "compatibility": "legacy", "options": { "padding": "32", "contents": "AES_256_XTS", diff --git a/metadata/metadata.pb.go b/metadata/metadata.pb.go index e6067f9b..a2148cef 100644 --- a/metadata/metadata.pb.go +++ b/metadata/metadata.pb.go @@ -45,7 +45,7 @@ func (x SourceType) String() string { return proto.EnumName(SourceType_name, int32(x)) } func (SourceType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{0} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{0} } // Type of encryption; should match declarations of unix.FSCRYPT_MODE @@ -87,7 +87,7 @@ func (x EncryptionOptions_Mode) String() string { return proto.EnumName(EncryptionOptions_Mode_name, int32(x)) } func (EncryptionOptions_Mode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{3, 0} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{3, 0} } // Cost parameters to be used in our hashing functions. @@ -104,7 +104,7 @@ func (m *HashingCosts) Reset() { *m = HashingCosts{} } func (m *HashingCosts) String() string { return proto.CompactTextString(m) } func (*HashingCosts) ProtoMessage() {} func (*HashingCosts) Descriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{0} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{0} } func (m *HashingCosts) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_HashingCosts.Unmarshal(m, b) @@ -159,7 +159,7 @@ func (m *WrappedKeyData) Reset() { *m = WrappedKeyData{} } func (m *WrappedKeyData) String() string { return proto.CompactTextString(m) } func (*WrappedKeyData) ProtoMessage() {} func (*WrappedKeyData) Descriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{1} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{1} } func (m *WrappedKeyData) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_WrappedKeyData.Unmarshal(m, b) @@ -219,7 +219,7 @@ func (m *ProtectorData) Reset() { *m = ProtectorData{} } func (m *ProtectorData) String() string { return proto.CompactTextString(m) } func (*ProtectorData) ProtoMessage() {} func (*ProtectorData) Descriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{2} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{2} } func (m *ProtectorData) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ProtectorData.Unmarshal(m, b) @@ -303,7 +303,7 @@ func (m *EncryptionOptions) Reset() { *m = EncryptionOptions{} } func (m *EncryptionOptions) String() string { return proto.CompactTextString(m) } func (*EncryptionOptions) ProtoMessage() {} func (*EncryptionOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{3} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{3} } func (m *EncryptionOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EncryptionOptions.Unmarshal(m, b) @@ -363,7 +363,7 @@ func (m *WrappedPolicyKey) Reset() { *m = WrappedPolicyKey{} } func (m *WrappedPolicyKey) String() string { return proto.CompactTextString(m) } func (*WrappedPolicyKey) ProtoMessage() {} func (*WrappedPolicyKey) Descriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{4} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{4} } func (m *WrappedPolicyKey) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_WrappedPolicyKey.Unmarshal(m, b) @@ -411,7 +411,7 @@ func (m *PolicyData) Reset() { *m = PolicyData{} } func (m *PolicyData) String() string { return proto.CompactTextString(m) } func (*PolicyData) ProtoMessage() {} func (*PolicyData) Descriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{5} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{5} } func (m *PolicyData) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PolicyData.Unmarshal(m, b) @@ -456,7 +456,6 @@ func (m *PolicyData) GetWrappedPolicyKeys() []*WrappedPolicyKey { type Config struct { Source SourceType `protobuf:"varint,1,opt,name=source,proto3,enum=metadata.SourceType" json:"source,omitempty"` HashCosts *HashingCosts `protobuf:"bytes,2,opt,name=hash_costs,json=hashCosts,proto3" json:"hash_costs,omitempty"` - Compatibility string `protobuf:"bytes,3,opt,name=compatibility,proto3" json:"compatibility,omitempty"` Options *EncryptionOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"` UseFsKeyringForV1Policies bool `protobuf:"varint,5,opt,name=use_fs_keyring_for_v1_policies,json=useFsKeyringForV1Policies,proto3" json:"use_fs_keyring_for_v1_policies,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -468,7 +467,7 @@ func (m *Config) Reset() { *m = Config{} } func (m *Config) String() string { return proto.CompactTextString(m) } func (*Config) ProtoMessage() {} func (*Config) Descriptor() ([]byte, []int) { - return fileDescriptor_metadata_0a34c99c54153da9, []int{6} + return fileDescriptor_metadata_20fa0d9b7a38c428, []int{6} } func (m *Config) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Config.Unmarshal(m, b) @@ -502,13 +501,6 @@ func (m *Config) GetHashCosts() *HashingCosts { return nil } -func (m *Config) GetCompatibility() string { - if m != nil { - return m.Compatibility - } - return "" -} - func (m *Config) GetOptions() *EncryptionOptions { if m != nil { return m.Options @@ -535,53 +527,53 @@ func init() { proto.RegisterEnum("metadata.EncryptionOptions_Mode", EncryptionOptions_Mode_name, EncryptionOptions_Mode_value) } -func init() { proto.RegisterFile("metadata/metadata.proto", fileDescriptor_metadata_0a34c99c54153da9) } - -var fileDescriptor_metadata_0a34c99c54153da9 = []byte{ - // 717 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x5d, 0x6b, 0x13, 0x4d, - 0x14, 0xc7, 0x9f, 0xdd, 0xa4, 0x79, 0x39, 0x79, 0x79, 0xb6, 0xd3, 0x3e, 0x7d, 0x56, 0x05, 0x09, - 0xd1, 0x42, 0x91, 0x52, 0x49, 0xa4, 0xa2, 0x20, 0x42, 0x4d, 0x5b, 0xad, 0xa5, 0x58, 0x37, 0x21, - 0x2a, 0x08, 0xcb, 0x74, 0x77, 0x92, 0x0c, 0xd9, 0xdd, 0x59, 0x66, 0x26, 0x0d, 0x7b, 0xe7, 0x9d, - 0x57, 0x5e, 0xf9, 0x5d, 0xfc, 0x34, 0x7e, 0x18, 0x99, 0xd9, 0xcd, 0x5b, 0x0b, 0xa5, 0xf5, 0x66, - 0x39, 0xf3, 0x9f, 0x33, 0xe7, 0x9c, 0xf9, 0x9d, 0x39, 0x0b, 0xff, 0x87, 0x44, 0x62, 0x1f, 0x4b, - 0xfc, 0x74, 0x66, 0xec, 0xc5, 0x9c, 0x49, 0x86, 0x4a, 0xb3, 0x75, 0xf3, 0x2b, 0x54, 0xdf, 0x61, - 0x31, 0xa2, 0xd1, 0xb0, 0xc3, 0x84, 0x14, 0x08, 0x41, 0x5e, 0xd2, 0x90, 0xd8, 0x66, 0xc3, 0xd8, - 0xc9, 0x39, 0xda, 0x46, 0x5b, 0x50, 0x08, 0x49, 0xc8, 0x78, 0x62, 0xe7, 0xb4, 0x9a, 0xad, 0x50, - 0x03, 0x2a, 0x31, 0xe6, 0x38, 0x08, 0x48, 0x40, 0x45, 0x68, 0xe7, 0xf5, 0xe6, 0xb2, 0xd4, 0xfc, - 0x02, 0xf5, 0x4f, 0x1c, 0xc7, 0x31, 0xf1, 0x4f, 0x49, 0x72, 0x88, 0x25, 0x46, 0x75, 0x30, 0x4f, - 0xfa, 0xb6, 0xd1, 0x30, 0x76, 0xaa, 0x8e, 0x79, 0xd2, 0x47, 0x8f, 0xa0, 0x46, 0x22, 0x8f, 0x27, - 0xb1, 0x24, 0xbe, 0x3b, 0x26, 0x89, 0x4e, 0x5c, 0x75, 0xaa, 0x73, 0xf1, 0x94, 0x24, 0xaa, 0xa8, - 0x51, 0x88, 0x3d, 0x9d, 0xbe, 0xea, 0x68, 0xbb, 0xf9, 0xd3, 0x84, 0xda, 0x39, 0x67, 0x92, 0x78, - 0x92, 0x71, 0x1d, 0xba, 0x05, 0x9b, 0xf1, 0x4c, 0x70, 0x7d, 0x22, 0x3c, 0x4e, 0x63, 0xc9, 0xb8, - 0x4e, 0x56, 0x76, 0x36, 0xe6, 0x7b, 0x87, 0xf3, 0x2d, 0xb4, 0x0b, 0x05, 0xc1, 0x26, 0xdc, 0x4b, - 0xef, 0x5b, 0x6f, 0x6f, 0xee, 0xcd, 0x41, 0x75, 0xb5, 0xde, 0x4b, 0x62, 0xe2, 0x64, 0x3e, 0xaa, - 0x8c, 0x08, 0x87, 0x44, 0x97, 0x51, 0x76, 0xb4, 0x8d, 0x76, 0x61, 0xcd, 0x53, 0xe0, 0xf4, 0xed, - 0x2b, 0xed, 0xad, 0x45, 0x80, 0x65, 0xac, 0x4e, 0xea, 0xa4, 0x22, 0x08, 0x1c, 0x48, 0x7b, 0x2d, - 0xbd, 0x88, 0xb2, 0x91, 0x05, 0xb9, 0x09, 0xf5, 0xed, 0x82, 0xa6, 0xa7, 0x4c, 0xf4, 0x12, 0x2a, - 0xd3, 0x94, 0x9a, 0x26, 0x52, 0xd4, 0x91, 0xed, 0x45, 0xe4, 0x55, 0xa4, 0x0e, 0x4c, 0xe7, 0xeb, - 0xe6, 0x6f, 0x13, 0xd6, 0x8f, 0x52, 0x74, 0x94, 0x45, 0x1f, 0xf4, 0x57, 0x20, 0x1b, 0x8a, 0x31, - 0xf6, 0x7d, 0x1a, 0x0d, 0x35, 0x8c, 0x9c, 0x33, 0x5b, 0xa2, 0x57, 0x50, 0xf2, 0x58, 0x24, 0x49, - 0x24, 0x45, 0x86, 0xa0, 0xb1, 0xc8, 0x73, 0x2d, 0xd0, 0xde, 0x19, 0xf3, 0x89, 0x33, 0x3f, 0x81, - 0x5e, 0x43, 0x79, 0x40, 0x03, 0xa2, 0x40, 0x08, 0x4d, 0xe5, 0x36, 0xc7, 0x17, 0x47, 0xd0, 0x36, - 0xd4, 0x63, 0x16, 0x50, 0x2f, 0x71, 0x2f, 0x09, 0x17, 0x94, 0x45, 0xd9, 0x1b, 0xaa, 0xa5, 0x6a, - 0x3f, 0x15, 0x9b, 0xdf, 0x0d, 0xc8, 0xab, 0xa3, 0xa8, 0x02, 0x45, 0x9f, 0x0c, 0xf0, 0x24, 0x90, - 0xd6, 0x3f, 0xe8, 0x5f, 0xa8, 0x1c, 0x1c, 0x75, 0xdd, 0xf6, 0xfe, 0x73, 0xf7, 0x73, 0xaf, 0x6b, - 0x19, 0xcb, 0xc2, 0xdb, 0xce, 0x99, 0x65, 0x2e, 0x0b, 0x9d, 0x37, 0x1d, 0x2b, 0xb7, 0x22, 0xf4, - 0xba, 0x56, 0x7e, 0x26, 0xb4, 0xda, 0x2f, 0xb4, 0xc7, 0xda, 0x8a, 0xd0, 0xeb, 0x5a, 0x05, 0x54, - 0x85, 0xd2, 0x81, 0x4f, 0x71, 0x24, 0x27, 0xa1, 0x55, 0x6e, 0x7e, 0x33, 0xc0, 0xca, 0xe8, 0x9f, - 0xeb, 0x12, 0xd5, 0xeb, 0xfc, 0x8b, 0x77, 0x77, 0xa5, 0xc3, 0xe6, 0x1d, 0x3a, 0xfc, 0xcb, 0x00, - 0x48, 0x73, 0xeb, 0x47, 0xbf, 0x0d, 0xf5, 0x31, 0x49, 0xae, 0xa7, 0xad, 0x8d, 0x49, 0xb2, 0x94, - 0x70, 0x1f, 0x8a, 0x2c, 0x6d, 0x42, 0x96, 0xec, 0xc1, 0x0d, 0x7d, 0x72, 0x66, 0xbe, 0xe8, 0x3d, - 0x6c, 0xcc, 0xea, 0xcc, 0x1a, 0x35, 0x26, 0x89, 0x6a, 0x75, 0x6e, 0xa7, 0xd2, 0xbe, 0x7f, 0xad, - 0xde, 0x39, 0x13, 0x67, 0x7d, 0x7a, 0x45, 0x11, 0xcd, 0x1f, 0x26, 0x14, 0x3a, 0x2c, 0x1a, 0xd0, - 0xe1, 0xd2, 0xd8, 0x19, 0xb7, 0x18, 0xbb, 0x7d, 0x80, 0x11, 0x16, 0x23, 0x37, 0x9d, 0x33, 0xf3, - 0xc6, 0x39, 0x2b, 0x2b, 0xcf, 0xf4, 0x4f, 0xf6, 0x18, 0x6a, 0x1e, 0x0b, 0x63, 0x2c, 0xe9, 0x05, - 0x0d, 0xa8, 0x4c, 0xb2, 0xb1, 0x5d, 0x15, 0x97, 0xc1, 0xe4, 0xef, 0x00, 0xe6, 0x00, 0x1e, 0x4e, - 0x04, 0x71, 0x07, 0x42, 0x01, 0xe1, 0x34, 0x1a, 0xba, 0x03, 0xc6, 0xdd, 0xcb, 0x56, 0x8a, 0x89, - 0x12, 0xa1, 0x47, 0xbc, 0xe4, 0xdc, 0x9b, 0x08, 0x72, 0x2c, 0x4e, 0x53, 0x9f, 0x63, 0xc6, 0xfb, - 0xad, 0xf3, 0xcc, 0xe1, 0xc9, 0x47, 0x80, 0xc5, 0x65, 0x57, 0x9f, 0x36, 0x82, 0x7a, 0x8c, 0x43, - 0x37, 0xc6, 0x42, 0xc4, 0x23, 0x8e, 0x05, 0xb1, 0x0c, 0xf4, 0x1f, 0xac, 0x7b, 0x13, 0x21, 0xd9, - 0x8a, 0x6c, 0xaa, 0x73, 0x1c, 0x4f, 0x55, 0x15, 0x56, 0xee, 0xa2, 0xa0, 0xff, 0xee, 0xcf, 0xfe, - 0x04, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x97, 0x5e, 0xdf, 0xf8, 0x05, 0x00, 0x00, +func init() { proto.RegisterFile("metadata/metadata.proto", fileDescriptor_metadata_20fa0d9b7a38c428) } + +var fileDescriptor_metadata_20fa0d9b7a38c428 = []byte{ + // 716 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xdd, 0x6a, 0xdb, 0x48, + 0x14, 0x5e, 0x49, 0x8e, 0x7f, 0x8e, 0x7f, 0x56, 0x99, 0x64, 0xb3, 0xda, 0x5d, 0x58, 0x8c, 0x97, + 0x40, 0x58, 0x42, 0x16, 0x7b, 0x49, 0x69, 0xa1, 0x14, 0x52, 0x27, 0x69, 0x93, 0x10, 0x9a, 0x8e, + 0x8d, 0xdb, 0x42, 0x41, 0x4c, 0xa4, 0xb1, 0x3d, 0x58, 0xd2, 0x88, 0x99, 0x71, 0x8c, 0xee, 0x7a, + 0xd7, 0x07, 0xe8, 0xbb, 0xf4, 0x69, 0xfa, 0x28, 0xbd, 0x28, 0x1a, 0xc9, 0x7f, 0x09, 0x84, 0xa4, + 0x37, 0xe2, 0x9c, 0x6f, 0xce, 0xef, 0x77, 0xce, 0x11, 0xfc, 0x1e, 0x52, 0x45, 0x7c, 0xa2, 0xc8, + 0x7f, 0x73, 0xe1, 0x20, 0x16, 0x5c, 0x71, 0x54, 0x9e, 0xeb, 0xad, 0x8f, 0x50, 0x7b, 0x4d, 0xe4, + 0x98, 0x45, 0xa3, 0x2e, 0x97, 0x4a, 0x22, 0x04, 0x05, 0xc5, 0x42, 0xea, 0x98, 0x4d, 0x63, 0xcf, + 0xc2, 0x5a, 0x46, 0x3b, 0x50, 0x0c, 0x69, 0xc8, 0x45, 0xe2, 0x58, 0x1a, 0xcd, 0x35, 0xd4, 0x84, + 0x6a, 0x4c, 0x04, 0x09, 0x02, 0x1a, 0x30, 0x19, 0x3a, 0x05, 0xfd, 0xb8, 0x0a, 0xb5, 0x3e, 0x40, + 0xe3, 0x9d, 0x20, 0x71, 0x4c, 0xfd, 0x0b, 0x9a, 0x1c, 0x13, 0x45, 0x50, 0x03, 0xcc, 0xb3, 0x81, + 0x63, 0x34, 0x8d, 0xbd, 0x1a, 0x36, 0xcf, 0x06, 0xe8, 0x1f, 0xa8, 0xd3, 0xc8, 0x13, 0x49, 0xac, + 0xa8, 0xef, 0x4e, 0x68, 0xa2, 0x13, 0xd7, 0x70, 0x6d, 0x01, 0x5e, 0xd0, 0x24, 0x2d, 0x6a, 0x1c, + 0x12, 0x4f, 0xa7, 0xaf, 0x61, 0x2d, 0xb7, 0xbe, 0x98, 0x50, 0xbf, 0x12, 0x5c, 0x51, 0x4f, 0x71, + 0xa1, 0x43, 0xb7, 0x61, 0x3b, 0x9e, 0x03, 0xae, 0x4f, 0xa5, 0x27, 0x58, 0xac, 0xb8, 0xd0, 0xc9, + 0x2a, 0x78, 0x6b, 0xf1, 0x76, 0xbc, 0x78, 0x42, 0xfb, 0x50, 0x94, 0x7c, 0x2a, 0xbc, 0xac, 0xdf, + 0x46, 0x67, 0xfb, 0x60, 0x41, 0x54, 0x4f, 0xe3, 0xfd, 0x24, 0xa6, 0x38, 0xb7, 0x49, 0xcb, 0x88, + 0x48, 0x48, 0x75, 0x19, 0x15, 0xac, 0x65, 0xb4, 0x0f, 0x1b, 0x5e, 0x4a, 0x9c, 0xee, 0xbe, 0xda, + 0xd9, 0x59, 0x06, 0x58, 0xa5, 0x15, 0x67, 0x46, 0x69, 0x04, 0x49, 0x02, 0xe5, 0x6c, 0x64, 0x8d, + 0xa4, 0x32, 0xb2, 0xc1, 0x9a, 0x32, 0xdf, 0x29, 0x6a, 0xf6, 0x52, 0x11, 0x3d, 0x83, 0xea, 0x2c, + 0x63, 0x4d, 0x33, 0x52, 0xd2, 0x91, 0x9d, 0x65, 0xe4, 0x75, 0x4a, 0x31, 0xcc, 0x16, 0x7a, 0xeb, + 0x9b, 0x09, 0x9b, 0x27, 0x19, 0x75, 0x8c, 0x47, 0x6f, 0xf4, 0x57, 0x22, 0x07, 0x4a, 0x31, 0xf1, + 0x7d, 0x16, 0x8d, 0x34, 0x19, 0x16, 0x9e, 0xab, 0xe8, 0x39, 0x94, 0x3d, 0x1e, 0x29, 0x1a, 0x29, + 0x99, 0x53, 0xd0, 0x5c, 0xe6, 0xb9, 0x13, 0xe8, 0xe0, 0x92, 0xfb, 0x14, 0x2f, 0x3c, 0xd0, 0x0b, + 0xa8, 0x0c, 0x59, 0x40, 0x53, 0x22, 0xa4, 0x66, 0xe5, 0x21, 0xee, 0x4b, 0x17, 0xb4, 0x0b, 0x8d, + 0x98, 0x07, 0xcc, 0x4b, 0xdc, 0x1b, 0x2a, 0x24, 0xe3, 0x51, 0xbe, 0x43, 0xf5, 0x0c, 0x1d, 0x64, + 0x60, 0xeb, 0xb3, 0x01, 0x85, 0xd4, 0x15, 0x55, 0xa1, 0xe4, 0xd3, 0x21, 0x99, 0x06, 0xca, 0xfe, + 0x05, 0xfd, 0x0a, 0xd5, 0xa3, 0x93, 0x9e, 0xdb, 0x39, 0x7c, 0xe2, 0xbe, 0xef, 0xf7, 0x6c, 0x63, + 0x15, 0x78, 0xd5, 0xbd, 0xb4, 0xcd, 0x55, 0xa0, 0xfb, 0xb2, 0x6b, 0x5b, 0x6b, 0x40, 0xbf, 0x67, + 0x17, 0xe6, 0x40, 0xbb, 0xf3, 0x54, 0x5b, 0x6c, 0xac, 0x01, 0xfd, 0x9e, 0x5d, 0x44, 0x35, 0x28, + 0x1f, 0xf9, 0x8c, 0x44, 0x6a, 0x1a, 0xda, 0x95, 0xd6, 0x27, 0x03, 0xec, 0x9c, 0xfd, 0x2b, 0x5d, + 0x62, 0xba, 0x9d, 0x3f, 0xb1, 0x77, 0xb7, 0x26, 0x6c, 0x3e, 0x62, 0xc2, 0x5f, 0x0d, 0x80, 0x2c, + 0xb7, 0x5e, 0xfa, 0x5d, 0x68, 0x4c, 0x68, 0x72, 0x37, 0x6d, 0x7d, 0x42, 0x93, 0x95, 0x84, 0x87, + 0x50, 0xe2, 0xd9, 0x10, 0xf2, 0x64, 0x7f, 0xdd, 0x33, 0x27, 0x3c, 0xb7, 0x45, 0xe7, 0xb0, 0x35, + 0xaf, 0x33, 0x1f, 0xd4, 0x84, 0x26, 0xe9, 0xa8, 0xad, 0xbd, 0x6a, 0xe7, 0xcf, 0x3b, 0xf5, 0x2e, + 0x38, 0xc1, 0x9b, 0xb3, 0x5b, 0x88, 0x6c, 0x7d, 0x37, 0xa0, 0xd8, 0xe5, 0xd1, 0x90, 0x8d, 0x56, + 0xce, 0xce, 0x78, 0xc0, 0xd9, 0x1d, 0x02, 0x8c, 0x89, 0x1c, 0xbb, 0xd9, 0x9d, 0x99, 0xf7, 0xde, + 0x59, 0x25, 0xb5, 0xcc, 0xfe, 0x64, 0x2b, 0x2d, 0x17, 0x1e, 0xd1, 0xf2, 0x11, 0xfc, 0x3d, 0x95, + 0xd4, 0x1d, 0xca, 0xb4, 0x55, 0xc1, 0xa2, 0x91, 0x3b, 0xe4, 0xc2, 0xbd, 0x69, 0x67, 0x04, 0x30, + 0x2a, 0xf5, 0xf1, 0x96, 0xf1, 0x1f, 0x53, 0x49, 0x4f, 0xe5, 0x45, 0x66, 0x73, 0xca, 0xc5, 0xa0, + 0x7d, 0x95, 0x1b, 0x9c, 0x17, 0xca, 0x96, 0x5d, 0xc0, 0x75, 0x8f, 0x87, 0x31, 0x51, 0xec, 0x9a, + 0x05, 0x4c, 0x25, 0xff, 0xbe, 0x05, 0x58, 0xf6, 0xb6, 0xbe, 0xc9, 0x08, 0x1a, 0x31, 0x09, 0xdd, + 0x98, 0x48, 0x19, 0x8f, 0x05, 0x91, 0xd4, 0x36, 0xd0, 0x6f, 0xb0, 0xe9, 0x4d, 0xa5, 0xe2, 0x6b, + 0xb0, 0x99, 0xfa, 0x09, 0x32, 0x4b, 0x4b, 0xb3, 0xad, 0xeb, 0xa2, 0xfe, 0x99, 0xff, 0xff, 0x23, + 0x00, 0x00, 0xff, 0xff, 0x3d, 0x33, 0x9f, 0x0d, 0xe7, 0x05, 0x00, 0x00, } diff --git a/metadata/metadata.proto b/metadata/metadata.proto index 81b3bf9a..8ffb4f6e 100644 --- a/metadata/metadata.proto +++ b/metadata/metadata.proto @@ -97,7 +97,10 @@ message PolicyData { message Config { SourceType source = 1; HashingCosts hash_costs = 2; - string compatibility = 3; EncryptionOptions options = 4; bool use_fs_keyring_for_v1_policies = 5; + + // reserve the removed field 'string compatibility = 3;' + reserved 3; + reserved "compatibility"; } From de18d40a797ce14e348c33afd1f929239ddb4c63 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 17 Mar 2020 21:10:58 -0700 Subject: [PATCH 2/6] Create /etc/fscrypt.conf with policy_version 2 on kernel v5.4+ v2 encryption policies are now recommended, due to various security and usability advantages over v1 policies. Many people have been running into the usability problems with v1, so it's desirable to get people onto v2 without having to manually opt-in. Therefore, when 'fscrypt setup' creates /etc/fscrypt.conf, enable policy_version 2 automatically if the kernel supports it. I decided to go with this solution over the policy_version "auto" I suggested originally because this way is simpler, it can still be changed to "auto" later if desired, and "auto" might require changing how we parse the config file (since currently the config file is mapped directly to a protobuf where policy_version is an 'int' and is shared with EncryptionOptions). Resolves https://github.com/google/fscrypt/issues/182 --- actions/config.go | 9 +++++++-- actions/config_test.go | 26 +++++++++++++++++++++++++- actions/context_test.go | 2 +- cmd/fscrypt/setup.go | 16 +++++++++++++++- util/util.go | 23 +++++++++++++++++++++++ util/util_test.go | 7 +++++++ 6 files changed, 78 insertions(+), 5 deletions(-) diff --git a/actions/config.go b/actions/config.go index 3433438f..2463b957 100644 --- a/actions/config.go +++ b/actions/config.go @@ -58,8 +58,9 @@ var ( // CreateConfigFile creates a new config file at the appropriate location with // the appropriate hashing costs and encryption parameters. The hashing will be -// configured to take as long as the specified time target. -func CreateConfigFile(target time.Duration) error { +// configured to take as long as the specified time target. In addition, the +// version of encryption policy to use may be overridden from the default of v1. +func CreateConfigFile(target time.Duration, policyVersion int64) error { // Create the config file before computing the hashing costs, so we fail // immediately if the program has insufficient permissions. configFile, err := filesystem.OpenFileOverridingUmask(ConfigFileLocation, @@ -77,6 +78,10 @@ func CreateConfigFile(target time.Duration) error { Options: metadata.DefaultOptions, } + if policyVersion != 0 { + config.Options.PolicyVersion = policyVersion + } + if config.HashCosts, err = getHashingCosts(target); err != nil { return err } diff --git a/actions/config_test.go b/actions/config_test.go index 02c89e6b..35996676 100644 --- a/actions/config_test.go +++ b/actions/config_test.go @@ -26,6 +26,8 @@ import ( "time" "golang.org/x/sys/unix" + + "github.com/google/fscrypt/metadata" ) // Test that the global config file is created with mode 0644, regardless of the @@ -42,7 +44,7 @@ func TestConfigFileIsCreatedWithCorrectMode(t *testing.T) { defer os.RemoveAll(tempDir) ConfigFileLocation = filepath.Join(tempDir, "test.conf") - if err = CreateConfigFile(time.Millisecond); err != nil { + if err = CreateConfigFile(time.Millisecond, 0); err != nil { t.Fatal(err) } fileInfo, err := os.Stat(ConfigFileLocation) @@ -53,3 +55,25 @@ func TestConfigFileIsCreatedWithCorrectMode(t *testing.T) { t.Error("Expected newly created config file to have mode 0644") } } + +func TestCreateConfigFileV2Policy(t *testing.T) { + tempDir, err := ioutil.TempDir("", "fscrypt") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + ConfigFileLocation = filepath.Join(tempDir, "test.conf") + + if err = CreateConfigFile(time.Millisecond, 2); err != nil { + t.Fatal(err) + } + + var config *metadata.Config + config, err = getConfig() + if err != nil { + t.Fatal(err) + } + if config.Options.PolicyVersion != 2 { + t.Error("Expected PolicyVersion 2") + } +} diff --git a/actions/context_test.go b/actions/context_test.go index 4f93776c..4488a6be 100644 --- a/actions/context_test.go +++ b/actions/context_test.go @@ -52,7 +52,7 @@ func setupContext() (ctx *Context, err error) { return nil, fmt.Errorf("created context at %q without config file", badCtx.Mount.Path) } - if err = CreateConfigFile(testTime); err != nil { + if err = CreateConfigFile(testTime, 0); err != nil { return nil, err } defer func() { diff --git a/cmd/fscrypt/setup.go b/cmd/fscrypt/setup.go index 328788af..7b9bebbb 100644 --- a/cmd/fscrypt/setup.go +++ b/cmd/fscrypt/setup.go @@ -50,8 +50,22 @@ func createGlobalConfig(w io.Writer, path string) error { return err } + // v2 encryption policies are recommended, so set policy_version 2 when + // the kernel supports it. v2 policies are supported by upstream Linux + // v5.4 and later. For now we simply check the kernel version. Ideally + // we'd instead check whether setting a v2 policy actually works, in + // order to also detect backports of the kernel patches. However, that's + // hard because from this context (creating /etc/fscrypt.conf) we may + // not yet have access to a filesystem that supports encryption. + var policyVersion int64 + if util.IsKernelVersionAtLeast(5, 4) { + fmt.Fprintln(w, "Defaulting to policy_version 2 because kernel supports it.") + policyVersion = 2 + } else { + fmt.Fprintln(w, "Defaulting to policy_version 1 because kernel doesn't support v2.") + } fmt.Fprintln(w, "Customizing passphrase hashing difficulty for this system...") - err = actions.CreateConfigFile(timeTargetFlag.Value) + err = actions.CreateConfigFile(timeTargetFlag.Value, policyVersion) if err != nil { return err } diff --git a/util/util.go b/util/util.go index 97ee33cb..d97a7ae8 100644 --- a/util/util.go +++ b/util/util.go @@ -25,10 +25,14 @@ package util import ( "bufio" + "fmt" + "log" "os" "os/user" "strconv" "unsafe" + + "golang.org/x/sys/unix" ) // Ptr converts a Go byte array to a pointer to the start of the array. @@ -126,3 +130,22 @@ func EffectiveUser() (*user.User, error) { func IsUserRoot() bool { return os.Geteuid() == 0 } + +// IsKernelVersionAtLeast returns true if the Linux kernel version is at least +// major.minor. If something goes wrong it assumes false. +func IsKernelVersionAtLeast(major, minor int) bool { + var uname unix.Utsname + if err := unix.Uname(&uname); err != nil { + log.Printf("Uname failed [%v], assuming old kernel", err) + return false + } + release := string(uname.Release[:]) + log.Printf("Kernel version is %s", release) + var actualMajor, actualMinor int + if n, _ := fmt.Sscanf(release, "%d.%d", &actualMajor, &actualMinor); n != 2 { + log.Printf("Unrecognized uname format %q, assuming old kernel", release) + return false + } + return actualMajor > major || + (actualMajor == major && actualMinor >= minor) +} diff --git a/util/util_test.go b/util/util_test.go index 7739edd0..70e7070e 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -91,3 +91,10 @@ func TestNeverErrorNoPanic(t *testing.T) { NeverError(nil) } + +func TestIsKernelVersionAtLeast(t *testing.T) { + // Even just running Go requires at least v2.6.23, so... + if !IsKernelVersionAtLeast(2, 6) { + t.Error("IsKernelVersionAtLeast() is broken") + } +} From 0475620a403866e14f859e5a19577821bebe21b5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 17 Mar 2020 21:10:58 -0700 Subject: [PATCH 3/6] Improve error message when setting v2 policy is unsupported If trying to encrypt a directory using a v2 policy fails due to the kernel lacking support for v2 policies, show a better error message. One way this can happen is if someone runs 'fscrypt setup' with a new kernel and then downgrades to an old kernel. Before: # echo -n hunter2 | fscrypt encrypt dir --source=custom_passphrase --name=foo --quiet [ERROR] fscrypt encrypt: inappropriate ioctl for device: system error: could not add key to the keyring After: # echo -n hunter2 | fscrypt encrypt dir --source=custom_passphrase --name=foo --quiet [ERROR] fscrypt encrypt: kernel is too old to support v2 encryption policies v2 encryption policies are only supported by kernel version 5.4 and later. Either use a newer kernel, or change policy_version to 1 in /etc/fscrypt.conf. --- cmd/fscrypt/errors.go | 4 ++++ keyring/keyring.go | 48 ++++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/cmd/fscrypt/errors.go b/cmd/fscrypt/errors.go index bef6c2af..c2425525 100644 --- a/cmd/fscrypt/errors.go +++ b/cmd/fscrypt/errors.go @@ -113,6 +113,10 @@ func getErrorSuggestions(err error) string { return fmt.Sprintf(`You can only use %s to access the user keyring of another user if you are running as root.`, shortDisplay(userFlag)) + case keyring.ErrV2PoliciesUnsupported: + return fmt.Sprintf(`v2 encryption policies are only supported by kernel + version 5.4 and later. Either use a newer kernel, or change + policy_version to 1 in %s.`, actions.ConfigFileLocation) case actions.ErrBadConfigFile: return `Run "sudo fscrypt setup" to recreate the file.` case actions.ErrNoConfigFile: diff --git a/keyring/keyring.go b/keyring/keyring.go index 20bc48bf..f2fc286f 100644 --- a/keyring/keyring.go +++ b/keyring/keyring.go @@ -43,15 +43,16 @@ import ( // Keyring error values var ( - ErrKeyAdd = util.SystemError("could not add key to the keyring") - ErrKeyRemove = util.SystemError("could not remove key from the keyring") - ErrKeyNotPresent = errors.New("key not present or already removed") - ErrKeyFilesOpen = errors.New("some files using the key are still open") - ErrKeyAddedByOtherUsers = errors.New("other users have added the key too") - ErrKeySearch = errors.New("could not find key with descriptor") - ErrSessionUserKeying = errors.New("user keyring not linked into session keyring") - ErrAccessUserKeyring = errors.New("could not access user keyring") - ErrLinkUserKeyring = util.SystemError("could not link user keyring into root keyring") + ErrKeyAdd = util.SystemError("could not add key to the keyring") + ErrKeyRemove = util.SystemError("could not remove key from the keyring") + ErrKeyNotPresent = errors.New("key not present or already removed") + ErrKeyFilesOpen = errors.New("some files using the key are still open") + ErrKeyAddedByOtherUsers = errors.New("other users have added the key too") + ErrKeySearch = errors.New("could not find key with descriptor") + ErrSessionUserKeying = errors.New("user keyring not linked into session keyring") + ErrAccessUserKeyring = errors.New("could not access user keyring") + ErrLinkUserKeyring = util.SystemError("could not link user keyring into root keyring") + ErrV2PoliciesUnsupported = errors.New("kernel is too old to support v2 encryption policies") ) // Options are the options which specify *which* keyring the key should be @@ -69,16 +70,19 @@ type Options struct { UseFsKeyringForV1Policies bool } -func shouldUseFsKeyring(descriptor string, options *Options) bool { +func shouldUseFsKeyring(descriptor string, options *Options) (bool, error) { // For v1 encryption policy keys, use the filesystem keyring if // use_fs_keyring_for_v1_policies is set in /etc/fscrypt.conf and the // kernel supports it. if len(descriptor) == hex.EncodedLen(unix.FSCRYPT_KEY_DESCRIPTOR_SIZE) { - return options.UseFsKeyringForV1Policies && isFsKeyringSupported(options.Mount) + return options.UseFsKeyringForV1Policies && isFsKeyringSupported(options.Mount), nil } // For v2 encryption policy keys, always use the filesystem keyring; the // kernel doesn't support any other way. - return true + if !isFsKeyringSupported(options.Mount) { + return true, ErrV2PoliciesUnsupported + } + return true, nil } // buildKeyDescription builds the description for an fscrypt key of type @@ -103,7 +107,11 @@ func AddEncryptionKey(key *crypto.Key, descriptor string, options *Options) erro if err := util.CheckValidLength(metadata.PolicyKeyLen, key.Len()); err != nil { return errors.Wrap(err, "policy key") } - if shouldUseFsKeyring(descriptor, options) { + useFsKeyring, err := shouldUseFsKeyring(descriptor, options) + if err != nil { + return err + } + if useFsKeyring { return fsAddEncryptionKey(key, descriptor, options.Mount, options.User) } return userAddKey(key, buildKeyDescription(options, descriptor), options.User) @@ -113,7 +121,11 @@ func AddEncryptionKey(key *crypto.Key, descriptor string, options *Options) erro // It uses either the filesystem keyring for the target Mount or the user // keyring for the target User. func RemoveEncryptionKey(descriptor string, options *Options, allUsers bool) error { - if shouldUseFsKeyring(descriptor, options) { + useFsKeyring, err := shouldUseFsKeyring(descriptor, options) + if err != nil { + return err + } + if useFsKeyring { user := options.User if allUsers { user = nil @@ -156,10 +168,14 @@ func (status KeyStatus) String() string { // kernel keyring. It uses either the filesystem keyring for the target Mount // or the user keyring for the target User. func GetEncryptionKeyStatus(descriptor string, options *Options) (KeyStatus, error) { - if shouldUseFsKeyring(descriptor, options) { + useFsKeyring, err := shouldUseFsKeyring(descriptor, options) + if err != nil { + return KeyStatusUnknown, err + } + if useFsKeyring { return fsGetEncryptionKeyStatus(descriptor, options.Mount, options.User) } - _, err := userFindKey(buildKeyDescription(options, descriptor), options.User) + _, err = userFindKey(buildKeyDescription(options, descriptor), options.User) if err != nil { return KeyAbsent, nil } From 693f011c51edf140b8fb1bcaf0751d0b39714507 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 17 Mar 2020 21:10:58 -0700 Subject: [PATCH 4/6] Improve error message when unlocking v2 policy is unsupported If trying to unlock a v2-encrypted directory fails because the kernel lacks support for v2 policies, show a better error message. This can happen if someone downgrades their kernel or tries to access encrypted directories on removable storage from a computer with an older kernel. Detecting this case is difficult since all we have to go with is EACCES when opening the directory. Implement a heuristic where if get EACCES, we actually have read access to the directory, and the kernel doesn't support v2 policies, we show the improved error message. Before: # fscrypt unlock dir [ERROR] fscrypt unlock: open dir: permission denied After: # fscrypt unlock dir [ERROR] fscrypt unlock: open dir: permission denied This may be caused by the directory using a v2 encryption policy and the current kernel not supporting it. If indeed the case, then this directory can only be used on kernel v5.4 and later. You can create directories accessible on older kernels by changing policy_version to 1 in /etc/fscrypt.conf. --- actions/policy.go | 13 +++++++++++++ cmd/fscrypt/errors.go | 7 +++++++ filesystem/path.go | 6 ++++++ filesystem/path_test.go | 27 +++++++++++++++++++++++++++ keyring/fs_keyring.go | 4 ++-- keyring/keyring.go | 4 ++-- keyring/keyring_test.go | 2 +- 7 files changed, 58 insertions(+), 5 deletions(-) diff --git a/actions/policy.go b/actions/policy.go index b7fe5a65..94ea74ce 100644 --- a/actions/policy.go +++ b/actions/policy.go @@ -22,6 +22,7 @@ package actions import ( "fmt" "log" + "os" "github.com/golang/protobuf/proto" "github.com/pkg/errors" @@ -41,6 +42,7 @@ var ( ErrOnlyProtector = errors.New("cannot remove the only protector for a policy") ErrAlreadyProtected = errors.New("policy already protected by protector") ErrNotProtected = errors.New("policy not protected by protector") + ErrAccessDeniedPossiblyV2 = errors.New("permission denied") ) // PurgeAllPolicies removes all policy keys on the filesystem from the kernel @@ -152,6 +154,17 @@ func GetPolicyFromPath(ctx *Context, path string) (*Policy, error) { // the path, and the data we get from the mountpoint. pathData, err := metadata.GetPolicy(path) if err != nil { + // On kernels that don't support v2 encryption policies, trying + // to open a directory with a v2 policy simply gave EACCES. This + // is ambiguous with other errors, but try to detect this case + // and show a better error message. + if e, ok := err.(*os.PathError); ok { + if e.Err.Error() == "permission denied" && + filesystem.HaveReadAccessTo(path) && + !keyring.IsFsKeyringSupported(ctx.Mount) { + return nil, errors.Wrapf(ErrAccessDeniedPossiblyV2, "open %s", path) + } + } return nil, err } descriptor := pathData.KeyDescriptor diff --git a/cmd/fscrypt/errors.go b/cmd/fscrypt/errors.go index c2425525..8bda9213 100644 --- a/cmd/fscrypt/errors.go +++ b/cmd/fscrypt/errors.go @@ -131,6 +131,13 @@ func getErrorSuggestions(err error) string { metadata is corrupted.` case actions.ErrMissingProtectorName: return fmt.Sprintf("Use %s to specify a protector name.", shortDisplay(nameFlag)) + case actions.ErrAccessDeniedPossiblyV2: + return fmt.Sprintf(`This may be caused by the directory using a v2 + encryption policy and the current kernel not supporting it. If + indeed the case, then this directory can only be used on kernel + v5.4 and later. You can create directories accessible on older + kernels by changing policy_version to 1 in %s.`, + actions.ConfigFileLocation) case ErrNoDestructiveOps: return fmt.Sprintf("Use %s to automatically run destructive operations.", shortDisplay(forceFlag)) case ErrSpecifyProtector: diff --git a/filesystem/path.go b/filesystem/path.go index b9b403dd..274dc0a2 100644 --- a/filesystem/path.go +++ b/filesystem/path.go @@ -78,6 +78,12 @@ func isRegularFile(path string) bool { return err == nil && info.Mode().IsRegular() } +// HaveReadAccessTo returns true if the process has read access to a file or +// directory, without actually opening it. +func HaveReadAccessTo(path string) bool { + return unix.Access(path, unix.R_OK) == nil +} + // DeviceNumber represents a combined major:minor device number. type DeviceNumber uint64 diff --git a/filesystem/path_test.go b/filesystem/path_test.go index eef5ce3e..41520375 100644 --- a/filesystem/path_test.go +++ b/filesystem/path_test.go @@ -20,6 +20,8 @@ package filesystem import ( "fmt" + "io/ioutil" + "os" "testing" ) @@ -52,3 +54,28 @@ func TestDeviceNumber(t *testing.T) { t.Error("Should have failed to parse invalid device number") } } + +func TestHaveReadAccessTo(t *testing.T) { + file, err := ioutil.TempFile("", "fscrypt_test") + if err != nil { + t.Fatal(err) + } + file.Close() + defer os.Remove(file.Name()) + + testCases := map[os.FileMode]bool{ + 0444: true, + 0400: true, + 0000: false, + 0040: false, // user bits take priority in Linux + 0004: false, // user bits take priority in Linux + } + for mode, readable := range testCases { + if err := os.Chmod(file.Name(), mode); err != nil { + t.Error(err) + } + if HaveReadAccessTo(file.Name()) != readable { + t.Errorf("Expected readable=%v on mode=0%03o", readable, mode) + } + } +} diff --git a/keyring/fs_keyring.go b/keyring/fs_keyring.go index 42c16482..f0016a47 100644 --- a/keyring/fs_keyring.go +++ b/keyring/fs_keyring.go @@ -79,10 +79,10 @@ func checkForFsKeyringSupport(mount *filesystem.Mount) bool { return true } -// isFsKeyringSupported returns true if the kernel supports the ioctls to +// IsFsKeyringSupported returns true if the kernel supports the ioctls to // add/remove fscrypt keys directly to/from the filesystem. For support to be // detected, the given Mount must be for a filesystem that supports fscrypt. -func isFsKeyringSupported(mount *filesystem.Mount) bool { +func IsFsKeyringSupported(mount *filesystem.Mount) bool { fsKeyringSupportedLock.Lock() defer fsKeyringSupportedLock.Unlock() if !fsKeyringSupportedKnown { diff --git a/keyring/keyring.go b/keyring/keyring.go index f2fc286f..1f513c6b 100644 --- a/keyring/keyring.go +++ b/keyring/keyring.go @@ -75,11 +75,11 @@ func shouldUseFsKeyring(descriptor string, options *Options) (bool, error) { // use_fs_keyring_for_v1_policies is set in /etc/fscrypt.conf and the // kernel supports it. if len(descriptor) == hex.EncodedLen(unix.FSCRYPT_KEY_DESCRIPTOR_SIZE) { - return options.UseFsKeyringForV1Policies && isFsKeyringSupported(options.Mount), nil + return options.UseFsKeyringForV1Policies && IsFsKeyringSupported(options.Mount), nil } // For v2 encryption policy keys, always use the filesystem keyring; the // kernel doesn't support any other way. - if !isFsKeyringSupported(options.Mount) { + if !IsFsKeyringSupported(options.Mount) { return true, ErrV2PoliciesUnsupported } return true, nil diff --git a/keyring/keyring_test.go b/keyring/keyring_test.go index 22081056..26f6036b 100644 --- a/keyring/keyring_test.go +++ b/keyring/keyring_test.go @@ -81,7 +81,7 @@ func getTestMount(t *testing.T) *filesystem.Mount { // filesystem keyring and v2 encryption policies are supported. func getTestMountV2(t *testing.T) *filesystem.Mount { mount := getTestMount(t) - if !isFsKeyringSupported(mount) { + if !IsFsKeyringSupported(mount) { t.Skip("No support for fs keyring, skipping test.") } return mount From 6fbaac464997ad48e19b0806bf1fa7e29ca35ec9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 17 Mar 2020 21:10:58 -0700 Subject: [PATCH 5/6] README.md: update examples to use v2 policies Since on new kernels v1 encryption policies are deprecated in favor of v2, update the examples to show v2. This mostly just consists of updating the output, as the commands are essentially the same with one notable difference in 'fscrypt lock'. --- README.md | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 944493e1..e81bb62f 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ that looks like the following: "padding": "32", "contents": "AES_256_XTS", "filenames": "AES_256_CTS", - "policy_version": "1" + "policy_version": "2" }, "use_fs_keyring_for_v1_policies": false } @@ -335,6 +335,7 @@ MOUNTPOINT DEVICE FILESYSTEM ENCRYPTION FSCRYPT # Create the global configuration file. Nothing else necessarily needs root. >>>>> sudo fscrypt setup +Defaulting to policy_version 2 because kernel supports it. Customizing passphrase hashing difficulty for this system... Created global config file at "/etc/fscrypt.conf". Metadata directories created at "/.fscrypt". @@ -363,8 +364,8 @@ ext4 filesystem "/mnt/disk" has 1 protector and 1 policy PROTECTOR LINKED DESCRIPTION 7626382168311a9d No custom protector "Super Secret" -POLICY UNLOCKED PROTECTORS -7626382168311a9d Yes 7626382168311a9d +POLICY UNLOCKED PROTECTORS +16382f282d7b29ee27e6460151d03382 Yes 7626382168311a9d ``` #### Quiet Version @@ -382,24 +383,23 @@ POLICY UNLOCKED PROTECTORS >>>>> fscrypt status /mnt/disk/dir1 "/mnt/disk/dir1" is encrypted with fscrypt. -Policy: 16382f282d7b29ee -Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:1 +Policy: 16382f282d7b29ee27e6460151d03382 +Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2 Unlocked: Yes Protected with 1 protector: PROTECTOR LINKED DESCRIPTION 7626382168311a9d No custom protector "Super Secret" -# Lock the directory. 'sudo' and the '--user' argument are only -# required if the directory uses a v1 encryption policy. ->>>>> sudo fscrypt lock /mnt/disk/dir1 --user=$USER -Encrypted data removed from filesystem cache. +# Lock the directory. Note: if using a v1 encryption policy instead +# of v2, you'll need 'sudo fscrypt lock /mnt/disk/dir1 --user=$USER'. +>>>>> fscrypt lock /mnt/disk/dir1 "/mnt/disk/dir1" is now locked. >>>>> fscrypt status /mnt/disk/dir1 "/mnt/disk/dir1" is encrypted with fscrypt. -Policy: 16382f282d7b29ee -Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:1 +Policy: 16382f282d7b29ee27e6460151d03382 +Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2 Unlocked: No Protected with 1 protector: @@ -419,8 +419,8 @@ Enter custom passphrase for protector "Super Secret": >>>>> fscrypt status /mnt/disk/dir1 "/mnt/disk/dir1" is encrypted with fscrypt. -Policy: 16382f282d7b29ee -Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:1 +Policy: 16382f282d7b29ee27e6460151d03382 +Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2 Unlocked: Yes Protected with 1 protector: @@ -432,7 +432,7 @@ Hello World #### Quiet Version ```bash ->>>>> sudo fscrypt lock /mnt/disk/dir1 --quiet --user=$USER +>>>>> fscrypt lock /mnt/disk/dir1 --quiet >>>>> echo "hunter2" | fscrypt unlock /mnt/disk/dir1 --quiet ``` @@ -455,8 +455,8 @@ Enter login passphrase for joerichey: >>>>> fscrypt status /mnt/disk/dir2 "/mnt/disk/dir2" is encrypted with fscrypt. -Policy: fe1c92009abc1cff -Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:1 +Policy: fe1c92009abc1cff4f3257c77e8134e3 +Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2 Unlocked: Yes Protected with 1 protector: @@ -469,9 +469,9 @@ PROTECTOR LINKED DESCRIPTION 7626382168311a9d No custom protector "Super Secret" 6891f0a901f0065e Yes (/) login protector for joerichey -POLICY UNLOCKED PROTECTORS -16382f282d7b29ee Yes 7626382168311a9d -fe1c92009abc1cff Yes 6891f0a901f0065e +POLICY UNLOCKED PROTECTORS +16382f282d7b29ee27e6460151d03382 Yes 7626382168311a9d +fe1c92009abc1cff4f3257c77e8134e3 Yes 6891f0a901f0065e >>>>> fscrypt status / ext4 filesystem "/" has 1 protector(s) and 0 policy(ies) @@ -491,8 +491,8 @@ PROTECTOR LINKED DESCRIPTION >>>>> fscrypt status /mnt/disk/dir1 "/mnt/disk/dir1" is encrypted with fscrypt. -Policy: 16382f282d7b29ee -Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:1 +Policy: 16382f282d7b29ee27e6460151d03382 +Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2 Unlocked: Yes Protected with 1 protector: @@ -546,9 +546,9 @@ PROTECTOR LINKED DESCRIPTION 2c75f519b9c9959d No raw key protector "Skeleton" 6891f0a901f0065e Yes (/) login protector for joerichey -POLICY UNLOCKED PROTECTORS -16382f282d7b29ee Yes 7626382168311a9d -fe1c92009abc1cff Yes 6891f0a901f0065e +POLICY UNLOCKED PROTECTORS +16382f282d7b29ee27e6460151d03382 Yes 7626382168311a9d +fe1c92009abc1cff4f3257c77e8134e3 Yes 6891f0a901f0065e # Finally, we could apply this key to a directory >>>>> mkdir /mnt/disk/dir3 @@ -580,31 +580,31 @@ PROTECTOR LINKED DESCRIPTION 2c75f519b9c9959d No raw key protector "Skeleton" 6891f0a901f0065e Yes (/) login protector for joerichey -POLICY UNLOCKED PROTECTORS -d03fb894584a4318 No 2c75f519b9c9959d -16382f282d7b29ee No 7626382168311a9d -fe1c92009abc1cff No 6891f0a901f0065e +POLICY UNLOCKED PROTECTORS +d03fb894584a4318d1780e9a7b0b47eb No 2c75f519b9c9959d +16382f282d7b29ee27e6460151d03382 No 7626382168311a9d +fe1c92009abc1cff4f3257c77e8134e3 No 6891f0a901f0065e >>>>> fscrypt status /mnt/disk/dir1 "/mnt/disk/dir1" is encrypted with fscrypt. -Policy: 16382f282d7b29ee -Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:1 +Policy: 16382f282d7b29ee27e6460151d03382 +Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2 Unlocked: No Protected with 1 protector: PROTECTOR LINKED DESCRIPTION 7626382168311a9d No custom protector "Super Secret" ->>>>> fscrypt metadata add-protector-to-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee +>>>>> fscrypt metadata add-protector-to-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee27e6460151d03382 WARNING: All files using this policy will be accessible with this protector!! -Protect policy 16382f282d7b29ee with protector 2c75f519b9c9959d? [Y/n] +Protect policy 16382f282d7b29ee27e6460151d03382 with protector 2c75f519b9c9959d? [Y/n] Enter key file for protector "Skeleton": secret.key Enter custom passphrase for protector "Super Secret": -Protector 2c75f519b9c9959d now protecting policy 16382f282d7b29ee. +Protector 2c75f519b9c9959d now protecting policy 16382f282d7b29ee27e6460151d03382. >>>>> fscrypt status /mnt/disk/dir1 "/mnt/disk/dir1" is encrypted with fscrypt. -Policy: 16382f282d7b29ee -Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:1 +Policy: 16382f282d7b29ee27e6460151d03382 +Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2 Unlocked: No Protected with 2 protectors: @@ -622,16 +622,16 @@ Enter key file for protector "Skeleton": secret.key "/mnt/disk/dir1" is now unlocked and ready for use. # The protector can also be removed from the policy (if it is not the only one) ->>>>> fscrypt metadata remove-protector-from-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee +>>>>> fscrypt metadata remove-protector-from-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee27e6460151d03382 WARNING: All files using this policy will NO LONGER be accessible with this protector!! -Stop protecting policy 16382f282d7b29ee with protector 2c75f519b9c9959d? [y/N] y -Protector 2c75f519b9c9959d no longer protecting policy 16382f282d7b29ee. +Stop protecting policy 16382f282d7b29ee27e6460151d03382 with protector 2c75f519b9c9959d? [y/N] y +Protector 2c75f519b9c9959d no longer protecting policy 16382f282d7b29ee27e6460151d03382. ``` #### Quiet Version ```bash ->>>>> echo "hunter2" | fscrypt metadata add-protector-to-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee --key=secret.key --quiet ->>>>> fscrypt metadata remove-protector-from-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee --quiet --force +>>>>> echo "hunter2" | fscrypt metadata add-protector-to-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee27e6460151d03382 --key=secret.key --quiet +>>>>> fscrypt metadata remove-protector-from-policy --protector=/mnt/disk:2c75f519b9c9959d --policy=/mnt/disk:16382f282d7b29ee27e6460151d03382 --quiet --force ``` ## Contributing From 16bfa32bd6f92d0afbd50745df2ae47d7cd61e80 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 17 Mar 2020 21:10:58 -0700 Subject: [PATCH 6/6] README.md: update v2 policy-related documentation - Mention that a v5.4+ kernel is recommended. - Mention that policy_version defaults to 1 when unset. - Emphasize that v2 policies are the recommended solution to the key visibility problems, and add some more information. --- README.md | 61 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e81bb62f..f465451f 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,10 @@ To check whether the needed option is enabled in your kernel, run: zgrep -h ENCRYPTION /proc/config.gz /boot/config-$(uname -r) | sort | uniq ``` +It is also recommended for the kernel to be v5.4 or later, since this +allows the use of v2 encryption policies. v2 policies have several +security and usability improvements over v1 policies. + Be careful when using encryption on removable media, since filesystems with the `encrypt` feature cannot be mounted on systems with kernel versions older than the minimums listed above -- even to access unencrypted files! @@ -241,9 +245,10 @@ The fields are: for more details about the supported algorithms. * "policy\_version" is the version of encryption policy to use. - The choices are "1" and "2". Directories created with policy - version "2" are only usable on kernel v5.4 or later, but are - preferable to version "1" if you don't mind this restriction. + The choices are "1" and "2". If unset, "1" is assumed. + Directories created with policy version "2" are only usable on + kernel v5.4 or later, but are preferable to version "1" if you + don't mind this restriction. * "use\_fs\_keyring\_for\_v1\_policies" specifies whether to add keys for v1 encryption policies to the filesystem keyring, rather than to @@ -746,27 +751,45 @@ manifest in other ways such as Docker containers being unable to access encrypted files, or NetworkManager being unable to access certificates if they are located in an encrypted directory. -If you are using kernel v5.4 or later, you can fix this by setting the -following in `/etc/fscrypt.conf`: +The recommended way to fix this is by creating your encrypted +directories using v2 encryption policies rather than v1. This +requires Linux v5.4 or later and `fscrypt` v0.2.6 or later. If these +prerequisites are met, to enable v2 policies on new directories set +`"policy_version": "2"` in `/etc/fscrypt.conf`. For example: - "use_fs_keyring_for_v1_policies": true +``` + "options": { + "padding": "32", + "contents": "AES_256_XTS", + "filenames": "AES_256_CTS", + "policy_version": "2" + }, +``` + +This only affects new directories. If you want to upgrade an existing +encrypted directory to use a v2 policy, you'll need to re-create it by +using `fscrypt encrypt` to encrypt a new empty directory, copying your +files into it, and replacing the original directory with it. -However, this makes manually unlocking and locking encrypted -directories start to require root. (The PAM module will still work.) -E.g., you'll need to run `sudo fscrypt unlock`, not `fscrypt unlock`. +In `fscrypt` v0.2.7 and later, the `fscrypt setup` command +automatically sets `"policy_version": "2"` when creating +`/etc/fscrypt.conf` if kernel support is present. -Alternatively, you can upgrade your encrypted directories to use v2 -encryption policies by setting the following in the "options" section -of `/etc/fscrypt.conf`: +__IMPORTANT:__ directories that use v2 encryption policies are +unusable on Linux v5.3 and earlier. If this will be a problem for you +(for example, if your encrypted directories are on removable storage +that needs to work on computers with both old and new kernels), you'll +need to use v1 policies instead. In this case, or if you simply don't +want to re-create your encrypted directories, there's also a fallback +option to make `fscrypt` use the filesystem keyring for v1 policies +whenever the kernel supports it: - "policy_version": "2" + "use_fs_keyring_for_v1_policies": true -... and then for each of your encrypted directories, using `fscrypt -encrypt` to encrypt a new empty directory, copying your files into it, -and replacing the original directory with it. This will fix the key -access problems, while also keeping `fscrypt unlock` and `fscrypt -lock` usable by non-root users. This is the recommended solution if -you don't need to access your files on kernels older than v5.4. +However, this fallback option makes manually unlocking and locking +encrypted directories start to require root. (The PAM module will +still work.) E.g., you'll need to run `sudo fscrypt unlock`, not +`fscrypt unlock`. Most people should just use v2 policies instead. ## Legal