diff options
| author | ebiggers <ebiggers@google.com> | 2020-01-22 18:28:23 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-01-22 18:28:23 -0800 |
| commit | 059482129c5fdafebc582887a4ae4ef80988b708 (patch) | |
| tree | 8ec373c41a677ff6949148b56f4aeaafe22791a6 /actions | |
| parent | 80654f23ebfd552277ed217a2c5e1d0bb1374189 (diff) | |
| parent | fe2939cc7e50f4c6025253efdf7380c04fac9ae1 (diff) | |
Merge pull request #148 from ebiggers/fscrypt-key-mgmt-improvements
Filesystem keyring and v2 encryption policy support
Diffstat (limited to 'actions')
| -rw-r--r-- | actions/config.go | 4 | ||||
| -rw-r--r-- | actions/context.go | 19 | ||||
| -rw-r--r-- | actions/policy.go | 89 | ||||
| -rw-r--r-- | actions/protector.go | 6 |
4 files changed, 91 insertions, 27 deletions
diff --git a/actions/config.go b/actions/config.go index 7fdaf5b..6b019df 100644 --- a/actions/config.go +++ b/actions/config.go @@ -133,6 +133,10 @@ func getConfig() (*metadata.Config, error) { config.Options.Filenames = metadata.DefaultOptions.Filenames log.Printf("Falling back to filenames mode of %q", config.Options.Filenames) } + if config.Options.PolicyVersion == 0 { + config.Options.PolicyVersion = metadata.DefaultOptions.PolicyVersion + log.Printf("Falling back to policy version of %d", config.Options.PolicyVersion) + } if err := config.CheckValidity(); err != nil { return nil, errors.Wrap(ErrBadConfigFile, err.Error()) diff --git a/actions/context.go b/actions/context.go index 5a56789..f07f225 100644 --- a/actions/context.go +++ b/actions/context.go @@ -37,6 +37,7 @@ import ( "github.com/pkg/errors" "github.com/google/fscrypt/filesystem" + "github.com/google/fscrypt/keyring" "github.com/google/fscrypt/metadata" "github.com/google/fscrypt/util" ) @@ -57,10 +58,13 @@ type Context struct { // modified after being loaded to customise parameters. Config *metadata.Config // Mount is the filesystem relative to which all Protectors and Policies - // are added, edited, removed, and applied. + // are added, edited, removed, and applied, and to which policies using + // the filesystem keyring are provisioned. Mount *filesystem.Mount - // TargetUser is the user for which protectors are created and to whose - // keyring policies are provisioned. + // TargetUser is the user for whom protectors are created, and to whose + // keyring policies using the user keyring are provisioned. It's also + // the user for whom the keys are claimed in the filesystem keyring when + // v2 policies are provisioned. TargetUser *user.User } @@ -145,6 +149,15 @@ func (ctx *Context) getService() string { return unix.FSCRYPT_KEY_DESC_PREFIX } +func (ctx *Context) getKeyringOptions() *keyring.Options { + return &keyring.Options{ + Mount: ctx.Mount, + User: ctx.TargetUser, + Service: ctx.getService(), + UseFsKeyringForV1Policies: ctx.Config.GetUseFsKeyringForV1Policies(), + } +} + // getProtectorOption returns the ProtectorOption for the protector on the // context's mountpoint with the specified descriptor. func (ctx *Context) getProtectorOption(protectorDescriptor string) *ProtectorOption { diff --git a/actions/policy.go b/actions/policy.go index 875a01f..41e108e 100644 --- a/actions/policy.go +++ b/actions/policy.go @@ -28,8 +28,8 @@ import ( "github.com/google/fscrypt/crypto" "github.com/google/fscrypt/filesystem" + "github.com/google/fscrypt/keyring" "github.com/google/fscrypt/metadata" - "github.com/google/fscrypt/security" "github.com/google/fscrypt/util" ) @@ -44,8 +44,8 @@ var ( ) // 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. +// keyring. In order for this to fully take effect, the filesystem may also need +// to be unmounted or caches dropped. func PurgeAllPolicies(ctx *Context) error { if err := ctx.checkContext(); err != nil { return err @@ -56,12 +56,16 @@ func PurgeAllPolicies(ctx *Context) error { } for _, policyDescriptor := range policies { - service := ctx.getService() - err = security.RemoveKey(service+policyDescriptor, ctx.TargetUser) - + err = keyring.RemoveEncryptionKey(policyDescriptor, ctx.getKeyringOptions(), false) switch errors.Cause(err) { - case nil, security.ErrKeySearch: + case nil, keyring.ErrKeyNotPresent: // We don't care if the key has already been removed + case keyring.ErrKeyFilesOpen: + log.Printf("Key for policy %s couldn't be fully removed because some files are still in-use", + policyDescriptor) + case keyring.ErrKeyAddedByOtherUsers: + log.Printf("Key for policy %s couldn't be fully removed because other user(s) have added it too", + policyDescriptor) default: return err } @@ -94,11 +98,17 @@ func CreatePolicy(ctx *Context, protector *Protector) (*Policy, error) { return nil, err } + keyDescriptor, err := crypto.ComputeKeyDescriptor(key, ctx.Config.Options.PolicyVersion) + if err != nil { + key.Wipe() + return nil, err + } + policy := &Policy{ Context: ctx, data: &metadata.PolicyData{ Options: ctx.Config.Options, - KeyDescriptor: crypto.ComputeDescriptor(key), + KeyDescriptor: keyDescriptor, }, key: key, created: true, @@ -188,17 +198,16 @@ func (policy *Policy) Descriptor() string { return policy.data.KeyDescriptor } -// Description returns the description that will be used when the key for this -// Policy is inserted into the keyring -func (policy *Policy) Description() string { - return policy.Context.getService() + policy.Descriptor() -} - // Options returns the encryption options of this policy. func (policy *Policy) Options() *metadata.EncryptionOptions { return policy.data.Options } +// Version returns the version of this policy. +func (policy *Policy) Version() int64 { + return policy.data.Options.PolicyVersion +} + // Destroy removes a policy from the filesystem. The internal key should still // be wiped with Lock(). func (policy *Policy) Destroy() error { @@ -374,11 +383,24 @@ func (policy *Policy) Apply(path string) error { return metadata.SetPolicy(path, policy.data) } -// 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(), policy.Context.TargetUser) - return err == nil +// GetProvisioningStatus returns the status of this policy's key in the keyring. +func (policy *Policy) GetProvisioningStatus() keyring.KeyStatus { + status, _ := keyring.GetEncryptionKeyStatus(policy.Descriptor(), + policy.Context.getKeyringOptions()) + return status +} + +// IsProvisionedByTargetUser returns true if the policy's key is present in the +// target kernel keyring, but not if that keyring is a filesystem keyring and +// the key only been added by users other than Context.TargetUser. +func (policy *Policy) IsProvisionedByTargetUser() bool { + return policy.GetProvisioningStatus() == keyring.KeyPresent +} + +// IsFullyDeprovisioned returns true if the policy has been fully deprovisioned, +// including by all users and with all files protected by it having been closed. +func (policy *Policy) IsFullyDeprovisioned() bool { + return policy.GetProvisioningStatus() == keyring.KeyAbsent } // Provision inserts the Policy key into the kernel keyring. This allows reading @@ -387,13 +409,34 @@ func (policy *Policy) Provision() error { if policy.key == nil { return ErrLocked } - return crypto.InsertPolicyKey(policy.key, policy.Description(), policy.Context.TargetUser) + return keyring.AddEncryptionKey(policy.key, policy.Descriptor(), + policy.Context.getKeyringOptions()) } // 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(), policy.Context.TargetUser) +// reading and writing to the directory --- unless the target keyring is a user +// keyring, in which case caches must be dropped too. +func (policy *Policy) Deprovision(allUsers bool) error { + return keyring.RemoveEncryptionKey(policy.Descriptor(), + policy.Context.getKeyringOptions(), allUsers) +} + +// NeedsUserKeyring returns true if Provision and Deprovision for this policy +// will use a user keyring, not a filesystem keyring. +func (policy *Policy) NeedsUserKeyring() bool { + return policy.Version() == 1 && !policy.Context.Config.GetUseFsKeyringForV1Policies() +} + +// NeedsRootToProvision returns true if Provision and Deprovision will require +// root for this policy in the current configuration. +func (policy *Policy) NeedsRootToProvision() bool { + return policy.Version() == 1 && policy.Context.Config.GetUseFsKeyringForV1Policies() +} + +// CanBeAppliedWithoutProvisioning returns true if this process can apply this +// policy to a directory without first calling Provision. +func (policy *Policy) CanBeAppliedWithoutProvisioning() bool { + return policy.Version() == 1 || util.IsUserRoot() } // commitData writes the Policy's current data to the filesystem. diff --git a/actions/protector.go b/actions/protector.go index fe5d694..4bd7c15 100644 --- a/actions/protector.go +++ b/actions/protector.go @@ -140,7 +140,11 @@ func CreateProtector(ctx *Context, name string, keyFn KeyFunc) (*Protector, erro if protector.key, err = crypto.NewRandomKey(metadata.InternalKeyLen); err != nil { return nil, err } - protector.data.ProtectorDescriptor = crypto.ComputeDescriptor(protector.key) + protector.data.ProtectorDescriptor, err = crypto.ComputeKeyDescriptor(protector.key, 1) + if err != nil { + protector.Lock() + return nil, err + } if err = protector.Rewrap(keyFn); err != nil { protector.Lock() |