From 93415b198a3ef427c02893b8fdf036aa75ffe50f Mon Sep 17 00:00:00 2001 From: "Joe Richey joerichey@google.com" Date: Wed, 21 Jun 2017 10:03:44 -0700 Subject: 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 --- actions/protector.go | 125 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 45 deletions(-) (limited to 'actions/protector.go') 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) -} -- cgit v1.2.3