diff options
| -rw-r--r-- | actions/policy.go | 11 | ||||
| -rw-r--r-- | cmd/fscrypt/errors.go | 7 | ||||
| -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 | 4 | ||||
| -rw-r--r-- | keyring/keyring_test.go | 2 |
7 files changed, 56 insertions, 5 deletions
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/errors.go b/cmd/fscrypt/errors.go index c242552..8bda921 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 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 e232de3..6623943 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 2208105..26f6036 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 |