From dad0a047cefc79cbe664afc07d69db6b8bf123bd Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 30 Aug 2017 18:00:04 -0700 Subject: [PATCH] actions: context now hold a target user.User This user is used with policies to interface with the keryings and with protectors to indicate which user's login passphrase should be used to protectors of type pam_passphrase. --- actions/context.go | 70 ++++++++++++++++++++++++++++------------- actions/context_test.go | 4 +-- actions/policy.go | 8 ++--- actions/protector.go | 16 +++++----- 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/actions/context.go b/actions/context.go index 7e4b64b..8ad1357 100644 --- a/actions/context.go +++ b/actions/context.go @@ -30,6 +30,7 @@ package actions import ( "log" + "os/user" "golang.org/x/sys/unix" @@ -37,6 +38,7 @@ import ( "github.com/google/fscrypt/filesystem" "github.com/google/fscrypt/metadata" + "github.com/google/fscrypt/util" ) // Errors relating to Config files or Config structures. @@ -49,47 +51,73 @@ var ( ) // Context contains the necessary global state to perform most of fscrypt's -// actions. It contains a config struct, which is loaded from the global config -// file, but can be edited manually. A context is specific to a filesystem, and -// all actions to add, edit, remove, and apply Protectors and Policies are done -// relative to that filesystem. +// actions. type Context struct { + // Config is the struct loaded from the global config file. It can be + // modified after being loaded to customise parameters. Config *metadata.Config - Mount *filesystem.Mount + // Mount is the filesystem relitive to which all Protectors and Policies + // are added, edited, removed, and applied. + Mount *filesystem.Mount + // TargetUser is the user for which protectors are created and to whose + // keyring policies are provisioned. + TargetUser *user.User } // NewContextFromPath makes a context for the filesystem containing the // specified path and whose Config is loaded from the global config file. On -// 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 { - return +// success, the Context contains a valid Config and Mount. The target defaults +// the the current effective user if none is specified. +func NewContextFromPath(path string, target *user.User) (*Context, error) { + ctx, err := newContextFromUser(target) + if err != nil { + return nil, err } - if ctx.Config, err = getConfig(); err != nil { - return + if ctx.Mount, err = filesystem.FindMount(path); err != nil { + return nil, err } log.Printf("%s is on %s filesystem %q (%s)", path, ctx.Mount.Filesystem, ctx.Mount.Path, ctx.Mount.Device) - return + return ctx, nil } // NewContextFromMountpoint makes a context for the filesystem at the specified // mountpoint and whose Config is loaded from the global config file. On -// 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 { - return +// success, the Context contains a valid Config and Mount. The target defaults +// the the current effective user if none is specified. +func NewContextFromMountpoint(mountpoint string, target *user.User) (*Context, error) { + ctx, err := newContextFromUser(target) + if err != nil { + return nil, err } - if ctx.Config, err = getConfig(); err != nil { - return + if ctx.Mount, err = filesystem.GetMount(mountpoint); err != nil { + return nil, err } log.Printf("found %s filesystem %q (%s)", ctx.Mount.Filesystem, ctx.Mount.Path, ctx.Mount.Device) - return + return ctx, nil +} + +// newContextFromUser makes a context with the corresponding target user, and +// whose Config is loaded from the global config file. If the target is nil, the +// effecitive user is used. +func newContextFromUser(target *user.User) (*Context, error) { + var err error + if target == nil { + if target, err = util.EffectiveUser(); err != nil { + return nil, err + } + } + + ctx := &Context{TargetUser: target} + if ctx.Config, err = getConfig(); err != nil { + return nil, err + } + + log.Printf("creating context for %q", target.Username) + return ctx, nil } // checkContext verifies that the context contains an valid config and a mount diff --git a/actions/context_test.go b/actions/context_test.go index 4b38a33..593518f 100644 --- a/actions/context_test.go +++ b/actions/context_test.go @@ -47,7 +47,7 @@ func setupContext() (ctx *Context, err error) { ConfigFileLocation = filepath.Join(mountpoint, "test.conf") // Should not be able to setup without a config file - if badCtx, badCtxErr := NewContextFromMountpoint(mountpoint); badCtxErr == nil { + if badCtx, badCtxErr := NewContextFromMountpoint(mountpoint, nil); badCtxErr == nil { badCtx.Mount.RemoveAllMetadata() return nil, fmt.Errorf("created context at %q without config file", badCtx.Mount.Path) } @@ -61,7 +61,7 @@ func setupContext() (ctx *Context, err error) { } }() - ctx, err = NewContextFromMountpoint(mountpoint) + ctx, err = NewContextFromMountpoint(mountpoint, nil) if err != nil { return nil, err } diff --git a/actions/policy.go b/actions/policy.go index 461f8cc..510afa1 100644 --- a/actions/policy.go +++ b/actions/policy.go @@ -57,7 +57,7 @@ func PurgeAllPolicies(ctx *Context) error { for _, policyDescriptor := range policies { service := ctx.getService() - err = security.RemoveKey(service + policyDescriptor) + err = security.RemoveKey(service+policyDescriptor, ctx.TargetUser) switch errors.Cause(err) { case nil, security.ErrKeyringSearch: @@ -372,7 +372,7 @@ func (policy *Policy) Apply(path string) error { // 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 := security.FindKey(policy.Description()) + _, err := security.FindKey(policy.Description(), policy.Context.TargetUser) return err == nil } @@ -382,13 +382,13 @@ func (policy *Policy) Provision() error { if policy.key == nil { return ErrLocked } - return crypto.InsertPolicyKey(policy.key, policy.Description()) + return crypto.InsertPolicyKey(policy.key, policy.Description(), policy.Context.TargetUser) } // 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 security.RemoveKey(policy.Description()) + return security.RemoveKey(policy.Description(), policy.Context.TargetUser) } // commitData writes the Policy's current data to the filesystem. diff --git a/actions/protector.go b/actions/protector.go index 5245951..ffc3c43 100644 --- a/actions/protector.go +++ b/actions/protector.go @@ -22,12 +22,12 @@ package actions import ( "fmt" "log" - "os" "github.com/pkg/errors" "github.com/google/fscrypt/crypto" "github.com/google/fscrypt/metadata" + "github.com/google/fscrypt/util" ) // Errors relating to Protectors @@ -54,17 +54,17 @@ func checkForProtectorWithName(ctx *Context, name string) error { return nil } -// checkForProtectorWithUid returns an error if there is already a login -// protector on the filesystem with a specific UID (or if we cannot read the +// 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 checkForProtectorWithUID(ctx *Context, uid int64) error { +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, "uid %d", uid) + return errors.Wrapf(ErrDuplicateUID, "user %q", ctx.TargetUser.Username) } } return nil @@ -121,9 +121,9 @@ func CreateProtector(ctx *Context, name string, keyFn KeyFunc) (*Protector, erro 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(os.Getuid()) + protector.data.Uid = int64(util.AtoiOrPanic(ctx.TargetUser.Uid)) // Make sure we aren't duplicating protectors - if err := checkForProtectorWithUID(ctx, protector.data.Uid); err != nil { + if err := checkIfUserHasLoginProtector(ctx, protector.data.Uid); err != nil { return nil, err } fallthrough @@ -180,7 +180,7 @@ func GetProtectorFromOption(ctx *Context, option *ProtectorOption) (*Protector, // Replace the context if this is a linked protector if option.LinkedMount != nil { - ctx = &Context{ctx.Config, option.LinkedMount} + ctx = &Context{ctx.Config, option.LinkedMount, ctx.TargetUser} } return &Protector{Context: ctx, data: option.data}, nil } -- 2.39.5