aboutsummaryrefslogtreecommitdiff
path: root/actions/policy.go
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2020-05-09 14:52:07 -0700
committerEric Biggers <ebiggers@google.com>2020-05-09 15:21:31 -0700
commit209a2d1419ea575fd316bd9975fb63e40cce7a77 (patch)
tree30d6b308d60af9963f7dbfd0bf989b7728d3f2b2 /actions/policy.go
parent37457cce5b0436493dba7cdac6e1af5f51d25f47 (diff)
actions/policy: improve errors
ErrMissingPolicyMetadata: Include the mount, directory path, and metadata path. Also move the explanation into actions/ since it doesn't refer to any CLI command. ErrPolicyMetadataMismatch: Include a lot more information. Also start checking for consistency of the policy key descriptors, not just the encryption options. Add a test for this. ErrDifferentFilesystem: Include the mountpoints. ErrOnlyProtector: Clarify the message and include the protector descriptor. ErrAlreadyProtected: ErrNotProtected: Include the policy and protector descriptors. ErrAccessDeniedPossiblyV2: Make it slightly clearer what failed. Also move the explanation into actions/ since it doesn't refer to any CLI command.
Diffstat (limited to 'actions/policy.go')
-rw-r--r--actions/policy.go134
1 files changed, 113 insertions, 21 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)