/* * protector.go - functions for dealing with protectors * * Copyright 2017 Google Inc. * Author: Joe Richey (joerichey@google.com) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package actions import ( "fmt" "log" "github.com/pkg/errors" "github.com/google/fscrypt/crypto" "github.com/google/fscrypt/metadata" "github.com/google/fscrypt/util" ) // Errors relating to Protectors var ( ErrProtectorName = errors.New("login protectors do not need a name") ErrMissingProtectorName = errors.New("custom protectors must have a name") 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.ProtectorOptions() if err != nil { return err } for _, option := range options { if option.Name() == name { return errors.Wrapf(ErrDuplicateName, "name %q", name) } } return nil } // checkIfUserHasLoginProtector returns an error if there is already a login // protector on the filesystem for a specific user (or if we cannot read the // necessary data). func checkIfUserHasLoginProtector(ctx *Context, uid int64) error { options, err := ctx.ProtectorOptions() if err != nil { return err } for _, option := range options { if option.Source() == metadata.SourceType_pam_passphrase && option.UID() == uid { return errors.Wrapf(ErrDuplicateUID, "user %q", ctx.TargetUser.Username) } } return nil } // Protector represents an unlocked protector, so it contains the ProtectorData // as well as the actual protector key. These unlocked Protectors are necessary // to unlock policies and create new polices. As with the key struct, a // Protector should be wiped after use. type Protector struct { Context *Context data *metadata.ProtectorData key *crypto.Key created bool } // 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 } // Sanity checks for names if ctx.Config.Source == metadata.SourceType_pam_passphrase { // login protectors don't need a name (we use the username instead) if name != "" { return nil, ErrProtectorName } } else { // non-login protectors need a name (so we can distinguish between them) if name == "" { return nil, ErrMissingProtectorName } // we don't want to duplicate naming if err := checkForProtectorWithName(ctx, name); err != nil { return nil, err } } var err error protector := &Protector{ Context: ctx, data: &metadata.ProtectorData{ Name: name, Source: ctx.Config.Source, }, created: true, } // Extra data is needed for some SourceTypes switch protector.data.Source { case metadata.SourceType_pam_passphrase: // As the pam passphrases are user specific, we also store the // UID for this kind of source. protector.data.Uid = int64(util.AtoiOrPanic(ctx.TargetUser.Uid)) // Make sure we aren't duplicating protectors if err = checkIfUserHasLoginProtector(ctx, protector.data.Uid); err != nil { return nil, err } fallthrough case metadata.SourceType_custom_passphrase: // Our passphrase sources need costs and a random salt. if protector.data.Salt, err = crypto.NewRandomBuffer(metadata.SaltLen); err != nil { return nil, err } protector.data.Costs = ctx.Config.HashCosts } // Randomly create the underlying protector key (and wipe if we fail) if protector.key, err = crypto.NewRandomKey(metadata.InternalKeyLen); err != nil { return nil, err } protector.data.ProtectorDescriptor = crypto.ComputeDescriptor(protector.key) if err = protector.Rewrap(keyFn); err != nil { protector.Lock() return nil, err } return protector, 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 := &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 // 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 } if option.LoadError != nil { return nil, option.LoadError } // Replace the context if this is a linked protector if option.LinkedMount != nil { ctx = &Context{ctx.Config, option.LinkedMount, ctx.TargetUser} } return &Protector{Context: ctx, data: option.data}, nil } // 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. 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 } // Revert change to wrapped key on failure oldWrappedKey := protector.data.WrappedKey defer func() { wrappingKey.Wipe() if err != nil { protector.data.WrappedKey = oldWrappedKey } }() if protector.data.WrappedKey, err = crypto.Wrap(wrappingKey, protector.key); err != nil { return err } return protector.Context.Mount.AddProtector(protector.data) }