aboutsummaryrefslogtreecommitdiff
path: root/actions
diff options
context:
space:
mode:
authorJoe Richey joerichey@google.com <joerichey@google.com>2017-06-21 10:03:44 -0700
committerJoe Richey joerichey@google.com <joerichey@google.com>2017-06-28 15:15:15 -0700
commit93415b198a3ef427c02893b8fdf036aa75ffe50f (patch)
tree419be5fa11e9102597d3409800a3d7df4138b05e /actions
parent77b226a90ef70b77ca556830528c013a23b01e57 (diff)
actions: error handling and API changed
This commit changes the error handling for the actions package to use the error handling library github.com/pkg/errors. This means replacing "errors" with "github.com/pkg/errors", reworking some of the error values, and wrapping some errors with additional context. This commit also changes the Protector/Policy API, moving most of the package functionality into Protector or Policy methods. These types are now "locked" when they are queried from the filesystem, and Unlock() must be used to get their corresponding keys. Note that only certain operations will require unlocking the keys. Certain unnecessary functions and methods are also removed. This CL also fixes two bugs reported by Tyler Hicks in CreateConfigFile. CPU time is used instead of wall time, and kiB is used instead of kB. Change-Id: I88f45659e9fe4938d148843e3289e7b6d5b698d8
Diffstat (limited to 'actions')
-rw-r--r--actions/callback.go8
-rw-r--r--actions/config.go33
-rw-r--r--actions/context.go63
-rw-r--r--actions/context_test.go82
-rw-r--r--actions/hashing_test.go (renamed from actions/config_test.go)45
-rw-r--r--actions/policy.go334
-rw-r--r--actions/policy_test.go76
-rw-r--r--actions/protector.go125
-rw-r--r--actions/protector_test.go25
9 files changed, 421 insertions, 370 deletions
diff --git a/actions/callback.go b/actions/callback.go
index 18be670..2415d8c 100644
--- a/actions/callback.go
+++ b/actions/callback.go
@@ -21,10 +21,13 @@
package actions
import (
+ "log"
+
+ "github.com/pkg/errors"
+
"fscrypt/crypto"
"fscrypt/filesystem"
"fscrypt/metadata"
- "log"
)
// ProtectorInfo is the information a caller will receive about a Protector
@@ -91,7 +94,8 @@ func unwrapProtectorKey(info ProtectorInfo, keyFn KeyFunc) (*crypto.Key, error)
protectorKey, err := crypto.Unwrap(wrappingKey, info.data.WrappedKey)
wrappingKey.Wipe()
- switch err {
+
+ switch errors.Cause(err) {
case nil:
log.Printf("valid wrapping key for protector %s", info.Descriptor())
return protectorKey, nil
diff --git a/actions/config.go b/actions/config.go
index 2010ef1..1d81ff9 100644
--- a/actions/config.go
+++ b/actions/config.go
@@ -27,6 +27,8 @@ import (
"runtime"
"time"
+ "github.com/pkg/errors"
+
"golang.org/x/sys/unix"
"fscrypt/crypto"
@@ -68,7 +70,7 @@ func CreateConfigFile(target time.Duration, useLegacy bool) error {
case os.IsExist(err):
return ErrConfigFileExists
case err != nil:
- return util.UnderlyingError(err)
+ return err
}
defer configFile.Close()
@@ -99,15 +101,14 @@ func getConfig() (*metadata.Config, error) {
case os.IsNotExist(err):
return nil, ErrNoConfigFile
case err != nil:
- return nil, util.UnderlyingError(err)
+ return nil, err
}
defer configFile.Close()
log.Printf("Reading config from %q\n", ConfigFileLocation)
config, err := metadata.ReadConfig(configFile)
if err != nil {
- log.Printf("ReadConfig() = %v", err)
- return nil, ErrBadConfigFile
+ return nil, errors.Wrap(ErrBadConfigFile, err.Error())
}
// Use system defaults if not specified
@@ -128,8 +129,8 @@ func getConfig() (*metadata.Config, error) {
log.Printf("Falling back to filenames mode of %q", config.Options.Filenames)
}
- if !config.IsValid() {
- return nil, ErrBadConfigFile
+ if err := config.CheckValidity(); err != nil {
+ return nil, errors.Wrap(ErrBadConfigFile, err.Error())
}
return config, nil
@@ -203,8 +204,8 @@ func ramLimit() int64 {
err := unix.Sysinfo(&info)
// The sysinfo syscall only fails if given a bad address
util.NeverError(err)
- // Use half the RAM and convert to kB.
- return int64(info.Totalram / 1000 / 2)
+ // Use half the RAM and convert to kiB.
+ return int64(info.Totalram / 1024 / 2)
}
// betweenCosts returns a cost between a and b. Specifically, it returns the
@@ -222,11 +223,23 @@ func timeHashingCosts(costs *metadata.HashingCosts) (time.Duration, error) {
}
defer passphrase.Wipe()
- start := time.Now()
+ // Be sure to measure CPU time, not wall time (time.Now)
+ begin := cpuTimeInNanoseconds()
hash, err := crypto.PassphraseHash(passphrase, timingSalt, costs)
if err == nil {
hash.Wipe()
}
+ end := cpuTimeInNanoseconds()
- return time.Since(start), err
+ return time.Duration(end - begin), nil
+}
+
+// cpuTimeInNanoseconds returns the nanosecond count based on the process's CPU usage.
+// This number has no absolute meaning, only relative meaning to other calls.
+func cpuTimeInNanoseconds() int64 {
+ var ts unix.Timespec
+ err := unix.ClockGettime(unix.CLOCK_PROCESS_CPUTIME_ID, &ts)
+ // ClockGettime fails if given a bad address or on a VERY old system.
+ util.NeverError(err)
+ return unix.TimespecToNsec(ts)
}
diff --git a/actions/context.go b/actions/context.go
index 4d7d30d..f8d0a3d 100644
--- a/actions/context.go
+++ b/actions/context.go
@@ -29,21 +29,22 @@
package actions
import (
- "errors"
- "fmt"
"log"
+ "github.com/pkg/errors"
+
+ "fscrypt/crypto"
"fscrypt/filesystem"
"fscrypt/metadata"
- "fscrypt/util"
)
// Errors relating to Config files or Config structures.
var (
- ErrNoConfigFile = fmt.Errorf("config file %q does not exist", ConfigFileLocation)
- ErrBadConfigFile = fmt.Errorf("config file %q has invalid data", ConfigFileLocation)
- ErrConfigFileExists = fmt.Errorf("config file %q already exists", ConfigFileLocation)
+ ErrNoConfigFile = errors.New("global config file does not exist")
+ ErrBadConfigFile = errors.New("global config file has invalid data")
+ ErrConfigFileExists = errors.New("global config file already exists")
ErrBadConfig = errors.New("invalid Config structure provided")
+ ErrLocked = errors.New("method needs a call to Unlock() first")
)
// Context contains the necessary global state to perform most of fscrypt's
@@ -61,12 +62,9 @@ type Context struct {
// success, the Context contains a valid Config and Mount.
func NewContextFromPath(path string) (ctx *Context, err error) {
ctx = new(Context)
-
if ctx.Mount, err = filesystem.FindMount(path); err != nil {
- err = util.UnderlyingError(err)
return
}
-
if ctx.Config, err = getConfig(); err != nil {
return
}
@@ -81,12 +79,9 @@ func NewContextFromPath(path string) (ctx *Context, err error) {
// success, the Context contains a valid Config and Mount.
func NewContextFromMountpoint(mountpoint string) (ctx *Context, err error) {
ctx = new(Context)
-
if ctx.Mount, err = filesystem.GetMount(mountpoint); err != nil {
- err = util.UnderlyingError(err)
return
}
-
if ctx.Config, err = getConfig(); err != nil {
return
}
@@ -99,15 +94,29 @@ func NewContextFromMountpoint(mountpoint string) (ctx *Context, err error) {
// checkContext verifies that the context contains an valid config and a mount
// which is being used with fscrypt.
func (ctx *Context) checkContext() error {
- if !ctx.Config.IsValid() {
- return ErrBadConfig
+ if err := ctx.Config.CheckValidity(); err != nil {
+ return errors.Wrap(ErrBadConfig, err.Error())
}
return ctx.Mount.CheckSetup()
}
-// GetProtectorOption returns the ProtectorOption for the protector on the
+// 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 (ctx *Context) getService() string {
+ // For legacy configurations, we may need non-standard services
+ if ctx.Config.HasCompatibilityOption(LegacyConfig) {
+ switch ctx.Mount.Filesystem {
+ case "ext4", "f2fs":
+ return ctx.Mount.Filesystem + ":"
+ }
+ }
+ return crypto.DefaultService
+}
+
+// getProtectorOption returns the ProtectorOption for the protector on the
// context's mountpoint with the specified descriptor.
-func (ctx *Context) GetProtectorOption(protectorDescriptor string) *ProtectorOption {
+func (ctx *Context) getProtectorOption(protectorDescriptor string) *ProtectorOption {
mnt, data, err := ctx.Mount.GetProtector(protectorDescriptor)
if err != nil {
return &ProtectorOption{ProtectorInfo{}, nil, err}
@@ -121,9 +130,12 @@ func (ctx *Context) GetProtectorOption(protectorDescriptor string) *ProtectorOpt
return &ProtectorOption{info, mnt, nil}
}
-// ListProtectorOptions creates a slice of all the options for all of the
-// Protectors on the Context's mountpoint.
-func (ctx *Context) ListProtectorOptions() ([]*ProtectorOption, error) {
+// ProtectorOptions creates a slice of all the options for all of the Protectors
+// on the Context's mountpoint.
+func (ctx *Context) ProtectorOptions() ([]*ProtectorOption, error) {
+ if err := ctx.checkContext(); err != nil {
+ return nil, err
+ }
descriptors, err := ctx.Mount.ListProtectors()
if err != nil {
return nil, err
@@ -131,18 +143,7 @@ func (ctx *Context) ListProtectorOptions() ([]*ProtectorOption, error) {
options := make([]*ProtectorOption, len(descriptors))
for i, descriptor := range descriptors {
- options[i] = ctx.GetProtectorOption(descriptor)
+ options[i] = ctx.getProtectorOption(descriptor)
}
return options, nil
}
-
-// ListOptionsForPolicy creates a slice of the ProtectorOptions which protect
-// the policy specified by policyDescriptor.
-func (ctx *Context) ListOptionsForPolicy(policyDescriptor string) ([]*ProtectorOption, error) {
- policy, err := getPolicyData(ctx, policyDescriptor)
- if err != nil {
- return nil, err
- }
-
- return policy.listOptions(), nil
-}
diff --git a/actions/context_test.go b/actions/context_test.go
index 74629a3..79adedf 100644
--- a/actions/context_test.go
+++ b/actions/context_test.go
@@ -20,57 +20,77 @@
package actions
import (
+ "fmt"
+ "fscrypt/util"
+ "log"
"os"
+ "path/filepath"
"testing"
-
- "fscrypt/filesystem"
+ "time"
)
-var mountpoint = os.Getenv("TEST_FILESYSTEM_ROOT")
+const testTime = 10 * time.Millisecond
+
+// holds the context we will use throughout the actions tests
+var testContext *Context
// Makes a context using the testing locations for the filesystem and
// configuration file.
-func makeContext() (*Context, error) {
- if err := CreateConfigFile(testTime, true); err != nil {
+func setupContext() (ctx *Context, err error) {
+ mountpoint, err := util.TestPath()
+ if err != nil {
return nil, err
}
- mnt := filesystem.Mount{Path: mountpoint}
- if err := mnt.Setup(); err != nil {
- return nil, err
- }
+ ConfigFileLocation = filepath.Join(mountpoint, "test.conf")
- return NewContextFromMountpoint(mountpoint)
-}
+ // Should not be able to setup without a config file
+ if badCtx, badCtxErr := NewContextFromMountpoint(mountpoint); badCtxErr == nil {
+ badCtx.Mount.RemoveAllMetadata()
+ return nil, fmt.Errorf("created context at %q without config file", badCtx.Mount.Path)
+ }
-// Cleans up the testing config file and testing filesystem data.
-func cleaupContext() {
- os.RemoveAll(ConfigFileLocation)
- mnt := filesystem.Mount{Path: mountpoint}
- mnt.RemoveAllMetadata()
-}
+ if err = CreateConfigFile(testTime, true); err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err != nil {
+ os.RemoveAll(ConfigFileLocation)
+ }
+ }()
-// Tests that we can create a context
-func TestSetupContext(t *testing.T) {
- _, err := makeContext()
- defer cleaupContext()
+ ctx, err = NewContextFromMountpoint(mountpoint)
if err != nil {
- t.Fatal(err)
+ return nil, err
}
+ return ctx, ctx.Mount.Setup()
}
-// Tests that we cannot create a context without a config file.
-func TestNoConfigFile(t *testing.T) {
- mnt := filesystem.Mount{Path: mountpoint}
- if err := mnt.Setup(); err != nil {
- t.Fatal(err)
+// Cleans up the testing config file and testing filesystem data.
+func cleaupContext(ctx *Context) error {
+ err1 := os.RemoveAll(ConfigFileLocation)
+ err2 := ctx.Mount.RemoveAllMetadata()
+ if err1 != nil {
+ return err1
}
+ return err2
+}
- _, err := NewContextFromMountpoint(mountpoint)
- defer cleaupContext()
+func TestMain(m *testing.M) {
+ log.SetFlags(log.LstdFlags | log.Lmicroseconds)
+ var err error
+ testContext, err = setupContext()
+ if err != nil {
+ fmt.Printf("setupContext() = %v\n", err)
+ os.Exit(1)
+ }
- if err == nil {
- t.Error("should not be able to create context without config file")
+ returnCode := m.Run()
+ err = cleaupContext(testContext)
+ if err != nil {
+ fmt.Printf("cleanupContext() = %v\n", err)
+ os.Exit(1)
}
+ os.Exit(returnCode)
}
diff --git a/actions/config_test.go b/actions/hashing_test.go
index c0b2089..e3cffb6 100644
--- a/actions/config_test.go
+++ b/actions/hashing_test.go
@@ -1,5 +1,5 @@
/*
- * config_test.go - tests for setting up the config file
+ * hashing_test.go - tests for computing and benchmarking hashing costs
*
* Copyright 2017 Google Inc.
* Author: Joe Richey (joerichey@google.com)
@@ -22,43 +22,17 @@ package actions
import (
"io/ioutil"
"log"
- "os"
"testing"
"time"
)
-const testTime = 10 * time.Millisecond
-
-func init() {
- // All our testing uses an alternative config file location, so we don't
- // need root to run the tests
- ConfigFileLocation = "fscrypt_test.conf"
-}
-
-// Tests that we can make the config files with and without legacy settings
-func TestMakeConfig(t *testing.T) {
- defer os.RemoveAll(ConfigFileLocation)
-
- err := CreateConfigFile(testTime, true)
- if err != nil {
- t.Error(err)
- }
- os.RemoveAll(ConfigFileLocation)
-
- err = CreateConfigFile(testTime, false)
- if err != nil {
- t.Error(err)
- }
-}
-
// Tests that we can find valid hashing costs for various time targets and the
// estimations are somewhat close to the targets.
func TestCostsSearch(t *testing.T) {
for _, target := range []time.Duration{
- 100 * time.Microsecond,
- 1 * time.Millisecond,
- 10 * time.Millisecond,
+ 20 * time.Millisecond,
100 * time.Millisecond,
+ 500 * time.Millisecond,
} {
costs, err := getHashingCosts(target)
if err != nil {
@@ -69,14 +43,11 @@ func TestCostsSearch(t *testing.T) {
t.Error(err)
}
- // Timing tests are only reliable for sufficiently long targets.
- if target > time.Millisecond {
- if actual*2 < target {
- t.Errorf("actual=%v is too small (target=%v)", actual, target)
- }
- if target*2 < actual {
- t.Errorf("actual=%v is too big (target=%v)", actual, target)
- }
+ if actual*2 < target {
+ t.Errorf("actual=%v is too small (target=%v)", actual, target)
+ }
+ if target*2 < actual {
+ t.Errorf("actual=%v is too big (target=%v)", actual, target)
}
}
}
diff --git a/actions/policy.go b/actions/policy.go
index 678bcdc..ff61e8b 100644
--- a/actions/policy.go
+++ b/actions/policy.go
@@ -20,10 +20,12 @@
package actions
import (
- "errors"
+ "fmt"
"log"
"reflect"
+ "github.com/pkg/errors"
+
"fscrypt/crypto"
"fscrypt/filesystem"
"fscrypt/metadata"
@@ -32,74 +34,14 @@ import (
// Errors relating to Policies
var (
- 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")
+ 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("this policy is already protected by this protector")
- ErrNotProtected = errors.New("this policy is not protected by this protector")
+ ErrAlreadyProtected = errors.New("policy already protected by protector")
+ ErrNotProtected = errors.New("policy not protected by 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.
@@ -112,46 +54,21 @@ func PurgeAllPolicies(ctx *Context) error {
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.
+ for _, policyDescriptor := range policies {
+ service := ctx.getService()
+ err = crypto.RemovePolicyKey(policyDescriptor, service)
+
+ switch errors.Cause(err) {
+ case nil, crypto.ErrKeyringSearch:
+ // We don't care if the key has already been removed
+ break
+ default:
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
@@ -161,6 +78,7 @@ type Policy struct {
Context *Context
data *metadata.PolicyData
key *crypto.Key
+ created bool
}
// CreatePolicy creates a Policy protected by given Protector and stores the
@@ -182,51 +100,127 @@ func CreatePolicy(ctx *Context, protector *Protector) (*Policy, error) {
Options: ctx.Config.Options,
KeyDescriptor: crypto.ComputeDescriptor(key),
},
- key: key,
+ key: key,
+ created: true,
}
if err = policy.AddProtector(protector); err != nil {
- policy.Wipe()
+ policy.Lock()
return nil, err
}
return policy, nil
}
-// 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)
+// GetPolicy retrieves a locked policy with a specific descriptor. The Policy is
+// still locked in this case, so it must be unlocked before using certain
+// methods.
+func GetPolicy(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
+}
+
+// GetPolicyFromPath returns the locked policy descriptor for a file on the
+// filesystem. The Policy is still locked in this case, so it must be unlocked
+// before using certain methods. An error is returned if the metadata is
+// inconsistent or the path is not encrypted.
+func GetPolicyFromPath(ctx *Context, path string) (*Policy, error) {
+ if err := ctx.checkContext(); err != nil {
+ return nil, err
+ }
+
+ // 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
}
- return policy, policy.unwrapPolicy(optionFn, keyFn)
+ descriptor := pathData.KeyDescriptor
+ log.Printf("found policy %s for %q", descriptor, path)
+
+ mountData, err := ctx.Mount.GetPolicy(descriptor)
+ if err != nil {
+ log.Printf("getting policy metadata: %v", err)
+ return nil, errors.Wrap(ErrMissingPolicyMetadata, path)
+ }
+ 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 nil, errors.Wrapf(ErrPolicyMetadataMismatch, "policy %s", descriptor)
+ }
+ log.Print("data from filesystem and path agree")
+
+ return &Policy{Context: ctx, data: mountData}, nil
}
-// listOptions creates a slice of ProtectorOptions for the protectors protecting
-// this policy.
-func (policy *Policy) listOptions() []*ProtectorOption {
+// ProtectorOptions creates a slice of ProtectorOptions for the protectors
+// protecting this policy.
+func (policy *Policy) ProtectorOptions() []*ProtectorOption {
options := make([]*ProtectorOption, len(policy.data.WrappedPolicyKeys))
for i, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
- options[i] = policy.Context.GetProtectorOption(wrappedPolicyKey.ProtectorDescriptor)
+ options[i] = policy.Context.getProtectorOption(wrappedPolicyKey.ProtectorDescriptor)
}
return options
}
-// unwrapPolicy initializes the policy key using the provided callbacks.
-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))
-
+// ProtectorDescriptors creates a slice of the Protector descriptors for the
+// protectors protecting this policy.
+func (policy *Policy) ProtectorDescriptors() []string {
+ descriptors := make([]string, len(policy.data.WrappedPolicyKeys))
for i, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
- wrappedKeys[i] = wrappedPolicyKey.WrappedKey
+ descriptors[i] = wrappedPolicyKey.ProtectorDescriptor
}
+ return descriptors
+}
+
+// Descriptor returns the key descriptor for this policy.
+func (policy *Policy) Descriptor() string {
+ return policy.data.KeyDescriptor
+}
+
+// Destroy removes a policy from the filesystem. The internal key should still
+// be wiped with Lock().
+func (policy *Policy) Destroy() error {
+ return policy.Context.Mount.RemovePolicy(policy.Descriptor())
+}
+
+// Revert destroys a policy if it was created, but does nothing if it was just
+// queried from the filesystem.
+func (policy *Policy) Revert() error {
+ if !policy.created {
+ return nil
+ }
+ return policy.Destroy()
+}
+
+func (policy *Policy) String() string {
+ return fmt.Sprintf("Policy: %s\nMountpoint: %s\nOptions: %v\nProtectors:%+v",
+ policy.Descriptor(), policy.Context.Mount, policy.data.Options,
+ policy.ProtectorDescriptors())
+}
+
+// Unlock unwraps the Policy's internal key. 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. Does nothing if policy is already unlocked.
+func (policy *Policy) Unlock(optionFn OptionFunc, keyFn KeyFunc) error {
+ if policy.key != nil {
+ return nil
+ }
+ options := policy.ProtectorOptions()
// The OptionFunc indicates which option and wrapped key we should use.
- idx, err := optionFn(policy.data.KeyDescriptor, options)
+ idx, err := optionFn(policy.Descriptor(), options)
if err != nil {
return err
}
@@ -235,52 +229,67 @@ func (policy *Policy) unwrapPolicy(optionFn OptionFunc, keyFn KeyFunc) error {
return option.LoadError
}
- wrappedPolicyKey := wrappedKeys[idx]
log.Printf("protector %s selected in callback", option.Descriptor())
-
protectorKey, err := unwrapProtectorKey(option.ProtectorInfo, keyFn)
if err != nil {
return err
}
defer protectorKey.Wipe()
- log.Printf("unwrapping policy %s with protector", policy.data.KeyDescriptor)
+ log.Printf("unwrapping policy %s with protector", policy.Descriptor())
+ wrappedPolicyKey := policy.data.WrappedPolicyKeys[idx].WrappedKey
policy.key, err = crypto.Unwrap(protectorKey, wrappedPolicyKey)
return err
}
+// Lock wipes a Policy's internal Key. It should always be called after using a
+// Policy. This is often done with a defer statement. There is no effect if
+// called multiple times.
+func (policy *Policy) Lock() error {
+ err := policy.key.Wipe()
+ policy.key = nil
+ return err
+}
+
// AddProtector updates the data that is wrapping the Policy Key so that the
// provided Protector is now protecting the specified Policy. If an error is
// returned, no data has been changed. If the policy and protector are on
-// different filesystems, a link will be created between them.
+// different filesystems, a link will be created between them. The policy and
+// protector must both be unlocked.
func (policy *Policy) AddProtector(protector *Protector) error {
- _, err := policy.findWrappedKeyIndex(protector.data.ProtectorDescriptor)
- if err == nil {
+ if _, ok := policy.findWrappedKeyIndex(protector.Descriptor()); ok {
return ErrAlreadyProtected
}
+ if policy.key == nil || protector.key == nil {
+ return ErrLocked
+ }
// If the protector is on a different filesystem, we need to add a link
// to it on the policy's filesystem.
if policy.Context.Mount != protector.Context.Mount {
- err = policy.Context.Mount.AddLinkedProtector(
- protector.data.ProtectorDescriptor, protector.Context.Mount)
+ log.Printf("policy on %s\n protector on %s\n", policy.Context.Mount, protector.Context.Mount)
+ err := policy.Context.Mount.AddLinkedProtector(
+ protector.Descriptor(), protector.Context.Mount)
if err != nil {
return err
}
+ } else {
+ log.Printf("policy and protector both on %q", policy.Context.Mount)
}
// Create the wrapped policy key
- wrappedPolicyKey := &metadata.WrappedPolicyKey{
- ProtectorDescriptor: protector.data.ProtectorDescriptor,
- }
- if wrappedPolicyKey.WrappedKey, err = crypto.Wrap(protector.key, policy.key); err != nil {
+ wrappedKey, err := crypto.Wrap(protector.key, policy.key)
+ if err != nil {
return err
}
// Append the wrapped key to the data
- policy.addKey(wrappedPolicyKey)
+ policy.addKey(&metadata.WrappedPolicyKey{
+ ProtectorDescriptor: protector.Descriptor(),
+ WrappedKey: wrappedKey,
+ })
- if err = policy.commitData(); err != nil {
+ if err := policy.commitData(); err != nil {
// revert the addition on failure
policy.removeKey(len(policy.data.WrappedPolicyKeys) - 1)
return err
@@ -290,13 +299,13 @@ func (policy *Policy) AddProtector(protector *Protector) error {
// RemoveProtector updates the data that is wrapping the Policy Key so that the
// provided Protector is no longer protecting the specified Policy. If an 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(protectorDescriptor string) error {
- idx, err := policy.findWrappedKeyIndex(protectorDescriptor)
- if err != nil {
- return err
+// is returned, no data has been changed. Note that no protector links are
+// removed (in the case where the protector and policy are on different
+// filesystems). The policy and protector can be locked or unlocked.
+func (policy *Policy) RemoveProtector(protector *Protector) error {
+ idx, ok := policy.findWrappedKeyIndex(protector.Descriptor())
+ if !ok {
+ return ErrNotProtected
}
if len(policy.data.WrappedPolicyKeys) == 1 {
@@ -306,7 +315,7 @@ func (policy *Policy) RemoveProtector(protectorDescriptor string) error {
// Remove the wrapped key from the data
toRemove := policy.removeKey(idx)
- if err = policy.commitData(); err != nil {
+ if err := policy.commitData(); err != nil {
// revert the removal on failure (order is irrelevant)
policy.addKey(toRemove)
return err
@@ -327,40 +336,43 @@ func (policy *Policy) Apply(path string) error {
return metadata.SetPolicy(path, policy.data)
}
-// 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 {
- return crypto.InsertPolicyKey(policy.key, policy.data.KeyDescriptor, getService(policy.Context))
+// IsProvisioned returns a boolean indicating if the policy has its key in the
+// keyring, meaning files and directories using this policy are accessible.
+func (policy *Policy) IsProvisioned() bool {
+ _, _, err := crypto.FindPolicyKey(policy.Descriptor(), policy.Context.getService())
+ return err == nil
}
-// 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()
+// Provision inserts the Policy key into the kernel keyring. This allows reading
+// and writing of files encrypted with this directory. Requires unlocked Policy.
+func (policy *Policy) Provision() error {
+ if policy.key == nil {
+ return ErrLocked
+ }
+ return crypto.InsertPolicyKey(policy.key, policy.Descriptor(), policy.Context.getService())
}
-// Destroy removes a policy from the filesystem. The internal key should still
-// be wiped with Wipe().
-func (policy *Policy) Destroy() error {
- return policy.Context.Mount.RemovePolicy(policy.data.KeyDescriptor)
+// Deprovision removes the Policy key from the kernel keyring. This prevents
+// reading and writing to the directory once the caches are cleared.
+func (policy *Policy) Deprovision() error {
+ return crypto.RemovePolicyKey(policy.Descriptor(), policy.Context.getService())
}
-// commitData writes the Policy's current data to the filesystem
+// commitData writes the Policy's current data to the filesystem.
func (policy *Policy) commitData() error {
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(protectorDescriptor string) (int, error) {
+// corresponding to this policy and protector. The returned bool is false if no
+// wrapped policy key corresponds to the specified protector, true otherwise.
+func (policy *Policy) findWrappedKeyIndex(protectorDescriptor string) (int, bool) {
for idx, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
if wrappedPolicyKey.ProtectorDescriptor == protectorDescriptor {
- return idx, nil
+ return idx, true
}
}
-
- return 0, ErrNotProtected
+ return 0, false
}
// addKey adds the wrapped policy key to end of the wrapped key data.
diff --git a/actions/policy_test.go b/actions/policy_test.go
index 07a7f87..96b9bb0 100644
--- a/actions/policy_test.go
+++ b/actions/policy_test.go
@@ -21,53 +21,54 @@ package actions
import "testing"
-// Makes a context, protector, and policy
-func makeAll() (ctx *Context, protector *Protector, policy *Policy, err error) {
- ctx, err = makeContext()
+// Makes a protector and policy
+func makeBoth() (*Protector, *Policy, error) {
+ protector, err := CreateProtector(testContext, testProtectorName, goodCallback)
if err != nil {
- return
+ return nil, nil, err
}
- protector, err = CreateProtector(ctx, testProtectorName, goodCallback)
+ policy, err := CreatePolicy(testContext, protector)
if err != nil {
- return
+ cleanupProtector(protector)
+ return nil, nil, err
}
- policy, err = CreatePolicy(ctx, protector)
- return
+ return protector, policy, nil
}
-// Cleans up a context, protector, and policy
-func cleanupAll(protector *Protector, policy *Policy) {
- if policy != nil {
- policy.Wipe()
- }
- if protector != nil {
- protector.Wipe()
- }
- cleaupContext()
+func cleanupProtector(protector *Protector) {
+ protector.Lock()
+ protector.Destroy()
+}
+
+func cleanupPolicy(policy *Policy) {
+ policy.Lock()
+ policy.Destroy()
}
// Tests that we can make a policy/protector pair
func TestCreatePolicy(t *testing.T) {
- _, pro, pol, err := makeAll()
- defer cleanupAll(pro, pol)
+ pro, pol, err := makeBoth()
if err != nil {
t.Error(err)
}
+ cleanupPolicy(pol)
+ cleanupProtector(pro)
}
// Tests that we can add another protector to the policy
func TestPolicyGoodAddProtector(t *testing.T) {
- ctx, pro1, pol, err := makeAll()
- defer cleanupAll(pro1, pol)
+ pro1, pol, err := makeBoth()
+ defer cleanupProtector(pro1)
+ defer cleanupPolicy(pol)
if err != nil {
t.Fatal(err)
}
- pro2, err := CreateProtector(ctx, testProtectorName2, goodCallback)
+ pro2, err := CreateProtector(testContext, testProtectorName2, goodCallback)
if err != nil {
t.Fatal(err)
}
- defer pro2.Wipe()
+ defer cleanupProtector(pro2)
err = pol.AddProtector(pro2)
if err != nil {
@@ -77,8 +78,9 @@ func TestPolicyGoodAddProtector(t *testing.T) {
// Tests that we cannot add a protector to a policy twice
func TestPolicyBadAddProtector(t *testing.T) {
- _, pro, pol, err := makeAll()
- defer cleanupAll(pro, pol)
+ pro, pol, err := makeBoth()
+ defer cleanupProtector(pro)
+ defer cleanupPolicy(pol)
if err != nil {
t.Fatal(err)
}
@@ -90,24 +92,25 @@ func TestPolicyBadAddProtector(t *testing.T) {
// Tests that we can remove a protector we added
func TestPolicyGoodRemoveProtector(t *testing.T) {
- ctx, pro1, pol, err := makeAll()
- defer cleanupAll(pro1, pol)
+ pro1, pol, err := makeBoth()
+ defer cleanupProtector(pro1)
+ defer cleanupPolicy(pol)
if err != nil {
t.Fatal(err)
}
- pro2, err := CreateProtector(ctx, testProtectorName2, goodCallback)
+ pro2, err := CreateProtector(testContext, testProtectorName2, goodCallback)
if err != nil {
t.Fatal(err)
}
- defer pro2.Wipe()
+ defer cleanupProtector(pro2)
err = pol.AddProtector(pro2)
if err != nil {
t.Fatal(err)
}
- err = pol.RemoveProtector(pro1.data.ProtectorDescriptor)
+ err = pol.RemoveProtector(pro1)
if err != nil {
t.Error(err)
}
@@ -115,23 +118,24 @@ func TestPolicyGoodRemoveProtector(t *testing.T) {
// Tests various bad ways to remove protectors
func TestPolicyBadRemoveProtector(t *testing.T) {
- ctx, pro1, pol, err := makeAll()
- defer cleanupAll(pro1, pol)
+ pro1, pol, err := makeBoth()
+ defer cleanupProtector(pro1)
+ defer cleanupPolicy(pol)
if err != nil {
t.Fatal(err)
}
- pro2, err := CreateProtector(ctx, testProtectorName2, goodCallback)
+ pro2, err := CreateProtector(testContext, testProtectorName2, goodCallback)
if err != nil {
t.Fatal(err)
}
- defer pro2.Wipe()
+ defer cleanupProtector(pro2)
- if pol.RemoveProtector(pro2.data.ProtectorDescriptor) == nil {
+ if pol.RemoveProtector(pro2) == nil {
t.Error("we should not be able to remove a protector we did not add")
}
- if pol.RemoveProtector(pro1.data.ProtectorDescriptor) == nil {
+ if pol.RemoveProtector(pro1) == nil {
t.Error("we should not be able to remove all the protectors from a policy")
}
}
diff --git a/actions/protector.go b/actions/protector.go
index 4680cba..0409b56 100644
--- a/actions/protector.go
+++ b/actions/protector.go
@@ -20,9 +20,12 @@
package actions
import (
- "errors"
+ "fmt"
+ "log"
"os"
+ "github.com/pkg/errors"
+
"fscrypt/crypto"
"fscrypt/metadata"
)
@@ -31,21 +34,21 @@ import (
var (
ErrProtectorName = errors.New("login protectors do not need a name")
ErrMissingProtectorName = errors.New("custom protectors must have a name")
- ErrDuplicateName = errors.New("a protector with this name already exists")
- ErrDuplicateUID = errors.New("there is already a login protector for this user")
+ ErrDuplicateName = errors.New("protector with this name already exists")
+ ErrDuplicateUID = errors.New("login protector for this user already exists")
)
// checkForProtectorWithName returns an error if there is already a protector
// on the filesystem with a specific name (or if we cannot read the necessary
// data).
func checkForProtectorWithName(ctx *Context, name string) error {
- options, err := ctx.ListProtectorOptions()
+ options, err := ctx.ProtectorOptions()
if err != nil {
return err
}
for _, option := range options {
if option.Name() == name {
- return ErrDuplicateName
+ return errors.Wrapf(ErrDuplicateName, "name %q", name)
}
}
return nil
@@ -55,13 +58,13 @@ func checkForProtectorWithName(ctx *Context, name string) error {
// protector on the filesystem with a specific UID (or if we cannot read the
// necessary data).
func checkForProtectorWithUID(ctx *Context, uid int64) error {
- options, err := ctx.ListProtectorOptions()
+ options, err := ctx.ProtectorOptions()
if err != nil {
return err
}
for _, option := range options {
if option.Source() == metadata.SourceType_pam_passphrase && option.UID() == uid {
- return ErrDuplicateUID
+ return errors.Wrapf(ErrDuplicateUID, "uid %d", uid)
}
}
return nil
@@ -75,12 +78,13 @@ type Protector struct {
Context *Context
data *metadata.ProtectorData
key *crypto.Key
+ created bool
}
-// CreateProtector creates a protector with a given name (only for custom and
-// raw protector types). The keyFn provided to create the Protector key will
-// only be called once. If an error is returned, no data has been changed on the
-// filesystem.
+// CreateProtector creates an unlocked protector with a given name (name only
+// needed for custom and raw protector types). The keyFn provided to create the
+// Protector key will only be called once. If an error is returned, no data has
+// been changed on the filesystem.
func CreateProtector(ctx *Context, name string, keyFn KeyFunc) (*Protector, error) {
if err := ctx.checkContext(); err != nil {
return nil, err
@@ -109,6 +113,7 @@ func CreateProtector(ctx *Context, name string, keyFn KeyFunc) (*Protector, erro
Name: name,
Source: ctx.Config.Source,
},
+ created: true,
}
// Extra data is needed for some SourceTypes
@@ -138,36 +143,34 @@ func CreateProtector(ctx *Context, name string, keyFn KeyFunc) (*Protector, erro
protector.data.ProtectorDescriptor = crypto.ComputeDescriptor(protector.key)
if err := protector.Rewrap(keyFn); err != nil {
- protector.Wipe()
+ protector.Lock()
return nil, err
}
return protector, nil
}
-// GetProtector retrieves a Protector with a specific descriptor. The keyFn
-// provided to unwrap the Protector key will be retied as necessary to get the
-// correct key.
-func GetProtector(ctx *Context, descriptor string, keyFn KeyFunc) (*Protector, error) {
- if err := ctx.checkContext(); err != nil {
- return nil, err
- }
- var err error
- protector := &Protector{Context: ctx}
-
- if protector.data, err = ctx.Mount.GetRegularProtector(descriptor); err != nil {
+// GetProtector retrieves a Protector with a specific descriptor. The Protector
+// is still locked in this case, so it must be unlocked before using certain
+// methods.
+func GetProtector(ctx *Context, descriptor string) (*Protector, error) {
+ log.Printf("Getting protector %s", descriptor)
+ err := ctx.checkContext()
+ if err != nil {
return nil, err
}
- protector.key, err = unwrapProtectorKey(ProtectorInfo{protector.data}, keyFn)
+ protector := &Protector{Context: ctx}
+ protector.data, err = ctx.Mount.GetRegularProtector(descriptor)
return protector, err
}
// GetProtectorFromOption retrieves a protector based on a protector option.
// If the option had a load error, this function returns that error. The
-// keyFn provided to unwrap the Protector key will be retied as necessary to
-// get the correct key.
-func GetProtectorFromOption(ctx *Context, option *ProtectorOption, keyFn KeyFunc) (*Protector, error) {
+// Protector is still locked in this case, so it must be unlocked before using
+// certain methods.
+func GetProtectorFromOption(ctx *Context, option *ProtectorOption) (*Protector, error) {
+ log.Printf("Getting protector %s from option", option.Descriptor())
if err := ctx.checkContext(); err != nil {
return nil, err
}
@@ -179,18 +182,62 @@ func GetProtectorFromOption(ctx *Context, option *ProtectorOption, keyFn KeyFunc
if option.LinkedMount != nil {
ctx = &Context{ctx.Config, option.LinkedMount}
}
- var err error
- protector := &Protector{Context: ctx, data: option.data}
+ return &Protector{Context: ctx, data: option.data}, nil
+}
- protector.key, err = unwrapProtectorKey(option.ProtectorInfo, keyFn)
- return protector, err
+// Descriptor returns the protector descriptor.
+func (protector *Protector) Descriptor() string {
+ return protector.data.ProtectorDescriptor
+}
+
+// Destroy removes a protector from the filesystem. The internal key should
+// still be wiped with Lock().
+func (protector *Protector) Destroy() error {
+ return protector.Context.Mount.RemoveProtector(protector.Descriptor())
+}
+
+// Revert destroys a protector if it was created, but does nothing if it was
+// just queried from the filesystem.
+func (protector *Protector) Revert() error {
+ if !protector.created {
+ return nil
+ }
+ return protector.Destroy()
+}
+
+func (protector *Protector) String() string {
+ return fmt.Sprintf("Protector: %s\nMountpoint: %s\nSource: %s\nName: %s\nCosts: %v\nUID: %d",
+ protector.Descriptor(), protector.Context.Mount, protector.data.Source,
+ protector.data.Name, protector.data.Costs, protector.data.Uid)
+}
+
+// Unlock unwraps the Protector's internal key. The keyFn provided to unwrap the
+// Protector key will be retried as necessary to get the correct key. Lock()
+// should be called after use. Does nothing if protector is already unlocked.
+func (protector *Protector) Unlock(keyFn KeyFunc) (err error) {
+ if protector.key != nil {
+ return
+ }
+ protector.key, err = unwrapProtectorKey(ProtectorInfo{protector.data}, keyFn)
+ return
+}
+
+// Lock wipes a Protector's internal Key. It should always be called after using
+// an unlocked Protector. This is often done with a defer statement. There is
+// no effect if called multiple times.
+func (protector *Protector) Lock() error {
+ err := protector.key.Wipe()
+ protector.key = nil
+ return err
}
// Rewrap updates the data that is wrapping the Protector Key. This is useful if
// a user's password has changed, for example. The keyFn provided to rewrap
-// the Protector key will only be called once. If an error is returned, no data
-// has been changed on the filesystem.
+// the Protector key will only be called once. Requires unlocked Protector.
func (protector *Protector) Rewrap(keyFn KeyFunc) error {
+ if protector.key == nil {
+ return ErrLocked
+ }
wrappingKey, err := getWrappingKey(ProtectorInfo{protector.data}, keyFn, false)
if err != nil {
return err
@@ -211,15 +258,3 @@ func (protector *Protector) Rewrap(keyFn KeyFunc) error {
return protector.Context.Mount.AddProtector(protector.data)
}
-
-// Wipe wipes a Protector's internal Key. It should always be called after using
-// a Protector. This is often done with a defer statement.
-func (protector *Protector) Wipe() error {
- return protector.key.Wipe()
-}
-
-// Destroy removes a protector from the filesystem. The internal key should
-// still be wiped with Wipe().
-func (protector *Protector) Destroy() error {
- return protector.Context.Mount.RemoveProtector(protector.data.ProtectorDescriptor)
-}
diff --git a/actions/protector_test.go b/actions/protector_test.go
index 08d9aed..eacba83 100644
--- a/actions/protector_test.go
+++ b/actions/protector_test.go
@@ -21,9 +21,10 @@ package actions
import (
"bytes"
- "errors"
"testing"
+ "github.com/pkg/errors"
+
. "fscrypt/crypto"
)
@@ -42,31 +43,21 @@ func badCallback(info ProtectorInfo, retry bool) (*Key, error) {
// Tests that we can create a valid protector.
func TestCreateProtector(t *testing.T) {
- ctx, err := makeContext()
- defer cleaupContext()
- if err != nil {
- t.Fatal(err)
- }
-
- p, err := CreateProtector(ctx, testProtectorName, goodCallback)
+ p, err := CreateProtector(testContext, testProtectorName, goodCallback)
if err != nil {
t.Error(err)
} else {
- p.Wipe()
+ p.Lock()
+ p.Destroy()
}
}
// Tests that a failure in the callback is relayed back to the caller.
func TestBadCallback(t *testing.T) {
- ctx, err := makeContext()
- defer cleaupContext()
- if err != nil {
- t.Fatal(err)
- }
-
- p, err := CreateProtector(ctx, testProtectorName, badCallback)
+ p, err := CreateProtector(testContext, testProtectorName, badCallback)
if err == nil {
- p.Wipe()
+ p.Lock()
+ p.Destroy()
}
if err != errCallback {
t.Error("callback error was not relayed back to caller")