aboutsummaryrefslogtreecommitdiff
path: root/actions/policy.go
diff options
context:
space:
mode:
authorJoe Richey joerichey@google.com <joerichey@google.com>2017-05-31 17:54:35 -0700
committerJoe Richey joerichey@google.com <joerichey@google.com>2017-06-26 15:40:08 -0700
commitdefd27f75df3a6eef84ac33adf89b1ce255e738c (patch)
tree851a587fb4a12381e7a29e32759636021ecaf42c /actions/policy.go
parentd71b7f248e21f5254c32ecbf752a1dbe940a1177 (diff)
actions: Simplify the callback mechanism
This commit makes the callbacks for getting keys easier to understand. Functions which need keys now take a KeyFunc callback. This callback contains a ProtectorInfo parameter (basically a read-only version of metadata.ProtectorData) and a boolean which indicates if the call is being retried. The documentation is also updated to say which functions will retry the KeyFunc. For selecting a protector, there is now an OptionFunc callback which takes a slice of ProtectorOptions. A ProtectorOption is a ProtectorInfo along with additional information about a linked filesystem (if applicable). This commit also adds in methods for getting the protector options for a specific filesystem or policy. It also adds a function for getting the policy descriptor for a specific path. Change-Id: I41e0d94ffd44e7166b0c5cf1b5d18437960bdf90
Diffstat (limited to 'actions/policy.go')
-rw-r--r--actions/policy.go291
1 files changed, 167 insertions, 124 deletions
diff --git a/actions/policy.go b/actions/policy.go
index cc1a92c..678bcdc 100644
--- a/actions/policy.go
+++ b/actions/policy.go
@@ -32,34 +32,144 @@ import (
// Errors relating to Policies
var (
- ErrBadPolicyMetadata = util.SystemError("policy metadata is inconsistent")
- ErrPathWrongFilesystem = errors.New("provided path for policy is on the wrong filesystem")
- 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("this policy is already protected by this protector")
- ErrNotProtected = errors.New("this policy is not protected by this protector")
- ErrInvalidIndex = errors.New("policy callback returned an invalid index")
+ ErrMissingPolicyMetadata = util.SystemError("policy for directory has no filesystem metadata; metadata may be corrupted")
+ ErrPolicyMetadataMismatch = util.SystemError("policy metadata is inconsistent; metadata may be corrupted")
+ ErrPathWrongFilesystem = errors.New("provided path for policy is on the wrong filesystem")
+ 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("this policy is already protected by this protector")
+ ErrNotProtected = errors.New("this policy is not protected by this protector")
)
+// PolicyDescriptorForPath returns the policy descriptor for a file on the
+// filesystem. An error is returned if the metadata is inconsistent, the path is
+// for the wrong filesystem, or the path is not encrypted.
+func PolicyDescriptorForPath(ctx *Context, path string) (string, error) {
+ if err := ctx.checkContext(); err != nil {
+ return "", err
+ }
+ // Policies and their paths will always be on the same filesystem
+ if pathMount, err := filesystem.FindMount(path); err != nil {
+ return "", err
+ } else if pathMount != ctx.Mount {
+ return "", ErrPathWrongFilesystem
+ }
+ log.Printf("%q is on mountpoint %q", path, ctx.Mount.Path)
+
+ // We double check that the options agree for both the data we get from
+ // the path, and the data we get from the mountpoint.
+ pathData, err := metadata.GetPolicy(path)
+ if err != nil {
+ return "", err
+ }
+ descriptor := pathData.KeyDescriptor
+ log.Printf("found policy %s for %q", descriptor, path)
+
+ mountData, err := ctx.Mount.GetPolicy(descriptor)
+ if err != nil {
+ log.Printf("getting metadata for policy %s: %v", descriptor, err)
+ return "", ErrMissingPolicyMetadata
+ }
+ log.Printf("found data for policy %s on %q", descriptor, ctx.Mount.Path)
+
+ if !reflect.DeepEqual(pathData.Options, mountData.Options) {
+ log.Printf("options from path: %+v", pathData.Options)
+ log.Printf("options from mount: %+v", mountData.Options)
+ return "", ErrPolicyMetadataMismatch
+ }
+ log.Print("data from filesystem and path agree")
+
+ return descriptor, nil
+}
+
+// IsPolicyUnlocked returns a boolean indicating if the corresponding policy for
+// this filesystem has its key in the keyring, meaning files and directories
+// using this policy can be read and written.
+func IsPolicyUnlocked(ctx *Context, policyDescriptor string) bool {
+ _, err := crypto.FindPolicyKey(policyDescriptor, getService(ctx))
+ return err == nil
+}
+
+// LockPolicy removes a policy key from the keyring. This means after unmounting
+// and remounting the directory, files and directories using this policy will be
+// inaccessible.
+func LockPolicy(ctx *Context, policyDescriptor string) error {
+ if err := ctx.checkContext(); err != nil {
+ return err
+ }
+ return crypto.RemovePolicyKey(policyDescriptor, getService(ctx))
+}
+
+// PurgeAllPolicies removes all policy keys on the filesystem from the kernel
+// keyring. In order for this removal to have an effect, the filesystem should
+// also be unmounted.
+func PurgeAllPolicies(ctx *Context) error {
+ if err := ctx.checkContext(); err != nil {
+ return err
+ }
+ policies, err := ctx.Mount.ListPolicies()
+ if err != nil {
+ return err
+ }
+
+ for _, policy := range policies {
+ if err := LockPolicy(ctx, policy); err == crypto.ErrKeyringDelete {
+ // This means a policy key was present but we could not
+ // delete it. The other errors just indicate that the
+ // policy key was not present.
+ return err
+ }
+ }
+ return nil
+}
+
+// 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
+// (which are necessary for kernels before v4.8).
+func getService(ctx *Context) string {
+ if ctx.Config.HasCompatibilityOption(LegacyConfig) {
+ switch ctx.Mount.Filesystem {
+ case "ext4", "f2fs":
+ return ctx.Mount.Filesystem + ":"
+ }
+ }
+ return crypto.DefaultService
+}
+
+// getPolicyData creates a partially constructed policy by looking up
+// the descriptor on the appropriate filesystem. The policy returned will not
+// have its key initialized.
+func getPolicyData(ctx *Context, descriptor string) (*Policy, error) {
+ if err := ctx.checkContext(); err != nil {
+ return nil, err
+ }
+ data, err := ctx.Mount.GetPolicy(descriptor)
+ if err != nil {
+ return nil, err
+ }
+ log.Printf("got data for %s from %q", descriptor, ctx.Mount.Path)
+
+ return &Policy{Context: ctx, data: data}, nil
+}
+
// Policy represents an unlocked policy, so it contains the PolicyData as well
// as the actual protector key. These unlocked Polices can then be applied to a
// directory, or have their key material inserted into the keyring (which will
// allow encrypted files to be accessed). As with the key struct, a Policy
// should be wiped after use.
type Policy struct {
- *Context
- data *metadata.PolicyData
- key *crypto.Key
+ Context *Context
+ data *metadata.PolicyData
+ key *crypto.Key
}
-// NewPolicy creates a Policy protected by given Protector and stores the
+// CreatePolicy creates a Policy protected by given Protector and stores the
// appropriate data on the filesystem. On error, no data is changed on the
// filesystem.
-func (ctx *Context) NewPolicy(protector *Protector) (*Policy, error) {
- if !ctx.Config.IsValid() {
- return nil, ErrBadConfig
+func CreatePolicy(ctx *Context, protector *Protector) (*Policy, error) {
+ if err := ctx.checkContext(); err != nil {
+ return nil, err
}
-
// Randomly create the underlying policy key (and wipe if we fail)
key, err := crypto.NewRandomKey(metadata.PolicyKeyLen)
if err != nil {
@@ -83,107 +193,52 @@ func (ctx *Context) NewPolicy(protector *Protector) (*Policy, error) {
return policy, nil
}
-// getPolicyData creates a partially constructed policy by looking up
-// the descriptor on the appropriate filesystem. The policy returned will not
-// have its key initialized.
-func (ctx *Context) getPolicyData(descriptor string) (*Policy, error) {
- data, err := ctx.Mount.GetPolicy(descriptor)
+// GetPolicy retrieves a policy with a specific descriptor. As a Protector is
+// needed to unlock the policy, callbacks to select the policy and get the key
+// are needed. This method will retry the keyFn as necessary to get the correct
+// key for the selected protector.
+func GetPolicy(ctx *Context, descriptor string, optionFn OptionFunc, keyFn KeyFunc) (*Policy, error) {
+ policy, err := getPolicyData(ctx, descriptor)
if err != nil {
return nil, err
}
- log.Printf("got data for %s from filesystem", descriptor)
-
- return &Policy{Context: ctx, data: data}, nil
+ return policy, policy.unwrapPolicy(optionFn, keyFn)
}
-// GetPolicyFromDescriptor retrieves a policy with a specific descriptor. As a
-// Protector is needed to unlock the policy, callbacks are necessary as well.
-func (ctx *Context) GetPolicyFromDescriptor(descriptor string, c1 PolicyCallback, c2 KeyCallback) (*Policy, error) {
- if !ctx.Config.IsValid() {
- return nil, ErrBadConfig
+// listOptions creates a slice of ProtectorOptions for the protectors protecting
+// this policy.
+func (policy *Policy) listOptions() []*ProtectorOption {
+ options := make([]*ProtectorOption, len(policy.data.WrappedPolicyKeys))
+ for i, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
+ options[i] = policy.Context.GetProtectorOption(wrappedPolicyKey.ProtectorDescriptor)
}
-
- policy, err := ctx.getPolicyData(descriptor)
- if err != nil {
- return nil, err
- }
-
- return policy, policy.unwrapPolicy(c1, c2)
-}
-
-// GetPolicyFromPath returns the policy for a specific path on the same
-// filesystem as the Context. As a Protector is needed to unlock the policy,
-// callbacks are necessary as well.
-func (ctx *Context) GetPolicyFromPath(path string, c1 PolicyCallback, c2 KeyCallback) (*Policy, error) {
- if !ctx.Config.IsValid() {
- return nil, ErrBadConfig
- }
-
- // Policies and their paths will always be on the same filesystem
- if pathMount, err := filesystem.FindMount(path); err != nil {
- return nil, err
- } else if pathMount != ctx.Mount {
- return nil, ErrPathWrongFilesystem
- }
- log.Printf("using mountpoint %q for %q", ctx.Mount.Path, path)
-
- // We double check that the options agree for both the data we get from
- // the path, and the data we get from the mountpoint.
- pathData, err := metadata.GetPolicy(path)
- if err != nil {
- return nil, err
- }
- log.Printf("found policy %s for %s", pathData.KeyDescriptor, path)
-
- policy, err := ctx.getPolicyData(pathData.KeyDescriptor)
- if err != nil {
- return nil, err
- }
-
- if !reflect.DeepEqual(pathData.Options, policy.data.Options) {
- log.Printf("options from path: %+v", pathData.Options)
- log.Printf("options from mountpoint: %+v", policy.data.Options)
- return nil, ErrBadPolicyMetadata
- }
- log.Print("data from filesystem and directory agree")
-
- return policy, policy.unwrapPolicy(c1, c2)
+ return options
}
// unwrapPolicy initializes the policy key using the provided callbacks.
-// The policyCallback
-func (policy *Policy) unwrapPolicy(policyCallback PolicyCallback, keyCallback KeyCallback) error {
- // Create a list of the ProtectorData structures and a corresponding
- // list of the wrapped keys.
- totalKeys := len(policy.data.WrappedPolicyKeys)
- protectors := make([]ProtectorData, 0, totalKeys)
- wrappedKeys := make([]*metadata.WrappedKeyData, 0, totalKeys)
-
- // This loop excludes protectors that we cannot get from the mount.
- for _, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
- protector, err := policy.Mount.GetEitherProtector(wrappedPolicyKey.ProtectorDescriptor)
- if err != nil {
- log.Print(err)
- continue
- }
- protectors = append(protectors, protector)
- wrappedKeys = append(wrappedKeys, wrappedPolicyKey.WrappedKey)
+func (policy *Policy) unwrapPolicy(optionFn OptionFunc, keyFn KeyFunc) error {
+ // Create a list of the ProtectorOptions and a list of the wrapped keys.
+ options := policy.listOptions()
+ wrappedKeys := make([]*metadata.WrappedKeyData, len(policy.data.WrappedPolicyKeys))
+
+ for i, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
+ wrappedKeys[i] = wrappedPolicyKey.WrappedKey
}
- log.Printf("%d of our %d protectors are available", len(protectors), totalKeys)
- idx, err := policyCallback(policy.data.KeyDescriptor, protectors)
+ // The OptionFunc indicates which option and wrapped key we should use.
+ idx, err := optionFn(policy.data.KeyDescriptor, options)
if err != nil {
return err
}
- if idx < 0 || idx >= len(protectors) {
- return ErrInvalidIndex
+ option := options[idx]
+ if option.LoadError != nil {
+ return option.LoadError
}
- protectorData := protectors[idx].(*metadata.ProtectorData)
wrappedPolicyKey := wrappedKeys[idx]
- log.Printf("protector %s selected in callback", protectorData.ProtectorDescriptor)
+ log.Printf("protector %s selected in callback", option.Descriptor())
- protectorKey, err := unwrapProtectorKey(protectorData, keyCallback)
+ protectorKey, err := unwrapProtectorKey(option.ProtectorInfo, keyFn)
if err != nil {
return err
}
@@ -199,16 +254,16 @@ func (policy *Policy) unwrapPolicy(policyCallback PolicyCallback, keyCallback Ke
// returned, no data has been changed. If the policy and protector are on
// different filesystems, a link will be created between them.
func (policy *Policy) AddProtector(protector *Protector) error {
- _, err := policy.findWrappedKeyIndex(protector)
+ _, err := policy.findWrappedKeyIndex(protector.data.ProtectorDescriptor)
if err == nil {
return ErrAlreadyProtected
}
// If the protector is on a different filesystem, we need to add a link
// to it on the policy's filesystem.
- if policy.Mount != protector.Mount {
- err = policy.Mount.AddLinkedProtector(
- protector.data.ProtectorDescriptor, protector.Mount)
+ if policy.Context.Mount != protector.Context.Mount {
+ err = policy.Context.Mount.AddLinkedProtector(
+ protector.data.ProtectorDescriptor, protector.Context.Mount)
if err != nil {
return err
}
@@ -218,7 +273,6 @@ func (policy *Policy) AddProtector(protector *Protector) error {
wrappedPolicyKey := &metadata.WrappedPolicyKey{
ProtectorDescriptor: protector.data.ProtectorDescriptor,
}
-
if wrappedPolicyKey.WrappedKey, err = crypto.Wrap(protector.key, policy.key); err != nil {
return err
}
@@ -239,8 +293,8 @@ func (policy *Policy) AddProtector(protector *Protector) error {
// is returned, no data has been changed. Note that w do not attempt to remove
// any links (for the case where the protector and policy are on different
// filesystems). This is because one protector may protect many polices.
-func (policy *Policy) RemoveProtector(protector *Protector) error {
- idx, err := policy.findWrappedKeyIndex(protector)
+func (policy *Policy) RemoveProtector(protectorDescriptor string) error {
+ idx, err := policy.findWrappedKeyIndex(protectorDescriptor)
if err != nil {
return err
}
@@ -266,7 +320,7 @@ func (policy *Policy) RemoveProtector(protector *Protector) error {
func (policy *Policy) Apply(path string) error {
if pathMount, err := filesystem.FindMount(path); err != nil {
return err
- } else if pathMount != policy.Mount {
+ } else if pathMount != policy.Context.Mount {
return ErrDifferentFilesystem
}
@@ -276,22 +330,11 @@ func (policy *Policy) Apply(path string) error {
// Unlock provisions the Policy key into the kernel keyring. This allows reading
// and writing of files encrypted with this directory.
func (policy *Policy) Unlock() error {
- service := crypto.ServiceDefault
-
- // For legacy configurations, we may need non-standard services
- if policy.Config.HasCompatibilityOption(LegacyConfig) {
- switch policy.Mount.Filesystem {
- case "ext4":
- service = crypto.ServiceExt4
- case "f2fs":
- service = crypto.ServiceF2FS
- }
- }
-
- return crypto.InsertPolicyKey(policy.key, policy.data.KeyDescriptor, service)
+ return crypto.InsertPolicyKey(policy.key, policy.data.KeyDescriptor, getService(policy.Context))
}
-// Wipe wipes a Policy's internal Key.
+// Wipe wipes a Policy's internal Key. It should always be called after using a
+// Policy. This is often done with a defer statement.
func (policy *Policy) Wipe() error {
return policy.key.Wipe()
}
@@ -299,20 +342,20 @@ func (policy *Policy) Wipe() error {
// Destroy removes a policy from the filesystem. The internal key should still
// be wiped with Wipe().
func (policy *Policy) Destroy() error {
- return policy.Mount.RemovePolicy(policy.data.KeyDescriptor)
+ return policy.Context.Mount.RemovePolicy(policy.data.KeyDescriptor)
}
// commitData writes the Policy's current data to the filesystem
func (policy *Policy) commitData() error {
- return policy.Mount.AddPolicy(policy.data)
+ return policy.Context.Mount.AddPolicy(policy.data)
}
// findWrappedPolicyKey returns the index of the wrapped policy key
// corresponding to this policy and protector. An error is returned if no
// wrapped policy key corresponds to the specified protector.
-func (policy *Policy) findWrappedKeyIndex(protector *Protector) (int, error) {
+func (policy *Policy) findWrappedKeyIndex(protectorDescriptor string) (int, error) {
for idx, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
- if wrappedPolicyKey.ProtectorDescriptor == protector.data.ProtectorDescriptor {
+ if wrappedPolicyKey.ProtectorDescriptor == protectorDescriptor {
return idx, nil
}
}