diff options
| -rw-r--r-- | README.md | 149 | ||||
| -rw-r--r-- | actions/config.go | 20 | ||||
| -rw-r--r-- | actions/config_test.go | 26 | ||||
| -rw-r--r-- | actions/context.go | 19 | ||||
| -rw-r--r-- | actions/context_test.go | 2 | ||||
| -rw-r--r-- | actions/policy.go | 11 | ||||
| -rw-r--r-- | cmd/fscrypt/commands.go | 2 | ||||
| -rw-r--r-- | cmd/fscrypt/errors.go | 11 | ||||
| -rw-r--r-- | cmd/fscrypt/flags.go | 8 | ||||
| -rw-r--r-- | cmd/fscrypt/format.go | 7 | ||||
| -rw-r--r-- | cmd/fscrypt/setup.go | 16 | ||||
| -rw-r--r-- | filesystem/path.go | 6 | ||||
| -rw-r--r-- | filesystem/path_test.go | 27 | ||||
| -rw-r--r-- | keyring/fs_keyring.go | 4 | ||||
| -rw-r--r-- | keyring/keyring.go | 68 | ||||
| -rw-r--r-- | keyring/keyring_test.go | 28 | ||||
| -rw-r--r-- | metadata/config.go | 14 | ||||
| -rw-r--r-- | metadata/config_test.go | 8 | ||||
| -rw-r--r-- | metadata/metadata.pb.go | 124 | ||||
| -rw-r--r-- | metadata/metadata.proto | 5 | ||||
| -rw-r--r-- | util/util.go | 23 | ||||
| -rw-r--r-- | util/util_test.go | 7 |
22 files changed, 342 insertions, 243 deletions
@@ -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 to use Linux kernel 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! @@ -195,12 +199,11 @@ that looks like the following: "memory": "131072", "parallelism": "32" }, - "compatibility": "legacy", "options": { "padding": "32", "contents": "AES_256_XTS", "filenames": "AES_256_CTS", - "policy_version": "1" + "policy_version": "2" }, "use_fs_keyring_for_v1_policies": false } @@ -216,9 +219,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: @@ -245,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 @@ -366,6 +367,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". @@ -394,8 +396,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 @@ -413,24 +415,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: @@ -450,8 +451,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: @@ -463,7 +464,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 ``` @@ -486,8 +487,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: @@ -500,9 +501,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) @@ -522,8 +523,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: @@ -577,9 +578,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 @@ -611,31 +612,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: @@ -653,16 +654,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 @@ -777,27 +778,49 @@ 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, enable v2 policies for new directories by +setting `"policy_version": "2"` in `/etc/fscrypt.conf`. For example: + +``` + "options": { + "padding": "32", + "contents": "AES_256_XTS", + "filenames": "AES_256_CTS", + "policy_version": "2" + }, +``` - "use_fs_keyring_for_v1_policies": true +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, you can enable a +fallback option to make `fscrypt` use the filesystem keyring for v1 +policies: - "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. +This fallback option only has an effect if the kernel supports using +the filesystem keyring. This option is also useful if you simply +don't want to re-create your old, v1 directories. However, this +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 diff --git a/actions/config.go b/actions/config.go index 6b019df..2463b95 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,10 @@ 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. 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, @@ -83,9 +77,9 @@ 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 policyVersion != 0 { + config.Options.PolicyVersion = policyVersion } if config.HashCosts, err = getHashingCosts(target); err != nil { diff --git a/actions/config_test.go b/actions/config_test.go index 037e433..3599667 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, false); 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.go b/actions/context.go index f07f225..0db0671 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 e8aefd7..4488a6b 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, 0); err != nil { return nil, err } defer func() { diff --git a/actions/policy.go b/actions/policy.go index b7fe5a6..3baad72 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,15 @@ 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 os.IsPermission(err) && + 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/commands.go b/cmd/fscrypt/commands.go index 4a59d30..f84102e 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/errors.go b/cmd/fscrypt/errors.go index bef6c2a..8bda921 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: @@ -127,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/cmd/fscrypt/flags.go b/cmd/fscrypt/flags.go index ce2f30e..9679a8d 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 48a5a86..cc268aa 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 69787bb..7b9bebb 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, legacyFlag.Value) + err = actions.CreateConfigFile(timeTargetFlag.Value, policyVersion) if err != nil { return err } diff --git a/filesystem/path.go b/filesystem/path.go index b9b403d..274dc0a 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 eef5ce3..4152037 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 42c1648..f0016a4 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 5a75153..6623943 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 @@ -62,9 +63,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 @@ -72,16 +70,32 @@ 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 +// "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 { + switch options.Mount.FilesystemType { + case "ext4", "f2fs": + return options.Mount.FilesystemType + ":" + descriptor + default: + return unix.FSCRYPT_KEY_DESC_PREFIX + descriptor + } } // AddEncryptionKey adds an encryption policy key to a kernel keyring. It uses @@ -91,24 +105,32 @@ 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, options.Service+descriptor, options.User) + return userAddKey(key, buildKeyDescription(options, descriptor), options.User) } // RemoveEncryptionKey removes an encryption policy key from a kernel keyring. // 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 } 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. @@ -144,10 +166,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(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 8912556..26f6036 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) @@ -84,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 @@ -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 0f95fbe..b3c8726 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 83c1eb0..52f83f2 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 e6067f9..a2148ce 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 81b3bf9..8ffb4f6 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"; } diff --git a/util/util.go b/util/util.go index 97ee33c..d97a7ae 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 7739edd..70e7070 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") + } +} |