aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/policy.go134
-rw-r--r--cli-tests/t_unlock.out38
-rwxr-xr-xcli-tests/t_unlock.sh13
-rw-r--r--cmd/fscrypt/errors.go15
-rw-r--r--cmd/fscrypt/prompt.go3
-rw-r--r--filesystem/filesystem.go10
6 files changed, 161 insertions, 52 deletions
diff --git a/actions/policy.go b/actions/policy.go
index 6c2aa51..a5fd481 100644
--- a/actions/policy.go
+++ b/actions/policy.go
@@ -34,16 +34,109 @@ import (
"github.com/google/fscrypt/util"
)
-// Errors relating to Policies
-var (
- ErrMissingPolicyMetadata = util.SystemError("missing policy metadata for encrypted directory")
- ErrPolicyMetadataMismatch = util.SystemError("inconsistent metadata between filesystem and directory")
- ErrDifferentFilesystem = errors.New("policies may only protect files on the same filesystem")
- 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")
-)
+// ErrAccessDeniedPossiblyV2 indicates that a directory's encryption policy
+// couldn't be retrieved due to "permission denied", but it looks like it's due
+// to the directory using a v2 policy but the kernel not supporting it.
+type ErrAccessDeniedPossiblyV2 struct {
+ DirPath string
+}
+
+func (err *ErrAccessDeniedPossiblyV2) Error() string {
+ return fmt.Sprintf(`
+ failed to get encryption policy of %s: 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 %s.`,
+ err.DirPath, ConfigFileLocation)
+}
+
+// ErrAlreadyProtected indicates that a policy is already protected by the given
+// protector.
+type ErrAlreadyProtected struct {
+ Policy *Policy
+ Protector *Protector
+}
+
+func (err *ErrAlreadyProtected) Error() string {
+ return fmt.Sprintf("policy %s is already protected by protector %s",
+ err.Policy.Descriptor(), err.Protector.Descriptor())
+}
+
+// ErrDifferentFilesystem indicates that a policy can't be applied to a
+// directory on a different filesystem.
+type ErrDifferentFilesystem struct {
+ PolicyMount *filesystem.Mount
+ PathMount *filesystem.Mount
+}
+
+func (err *ErrDifferentFilesystem) Error() string {
+ return fmt.Sprintf(`cannot apply policy from filesystem %q to a
+ directory on filesystem %q. Policies may only protect files on the same
+ filesystem.`, err.PolicyMount.Path, err.PathMount.Path)
+}
+
+// ErrMissingPolicyMetadata indicates that a directory is encrypted but its
+// policy metadata cannot be found.
+type ErrMissingPolicyMetadata struct {
+ Mount *filesystem.Mount
+ DirPath string
+ Descriptor string
+}
+
+func (err *ErrMissingPolicyMetadata) Error() string {
+ return fmt.Sprintf(`filesystem %q does not contain the policy metadata
+ for %q. This directory has either been encrypted with another tool (such
+ as e4crypt), or the file %q has been deleted.`,
+ err.Mount.Path, err.DirPath,
+ err.Mount.PolicyPath(err.Descriptor))
+}
+
+// ErrNotProtected indicates that the given policy is not protected by the given
+// protector.
+type ErrNotProtected struct {
+ PolicyDescriptor string
+ ProtectorDescriptor string
+}
+
+func (err *ErrNotProtected) Error() string {
+ return fmt.Sprintf(`policy %s is not protected by protector %s`,
+ err.PolicyDescriptor, err.ProtectorDescriptor)
+}
+
+// ErrOnlyProtector indicates that the last protector can't be removed from a
+// policy.
+type ErrOnlyProtector struct {
+ Policy *Policy
+}
+
+func (err *ErrOnlyProtector) Error() string {
+ return fmt.Sprintf(`cannot remove the only protector from policy %s. A
+ policy must have at least one protector.`, err.Policy.Descriptor())
+}
+
+// ErrPolicyMetadataMismatch indicates that the policy metadata for an encrypted
+// directory is inconsistent with that directory.
+type ErrPolicyMetadataMismatch struct {
+ DirPath string
+ Mount *filesystem.Mount
+ PathData *metadata.PolicyData
+ MountData *metadata.PolicyData
+}
+
+func (err *ErrPolicyMetadataMismatch) Error() string {
+ return fmt.Sprintf(`inconsistent metadata between encrypted directory %q
+ and its corresponding metadata file %q.
+
+ Directory has descriptor:%s %s
+
+ Metadata file has descriptor:%s %s`,
+ err.DirPath, err.Mount.PolicyPath(err.PathData.KeyDescriptor),
+ err.PathData.KeyDescriptor, err.PathData.Options,
+ err.MountData.KeyDescriptor, err.MountData.Options)
+}
// PurgeAllPolicies removes all policy keys on the filesystem from the kernel
// keyring. In order for this to fully take effect, the filesystem may also need
@@ -161,7 +254,7 @@ func GetPolicyFromPath(ctx *Context, path string) (*Policy, error) {
if os.IsPermission(err) &&
filesystem.HaveReadAccessTo(path) &&
!keyring.IsFsKeyringSupported(ctx.Mount) {
- return nil, errors.Wrapf(ErrAccessDeniedPossiblyV2, "open %s", path)
+ return nil, &ErrAccessDeniedPossiblyV2{path}
}
return nil, err
}
@@ -171,14 +264,13 @@ func GetPolicyFromPath(ctx *Context, path string) (*Policy, error) {
mountData, err := ctx.Mount.GetPolicy(descriptor)
if err != nil {
log.Printf("getting policy metadata: %v", err)
- return nil, errors.Wrap(ErrMissingPolicyMetadata, path)
+ return nil, &ErrMissingPolicyMetadata{ctx.Mount, path, descriptor}
}
log.Printf("found data for policy %s on %q", descriptor, ctx.Mount.Path)
- if !proto.Equal(pathData.Options, mountData.Options) {
- log.Printf("options from path: %+v", pathData.Options)
- log.Printf("options from mount: %+v", mountData.Options)
- return nil, errors.Wrapf(ErrPolicyMetadataMismatch, "policy %s", descriptor)
+ if !proto.Equal(pathData.Options, mountData.Options) ||
+ pathData.KeyDescriptor != mountData.KeyDescriptor {
+ return nil, &ErrPolicyMetadataMismatch{path, ctx.Mount, pathData, mountData}
}
log.Print("data from filesystem and path agree")
@@ -290,7 +382,7 @@ func (policy *Policy) UnlockWithProtector(protector *Protector) error {
}
idx, ok := policy.findWrappedKeyIndex(protector.Descriptor())
if !ok {
- return ErrNotProtected
+ return &ErrNotProtected{policy.Descriptor(), protector.Descriptor()}
}
var err error
@@ -321,7 +413,7 @@ func (policy *Policy) UsesProtector(protector *Protector) bool {
// protector must both be unlocked.
func (policy *Policy) AddProtector(protector *Protector) error {
if policy.UsesProtector(protector) {
- return ErrAlreadyProtected
+ return &ErrAlreadyProtected{policy, protector}
}
if policy.key == nil || protector.key == nil {
return ErrLocked
@@ -372,11 +464,11 @@ func (policy *Policy) AddProtector(protector *Protector) error {
func (policy *Policy) RemoveProtector(protector *Protector) error {
idx, ok := policy.findWrappedKeyIndex(protector.Descriptor())
if !ok {
- return ErrNotProtected
+ return &ErrNotProtected{policy.Descriptor(), protector.Descriptor()}
}
if len(policy.data.WrappedPolicyKeys) == 1 {
- return ErrOnlyProtector
+ return &ErrOnlyProtector{policy}
}
// Remove the wrapped key from the data
@@ -397,7 +489,7 @@ func (policy *Policy) Apply(path string) error {
if pathMount, err := filesystem.FindMount(path); err != nil {
return err
} else if pathMount != policy.Context.Mount {
- return ErrDifferentFilesystem
+ return &ErrDifferentFilesystem{policy.Context.Mount, pathMount}
}
return metadata.SetPolicy(path, policy.data)
diff --git a/cli-tests/t_unlock.out b/cli-tests/t_unlock.out
index 29a10dd..710b063 100644
--- a/cli-tests/t_unlock.out
+++ b/cli-tests/t_unlock.out
@@ -81,21 +81,39 @@ contents
desc1 Yes desc2
# Try to unlock with corrupt policy metadata
-[ERROR] fscrypt unlock: MNT/dir: system error: missing
- policy metadata for encrypted directory
-
-This file or directory has either been encrypted with another tool (such as
-e4crypt) or the corresponding filesystem metadata has been deleted.
+[ERROR] fscrypt unlock: filesystem "MNT" does not contain
+ the policy metadata for "MNT/dir".
+ This directory has either been encrypted with another
+ tool (such as e4crypt), or the file
+ "MNT/.fscrypt/policies/desc1"
+ has been deleted.
# Try to unlock with missing policy metadata
-[ERROR] fscrypt unlock: MNT/dir: system error: missing
- policy metadata for encrypted directory
-
-This file or directory has either been encrypted with another tool (such as
-e4crypt) or the corresponding filesystem metadata has been deleted.
+[ERROR] fscrypt unlock: filesystem "MNT" does not contain
+ the policy metadata for "MNT/dir".
+ This directory has either been encrypted with another
+ tool (such as e4crypt), or the file
+ "MNT/.fscrypt/policies/desc20"
+ has been deleted.
# Try to unlock with missing protector metadata
[ERROR] fscrypt unlock: could not load any protectors
You may need to mount a linked filesystem. Run with --verbose for more
information.
+
+# Try to unlock with wrong policy metadata
+[ERROR] fscrypt unlock: inconsistent metadata between encrypted directory
+ "MNT/dir1" and its corresponding
+ metadata file
+ "MNT/.fscrypt/policies/desc21".
+
+ Directory has
+ descriptor:desc21 padding:32
+ contents:AES_256_XTS filenames:AES_256_CTS
+ policy_version:2
+
+ Metadata file has
+ descriptor:desc23 padding:32
+ contents:AES_256_XTS filenames:AES_256_CTS
+ policy_version:2
diff --git a/cli-tests/t_unlock.sh b/cli-tests/t_unlock.sh
index 3dfba41..e32b0f7 100755
--- a/cli-tests/t_unlock.sh
+++ b/cli-tests/t_unlock.sh
@@ -67,3 +67,16 @@ mkdir "$dir"
echo hunter2 | fscrypt encrypt --quiet --name=prot --skip-unlock "$dir"
rm "$MNT"/.fscrypt/protectors/*
_expect_failure "echo hunter2 | fscrypt unlock '$dir'"
+
+_print_header "Try to unlock with wrong policy metadata"
+_reset_filesystems
+mkdir "$MNT/dir1"
+mkdir "$MNT/dir2"
+echo hunter2 | fscrypt encrypt --quiet --name=dir1 --skip-unlock "$MNT/dir1"
+echo hunter2 | fscrypt encrypt --quiet --name=dir2 --skip-unlock "$MNT/dir2"
+policy1=$(find "$MNT/.fscrypt/policies/" -type f | head -1)
+policy2=$(find "$MNT/.fscrypt/policies/" -type f | tail -1)
+mv "$policy1" "$TMPDIR/policy"
+mv "$policy2" "$policy1"
+mv "$TMPDIR/policy" "$policy2"
+_expect_failure "echo hunter2 | fscrypt unlock '$MNT/dir1'"
diff --git a/cmd/fscrypt/errors.go b/cmd/fscrypt/errors.go
index 829a9d2..3e5beed 100644
--- a/cmd/fscrypt/errors.go
+++ b/cmd/fscrypt/errors.go
@@ -127,21 +127,6 @@ func getErrorSuggestions(err error) string {
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.ErrMissingPolicyMetadata:
- return `This file or directory has either been encrypted with
- another tool (such as e4crypt) or the corresponding
- filesystem metadata has been deleted.`
- case actions.ErrPolicyMetadataMismatch:
- return `The metadata for this encrypted directory is in an
- inconsistent state. This most likely means the filesystem
- metadata is corrupted.`
- 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/prompt.go b/cmd/fscrypt/prompt.go
index b854fb9..210d7bc 100644
--- a/cmd/fscrypt/prompt.go
+++ b/cmd/fscrypt/prompt.go
@@ -318,7 +318,8 @@ func optionFn(policyDescriptor string, options []*actions.ProtectorOption) (int,
return idx, nil
}
}
- return 0, actions.ErrNotProtected
+ return 0, &actions.ErrNotProtected{PolicyDescriptor: policyDescriptor,
+ ProtectorDescriptor: protector.Descriptor()}
}
log.Printf("optionFn(%s)", policyDescriptor)
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index e01f9ff..eb49182 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -181,9 +181,9 @@ func (m *Mount) PolicyDir() string {
return filepath.Join(m.BaseDir(), policyDirName)
}
-// policyPath returns the full path to a regular policy file with the
+// PolicyPath returns the full path to a regular policy file with the
// specified descriptor.
-func (m *Mount) policyPath(descriptor string) string {
+func (m *Mount) PolicyPath(descriptor string) string {
return filepath.Join(m.PolicyDir(), descriptor)
}
@@ -512,7 +512,7 @@ func (m *Mount) AddPolicy(data *metadata.PolicyData) error {
return err
}
- return m.err(m.addMetadata(m.policyPath(data.KeyDescriptor), data))
+ return m.err(m.addMetadata(m.PolicyPath(data.KeyDescriptor), data))
}
// GetPolicy looks up the policy metadata by descriptor.
@@ -521,7 +521,7 @@ func (m *Mount) GetPolicy(descriptor string) (*metadata.PolicyData, error) {
return nil, err
}
data := new(metadata.PolicyData)
- return data, m.err(m.getMetadata(m.policyPath(descriptor), data))
+ return data, m.err(m.getMetadata(m.PolicyPath(descriptor), data))
}
// RemovePolicy deletes the policy metadata from the filesystem storage.
@@ -529,7 +529,7 @@ func (m *Mount) RemovePolicy(descriptor string) error {
if err := m.CheckSetup(); err != nil {
return err
}
- return m.err(m.removeMetadata(m.policyPath(descriptor)))
+ return m.err(m.removeMetadata(m.PolicyPath(descriptor)))
}
// ListPolicies lists the descriptors of all policies on this filesystem.