vendor: vendor fscrypt integration dependencies

Signed-off-by: Marcel Lauhoff <marcel.lauhoff@suse.com>
This commit is contained in:
Marcel Lauhoff
2022-06-23 13:58:36 +02:00
committed by mergify[bot]
parent cfea8d7562
commit f8faffac89
44 changed files with 8910 additions and 1 deletions

132
vendor/github.com/google/fscrypt/actions/callback.go generated vendored Normal file
View File

@ -0,0 +1,132 @@
/*
* callback.go - defines how the caller of an action function passes along a key
* to be used in this package.
*
* 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 (
"log"
"github.com/pkg/errors"
"github.com/google/fscrypt/crypto"
"github.com/google/fscrypt/filesystem"
"github.com/google/fscrypt/metadata"
)
// ProtectorInfo is the information a caller will receive about a Protector
// before they have to return the corresponding key. This is currently a
// read-only view of metadata.ProtectorData.
type ProtectorInfo struct {
data *metadata.ProtectorData
}
// Descriptor is the Protector's descriptor used to uniquely identify it.
func (pi *ProtectorInfo) Descriptor() string { return pi.data.GetProtectorDescriptor() }
// Source indicates the type of the descriptor (how it should be unlocked).
func (pi *ProtectorInfo) Source() metadata.SourceType { return pi.data.GetSource() }
// Name is used to describe custom passphrase and raw key descriptors.
func (pi *ProtectorInfo) Name() string { return pi.data.GetName() }
// UID is used to identify the user for login passphrases.
func (pi *ProtectorInfo) UID() int64 { return pi.data.GetUid() }
// KeyFunc is passed to a function that will require some type of key.
// The info parameter is provided so the callback knows which key to provide.
// The retry parameter indicates that a previous key provided by this callback
// was incorrect (this allows for user feedback like "incorrect passphrase").
//
// For passphrase sources, the returned key should be a passphrase. For raw
// sources, the returned key should be a 256-bit cryptographic key. Consumers
// of the callback will wipe the returned key. An error returned by the callback
// will be propagated back to the caller.
type KeyFunc func(info ProtectorInfo, retry bool) (*crypto.Key, error)
// getWrappingKey uses the provided callback to get the wrapping key
// corresponding to the ProtectorInfo. This runs the passphrase hash for
// passphrase sources or just relays the callback for raw sources.
func getWrappingKey(info ProtectorInfo, keyFn KeyFunc, retry bool) (*crypto.Key, error) {
// For raw key sources, we can just use the key directly.
if info.Source() == metadata.SourceType_raw_key {
return keyFn(info, retry)
}
// Run the passphrase hash for other sources.
passphrase, err := keyFn(info, retry)
if err != nil {
return nil, err
}
defer passphrase.Wipe()
log.Printf("running passphrase hash for protector %s", info.Descriptor())
return crypto.PassphraseHash(passphrase, info.data.Salt, info.data.Costs)
}
// unwrapProtectorKey uses the provided callback and ProtectorInfo to return
// the unwrapped protector key. This will repeatedly call keyFn to get the
// wrapping key until the correct key is returned by the callback or the
// callback returns an error.
func unwrapProtectorKey(info ProtectorInfo, keyFn KeyFunc) (*crypto.Key, error) {
retry := false
for {
wrappingKey, err := getWrappingKey(info, keyFn, retry)
if err != nil {
return nil, err
}
protectorKey, err := crypto.Unwrap(wrappingKey, info.data.WrappedKey)
wrappingKey.Wipe()
switch errors.Cause(err) {
case nil:
log.Printf("valid wrapping key for protector %s", info.Descriptor())
return protectorKey, nil
case crypto.ErrBadAuth:
// After the first failure, we let the callback know we are retrying.
log.Printf("invalid wrapping key for protector %s", info.Descriptor())
retry = true
continue
default:
return nil, err
}
}
}
// ProtectorOption is information about a protector relative to a Policy.
type ProtectorOption struct {
ProtectorInfo
// LinkedMount is the mountpoint for a linked protector. It is nil if
// the protector is not a linked protector (or there is a LoadError).
LinkedMount *filesystem.Mount
// LoadError is non-nil if there was an error in getting the data for
// the protector.
LoadError error
}
// OptionFunc is passed to a function that needs to unlock a Policy.
// The callback is used to specify which protector should be used to unlock a
// Policy. The descriptor indicates which Policy we are using, while the options
// correspond to the valid Protectors protecting the Policy.
//
// The OptionFunc should either return a valid index into options, which
// corresponds to the desired protector, or an error (which will be propagated
// back to the caller).
type OptionFunc func(policyDescriptor string, options []*ProtectorOption) (int, error)

293
vendor/github.com/google/fscrypt/actions/config.go generated vendored Normal file
View File

@ -0,0 +1,293 @@
/*
* config.go - Actions for creating a new config file, which includes new
* hashing costs and the config file's location.
*
* 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 (
"bytes"
"fmt"
"log"
"os"
"runtime"
"time"
"golang.org/x/sys/unix"
"github.com/google/fscrypt/crypto"
"github.com/google/fscrypt/filesystem"
"github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/util"
)
// ConfigFileLocation is the location of fscrypt's global settings. This can be
// overridden by the user of this package.
var ConfigFileLocation = "/etc/fscrypt.conf"
// ErrBadConfig is an internal error that indicates that the config struct is invalid.
type ErrBadConfig struct {
Config *metadata.Config
UnderlyingError error
}
func (err *ErrBadConfig) Error() string {
return fmt.Sprintf(`internal error: config is invalid: %s
The invalid config is %s`, err.UnderlyingError, err.Config)
}
// ErrBadConfigFile indicates that the config file is invalid.
type ErrBadConfigFile struct {
Path string
UnderlyingError error
}
func (err *ErrBadConfigFile) Error() string {
return fmt.Sprintf("%q is invalid: %s", err.Path, err.UnderlyingError)
}
// ErrConfigFileExists indicates that the config file already exists.
type ErrConfigFileExists struct {
Path string
}
func (err *ErrConfigFileExists) Error() string {
return fmt.Sprintf("%q already exists", err.Path)
}
// ErrNoConfigFile indicates that the config file doesn't exist.
type ErrNoConfigFile struct {
Path string
}
func (err *ErrNoConfigFile) Error() string {
return fmt.Sprintf("%q doesn't exist", err.Path)
}
const (
// Permissions of the config file (global readable)
configPermissions = 0644
// Config file should be created for writing and not already exist
createFlags = os.O_CREATE | os.O_WRONLY | os.O_EXCL
// 128 MiB is a large enough amount of memory to make the password hash
// very difficult to brute force on specialized hardware, but small
// enough to work on most GNU/Linux systems.
maxMemoryBytes = 128 * 1024 * 1024
)
var (
timingPassphrase = []byte("I am a fake passphrase")
timingSalt = bytes.Repeat([]byte{42}, metadata.SaltLen)
)
// CreateConfigFile creates a new config file at the appropriate location with
// the appropriate hashing costs and encryption parameters. The hashing will be
// configured to take as long as the specified time target. In addition, the
// version of encryption policy to use may be overridden from the default of v1.
func CreateConfigFile(target time.Duration, policyVersion int64) error {
// Create the config file before computing the hashing costs, so we fail
// immediately if the program has insufficient permissions.
configFile, err := filesystem.OpenFileOverridingUmask(ConfigFileLocation,
createFlags, configPermissions)
switch {
case os.IsExist(err):
return &ErrConfigFileExists{ConfigFileLocation}
case err != nil:
return err
}
defer configFile.Close()
config := &metadata.Config{
Source: metadata.DefaultSource,
Options: metadata.DefaultOptions,
}
if policyVersion != 0 {
config.Options.PolicyVersion = policyVersion
}
if config.HashCosts, err = getHashingCosts(target); err != nil {
return err
}
log.Printf("Creating config at %q with %v\n", ConfigFileLocation, config)
return metadata.WriteConfig(config, configFile)
}
// getConfig returns the current configuration struct. Any fields not specified
// in the config file use the system defaults. An error is returned if the
// config file hasn't been setup with CreateConfigFile yet or the config
// contains invalid data.
func getConfig() (*metadata.Config, error) {
configFile, err := os.Open(ConfigFileLocation)
switch {
case os.IsNotExist(err):
return nil, &ErrNoConfigFile{ConfigFileLocation}
case err != nil:
return nil, err
}
defer configFile.Close()
log.Printf("Reading config from %q\n", ConfigFileLocation)
config, err := metadata.ReadConfig(configFile)
if err != nil {
return nil, &ErrBadConfigFile{ConfigFileLocation, err}
}
// Use system defaults if not specified
if config.Source == metadata.SourceType_default {
config.Source = metadata.DefaultSource
log.Printf("Falling back to source of %q", config.Source.String())
}
if config.Options.Padding == 0 {
config.Options.Padding = metadata.DefaultOptions.Padding
log.Printf("Falling back to padding of %d", config.Options.Padding)
}
if config.Options.Contents == metadata.EncryptionOptions_default {
config.Options.Contents = metadata.DefaultOptions.Contents
log.Printf("Falling back to contents mode of %q", config.Options.Contents)
}
if config.Options.Filenames == metadata.EncryptionOptions_default {
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, &ErrBadConfigFile{ConfigFileLocation, err}
}
return config, nil
}
// getHashingCosts returns hashing costs so that hashing a password will take
// approximately the target time. This is done using the total amount of RAM,
// the number of CPUs present, and by running the passphrase hash many times.
func getHashingCosts(target time.Duration) (*metadata.HashingCosts, error) {
log.Printf("Finding hashing costs that take %v\n", target)
// Start out with the minimal possible costs that use all the CPUs.
nCPUs := int64(runtime.NumCPU())
costs := &metadata.HashingCosts{
Time: 1,
Memory: 8 * nCPUs,
Parallelism: nCPUs,
}
// If even the minimal costs are not fast enough, just return the
// minimal costs and log a warning.
t, err := timeHashingCosts(costs)
if err != nil {
return nil, err
}
log.Printf("Min Costs={%v}\t-> %v\n", costs, t)
if t > target {
log.Printf("time exceeded the target of %v.\n", target)
return costs, nil
}
// Now we start doubling the costs until we reach the target.
memoryKiBLimit := memoryBytesLimit() / 1024
for {
// Store a copy of the previous costs
costsPrev := *costs
tPrev := t
// Double the memory up to the max, then double the time.
if costs.Memory < memoryKiBLimit {
costs.Memory = util.MinInt64(2*costs.Memory, memoryKiBLimit)
} else {
costs.Time *= 2
}
// If our hashing failed, return the last good set of costs.
if t, err = timeHashingCosts(costs); err != nil {
log.Printf("Hashing with costs={%v} failed: %v\n", costs, err)
return &costsPrev, nil
}
log.Printf("Costs={%v}\t-> %v\n", costs, t)
// If we have reached the target time, we return a set of costs
// based on the linear interpolation between the last two times.
if t >= target {
f := float64(target-tPrev) / float64(t-tPrev)
return &metadata.HashingCosts{
Time: betweenCosts(costsPrev.Time, costs.Time, f),
Memory: betweenCosts(costsPrev.Memory, costs.Memory, f),
Parallelism: costs.Parallelism,
}, nil
}
}
}
// memoryBytesLimit returns the maximum amount of memory we will use for
// passphrase hashing. This will never be more than a reasonable maximum (for
// compatibility) or an 8th the available system RAM.
func memoryBytesLimit() int64 {
// The sysinfo syscall only fails if given a bad address
var info unix.Sysinfo_t
err := unix.Sysinfo(&info)
util.NeverError(err)
totalRAMBytes := int64(info.Totalram)
return util.MinInt64(totalRAMBytes/8, maxMemoryBytes)
}
// betweenCosts returns a cost between a and b. Specifically, it returns the
// floor of a + f*(b-a). This way, f=0 returns a and f=1 returns b.
func betweenCosts(a, b int64, f float64) int64 {
return a + int64(f*float64(b-a))
}
// timeHashingCosts runs the passphrase hash with the specified costs and
// returns the time it takes to hash the passphrase.
func timeHashingCosts(costs *metadata.HashingCosts) (time.Duration, error) {
passphrase, err := crypto.NewKeyFromReader(bytes.NewReader(timingPassphrase))
if err != nil {
return 0, err
}
defer passphrase.Wipe()
// 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()
// This uses a lot of memory, run the garbage collector
runtime.GC()
return time.Duration((end - begin) / costs.Parallelism), 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)
}

184
vendor/github.com/google/fscrypt/actions/context.go generated vendored Normal file
View File

@ -0,0 +1,184 @@
/*
* context.go - top-level interface to fscrypt packages
*
* 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 is the high-level interface to the fscrypt packages. The
// functions here roughly correspond with commands for the tool in cmd/fscrypt.
// All of the actions include a significant amount of logging, so that good
// output can be provided for cmd/fscrypt's verbose mode.
// The top-level actions currently include:
// - Creating a new config file
// - Creating a context on which to perform actions
// - Creating, unlocking, and modifying Protectors
// - Creating, unlocking, and modifying Policies
package actions
import (
"log"
"os/user"
"github.com/pkg/errors"
"github.com/google/fscrypt/filesystem"
"github.com/google/fscrypt/keyring"
"github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/util"
)
// ErrLocked indicates that the key hasn't been unwrapped yet.
var ErrLocked = errors.New("key needs to be unlocked first")
// Context contains the necessary global state to perform most of fscrypt's
// 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 is the filesystem relative to which all Protectors and Policies
// are added, edited, removed, and applied, and to which policies using
// the filesystem keyring are provisioned.
Mount *filesystem.Mount
// 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
// TrustedUser is the user for whom policies and protectors are allowed
// to be read. Specifically, if TrustedUser is set, then only
// policies and protectors owned by TrustedUser or by root will be
// allowed to be read. If it's nil, then all policies and protectors
// the process has filesystem-level read access to will be allowed.
TrustedUser *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. The target user
// defaults to the current effective user if none is specified.
func NewContextFromPath(path string, targetUser *user.User) (*Context, error) {
ctx, err := newContextFromUser(targetUser)
if err != nil {
return nil, err
}
if ctx.Mount, err = filesystem.FindMount(path); err != nil {
return nil, err
}
log.Printf("%s is on %s filesystem %q (%s)", path,
ctx.Mount.FilesystemType, ctx.Mount.Path, ctx.Mount.Device)
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. The target user
// defaults to the current effective user if none is specified.
func NewContextFromMountpoint(mountpoint string, targetUser *user.User) (*Context, error) {
ctx, err := newContextFromUser(targetUser)
if err != nil {
return nil, err
}
if ctx.Mount, err = filesystem.GetMount(mountpoint); err != nil {
return nil, err
}
log.Printf("found %s filesystem %q (%s)", ctx.Mount.FilesystemType,
ctx.Mount.Path, ctx.Mount.Device)
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 user is
// nil, the effective user is used.
func newContextFromUser(targetUser *user.User) (*Context, error) {
var err error
if targetUser == nil {
if targetUser, err = util.EffectiveUser(); err != nil {
return nil, err
}
}
ctx := &Context{TargetUser: targetUser}
if ctx.Config, err = getConfig(); err != nil {
return nil, err
}
// By default, when running as a non-root user we only read policies and
// protectors owned by the user or root. When running as root, we allow
// reading all policies and protectors.
if !ctx.Config.GetAllowCrossUserMetadata() && !util.IsUserRoot() {
ctx.TrustedUser, err = util.EffectiveUser()
if err != nil {
return nil, err
}
}
log.Printf("creating context for user %q", targetUser.Username)
return ctx, nil
}
// checkContext verifies that the context contains a valid config and a mount
// which is being used with fscrypt.
func (ctx *Context) checkContext() error {
if err := ctx.Config.CheckValidity(); err != nil {
return &ErrBadConfig{ctx.Config, err}
}
return ctx.Mount.CheckSetup(ctx.TrustedUser)
}
func (ctx *Context) getKeyringOptions() *keyring.Options {
return &keyring.Options{
Mount: ctx.Mount,
User: ctx.TargetUser,
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 {
mnt, data, err := ctx.Mount.GetProtector(protectorDescriptor, ctx.TrustedUser)
if err != nil {
return &ProtectorOption{ProtectorInfo{}, nil, err}
}
info := ProtectorInfo{data}
// No linked path if on the same mountpoint
if mnt == ctx.Mount {
return &ProtectorOption{info, nil, nil}
}
return &ProtectorOption{info, mnt, nil}
}
// 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(ctx.TrustedUser)
if err != nil {
return nil, err
}
options := make([]*ProtectorOption, len(descriptors))
for i, descriptor := range descriptors {
options[i] = ctx.getProtectorOption(descriptor)
}
return options, nil
}

622
vendor/github.com/google/fscrypt/actions/policy.go generated vendored Normal file
View File

@ -0,0 +1,622 @@
/*
* policy.go - functions for dealing with policies
*
* 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"
"os"
"os/user"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"
"github.com/google/fscrypt/crypto"
"github.com/google/fscrypt/filesystem"
"github.com/google/fscrypt/keyring"
"github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/util"
)
// ErrAccessDeniedPossiblyV2 indicates that a directory's encryption policy
// couldn't be retrieved due to "permission denied", but it looks like it's due
// to the directory using a v2 policy but the kernel not supporting it.
type ErrAccessDeniedPossiblyV2 struct {
DirPath string
}
func (err *ErrAccessDeniedPossiblyV2) Error() string {
return fmt.Sprintf(`
failed to get encryption policy of %s: permission denied
This may be caused by the directory using a v2 encryption policy and the
current kernel not supporting it. If indeed the case, then this
directory can only be used on kernel v5.4 and later. You can create
directories accessible on older kernels by changing policy_version to 1
in %s.`,
err.DirPath, ConfigFileLocation)
}
// ErrAlreadyProtected indicates that a policy is already protected by the given
// protector.
type ErrAlreadyProtected struct {
Policy *Policy
Protector *Protector
}
func (err *ErrAlreadyProtected) Error() string {
return fmt.Sprintf("policy %s is already protected by protector %s",
err.Policy.Descriptor(), err.Protector.Descriptor())
}
// ErrDifferentFilesystem indicates that a policy can't be applied to a
// directory on a different filesystem.
type ErrDifferentFilesystem struct {
PolicyMount *filesystem.Mount
PathMount *filesystem.Mount
}
func (err *ErrDifferentFilesystem) Error() string {
return fmt.Sprintf(`cannot apply policy from filesystem %q to a
directory on filesystem %q. Policies may only protect files on the same
filesystem.`, err.PolicyMount.Path, err.PathMount.Path)
}
// ErrMissingPolicyMetadata indicates that a directory is encrypted but its
// policy metadata cannot be found.
type ErrMissingPolicyMetadata struct {
Mount *filesystem.Mount
DirPath string
Descriptor string
}
func (err *ErrMissingPolicyMetadata) Error() string {
return fmt.Sprintf(`filesystem %q does not contain the policy metadata
for %q. This directory has either been encrypted with another tool (such
as e4crypt), or the file %q has been deleted.`,
err.Mount.Path, err.DirPath,
err.Mount.PolicyPath(err.Descriptor))
}
// ErrNotProtected indicates that the given policy is not protected by the given
// protector.
type ErrNotProtected struct {
PolicyDescriptor string
ProtectorDescriptor string
}
func (err *ErrNotProtected) Error() string {
return fmt.Sprintf(`policy %s is not protected by protector %s`,
err.PolicyDescriptor, err.ProtectorDescriptor)
}
// ErrOnlyProtector indicates that the last protector can't be removed from a
// policy.
type ErrOnlyProtector struct {
Policy *Policy
}
func (err *ErrOnlyProtector) Error() string {
return fmt.Sprintf(`cannot remove the only protector from policy %s. A
policy must have at least one protector.`, err.Policy.Descriptor())
}
// ErrPolicyMetadataMismatch indicates that the policy metadata for an encrypted
// directory is inconsistent with that directory.
type ErrPolicyMetadataMismatch struct {
DirPath string
Mount *filesystem.Mount
PathData *metadata.PolicyData
MountData *metadata.PolicyData
}
func (err *ErrPolicyMetadataMismatch) Error() string {
return fmt.Sprintf(`inconsistent metadata between encrypted directory %q
and its corresponding metadata file %q.
Directory has descriptor:%s %s
Metadata file has descriptor:%s %s`,
err.DirPath, err.Mount.PolicyPath(err.PathData.KeyDescriptor),
err.PathData.KeyDescriptor, err.PathData.Options,
err.MountData.KeyDescriptor, err.MountData.Options)
}
// PurgeAllPolicies removes all policy keys on the filesystem from the kernel
// 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
}
policies, err := ctx.Mount.ListPolicies(nil)
if err != nil {
return err
}
for _, policyDescriptor := range policies {
err = keyring.RemoveEncryptionKey(policyDescriptor, ctx.getKeyringOptions(), false)
switch errors.Cause(err) {
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
}
}
return 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
// allow encrypted files to be accessed). As with the key struct, a Policy
// should be wiped after use.
type Policy struct {
Context *Context
data *metadata.PolicyData
key *crypto.Key
created bool
ownerIfCreating *user.User
newLinkedProtectors []string
}
// CreatePolicy creates a Policy protected by given Protector and stores the
// appropriate data on the filesystem. On error, no data is changed on the
// filesystem.
func CreatePolicy(ctx *Context, protector *Protector) (*Policy, error) {
if err := ctx.checkContext(); err != nil {
return nil, err
}
// Randomly create the underlying policy key (and wipe if we fail)
key, err := crypto.NewRandomKey(metadata.PolicyKeyLen)
if err != nil {
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: keyDescriptor,
},
key: key,
created: true,
}
policy.ownerIfCreating, err = getOwnerOfMetadataForProtector(protector)
if err != nil {
policy.Lock()
return nil, err
}
if err = policy.AddProtector(protector); err != nil {
policy.Lock()
return nil, err
}
return policy, nil
}
// 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, ctx.TrustedUser)
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)
err = ctx.Mount.EncryptionSupportError(err)
if err != nil {
// On kernels that don't support v2 encryption policies, trying
// to open a directory with a v2 policy simply gave EACCES. This
// is ambiguous with other errors, but try to detect this case
// and show a better error message.
if os.IsPermission(err) &&
filesystem.HaveReadAccessTo(path) &&
!keyring.IsFsKeyringSupported(ctx.Mount) {
return nil, &ErrAccessDeniedPossiblyV2{path}
}
return nil, err
}
descriptor := pathData.KeyDescriptor
log.Printf("found policy %s for %q", descriptor, path)
mountData, err := ctx.Mount.GetPolicy(descriptor, ctx.TrustedUser)
if err != nil {
log.Printf("getting policy metadata: %v", err)
if _, ok := err.(*filesystem.ErrPolicyNotFound); ok {
return nil, &ErrMissingPolicyMetadata{ctx.Mount, path, descriptor}
}
return nil, err
}
log.Printf("found data for policy %s on %q", descriptor, ctx.Mount.Path)
if !proto.Equal(pathData.Options, mountData.Options) ||
pathData.KeyDescriptor != mountData.KeyDescriptor {
return nil, &ErrPolicyMetadataMismatch{path, ctx.Mount, pathData, mountData}
}
log.Print("data from filesystem and path agree")
return &Policy{Context: ctx, data: mountData}, nil
}
// 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)
}
return options
}
// 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 {
descriptors[i] = wrappedPolicyKey.ProtectorDescriptor
}
return descriptors
}
// Descriptor returns the key descriptor for this policy.
func (policy *Policy) Descriptor() string {
return policy.data.KeyDescriptor
}
// 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. It also removes any new
// protector links that were created for the policy. This does *not* wipe the
// policy's internal key from memory; use Lock() to do that.
func (policy *Policy) Destroy() error {
for _, protectorDescriptor := range policy.newLinkedProtectors {
policy.Context.Mount.RemoveProtector(protectorDescriptor)
}
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.Descriptor(), options)
if err != nil {
return err
}
option := options[idx]
if option.LoadError != nil {
return option.LoadError
}
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.Descriptor())
wrappedPolicyKey := policy.data.WrappedPolicyKeys[idx].WrappedKey
policy.key, err = crypto.Unwrap(protectorKey, wrappedPolicyKey)
return err
}
// UnlockWithProtector uses an unlocked Protector to unlock a policy. An error
// is returned if the Protector is not yet unlocked or does not protect the
// policy. Does nothing if policy is already unlocked.
func (policy *Policy) UnlockWithProtector(protector *Protector) error {
if policy.key != nil {
return nil
}
if protector.key == nil {
return ErrLocked
}
idx, ok := policy.findWrappedKeyIndex(protector.Descriptor())
if !ok {
return &ErrNotProtected{policy.Descriptor(), protector.Descriptor()}
}
var err error
wrappedPolicyKey := policy.data.WrappedPolicyKeys[idx].WrappedKey
policy.key, err = crypto.Unwrap(protector.key, 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
}
// UsesProtector returns if the policy is protected with the protector
func (policy *Policy) UsesProtector(protector *Protector) bool {
_, ok := policy.findWrappedKeyIndex(protector.Descriptor())
return ok
}
// getOwnerOfMetadataForProtector returns the User to whom the owner of any new
// policies or protector links for the given protector should be set.
//
// This will return a non-nil value only when the protector is a login protector
// and the process is running as root. In this scenario, root is setting up
// encryption on the user's behalf, so we need to make new policies and
// protector links owned by the user (rather than root) to allow them to be read
// by the user, just like the login protector itself which is handled elsewhere.
func getOwnerOfMetadataForProtector(protector *Protector) (*user.User, error) {
if protector.data.Source == metadata.SourceType_pam_passphrase && util.IsUserRoot() {
owner, err := util.UserFromUID(protector.data.Uid)
if err != nil {
return nil, err
}
return owner, nil
}
return nil, nil
}
// 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. The policy and
// protector must both be unlocked.
func (policy *Policy) AddProtector(protector *Protector) error {
if policy.UsesProtector(protector) {
return &ErrAlreadyProtected{policy, protector}
}
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 {
log.Printf("policy on %s\n protector on %s\n", policy.Context.Mount, protector.Context.Mount)
ownerIfCreating, err := getOwnerOfMetadataForProtector(protector)
if err != nil {
return err
}
isNewLink, err := policy.Context.Mount.AddLinkedProtector(
protector.Descriptor(), protector.Context.Mount,
protector.Context.TrustedUser, ownerIfCreating)
if err != nil {
return err
}
if isNewLink {
policy.newLinkedProtectors = append(policy.newLinkedProtectors,
protector.Descriptor())
}
} else {
log.Printf("policy and protector both on %q", policy.Context.Mount)
}
// Create the wrapped policy key
wrappedKey, err := crypto.Wrap(protector.key, policy.key)
if err != nil {
return err
}
// Append the wrapped key to the data
policy.addKey(&metadata.WrappedPolicyKey{
ProtectorDescriptor: protector.Descriptor(),
WrappedKey: wrappedKey,
})
if err := policy.commitData(); err != nil {
// revert the addition on failure
policy.removeKey(len(policy.data.WrappedPolicyKeys) - 1)
return err
}
return nil
}
// RemoveProtector updates the data that is wrapping the Policy Key so that the
// protector with the given descriptor is no longer protecting the specified
// Policy. If an error is returned, no data has been changed. Note that the
// protector itself won't be removed, nor will a link to the protector be
// removed (in the case where the protector and policy are on different
// filesystems). The policy can be locked or unlocked.
func (policy *Policy) RemoveProtector(protectorDescriptor string) error {
idx, ok := policy.findWrappedKeyIndex(protectorDescriptor)
if !ok {
return &ErrNotProtected{policy.Descriptor(), protectorDescriptor}
}
if len(policy.data.WrappedPolicyKeys) == 1 {
return &ErrOnlyProtector{policy}
}
// Remove the wrapped key from the data
toRemove := policy.removeKey(idx)
if err := policy.commitData(); err != nil {
// revert the removal on failure (order is irrelevant)
policy.addKey(toRemove)
return err
}
return nil
}
// Apply sets the Policy on a specified directory. Currently we impose the
// additional constraint that policies and the directories they are applied to
// must reside on the same filesystem.
func (policy *Policy) Apply(path string) error {
if pathMount, err := filesystem.FindMount(path); err != nil {
return err
} else if pathMount != policy.Context.Mount {
return &ErrDifferentFilesystem{policy.Context.Mount, pathMount}
}
err := metadata.SetPolicy(path, policy.data)
return policy.Context.Mount.EncryptionSupportError(err)
}
// 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
}
// 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 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 --- unless the target keyring is a user
// keyring, in which case caches must be dropped too. If the Policy key was
// already removed, returns keyring.ErrKeyNotPresent.
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 (deprecated), 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.
func (policy *Policy) commitData() error {
return policy.Context.Mount.AddPolicy(policy.data, policy.ownerIfCreating)
}
// findWrappedPolicyKey returns the index of the wrapped policy key
// 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, true
}
}
return 0, false
}
// addKey adds the wrapped policy key to end of the wrapped key data.
func (policy *Policy) addKey(toAdd *metadata.WrappedPolicyKey) {
policy.data.WrappedPolicyKeys = append(policy.data.WrappedPolicyKeys, toAdd)
}
// removeKey removes the wrapped policy key at the specified index. This
// does not preserve the order of the wrapped policy key array. If no index is
// specified the last key is removed.
func (policy *Policy) removeKey(index int) *metadata.WrappedPolicyKey {
lastIdx := len(policy.data.WrappedPolicyKeys) - 1
toRemove := policy.data.WrappedPolicyKeys[index]
// See https://github.com/golang/go/wiki/SliceTricks
policy.data.WrappedPolicyKeys[index] = policy.data.WrappedPolicyKeys[lastIdx]
policy.data.WrappedPolicyKeys[lastIdx] = nil
policy.data.WrappedPolicyKeys = policy.data.WrappedPolicyKeys[:lastIdx]
return toRemove
}

300
vendor/github.com/google/fscrypt/actions/protector.go generated vendored Normal file
View File

@ -0,0 +1,300 @@
/*
* 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"
"os/user"
"github.com/google/fscrypt/crypto"
"github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/util"
)
// LoginProtectorMountpoint is the mountpoint where login protectors are stored.
// This can be overridden by the user of this package.
var LoginProtectorMountpoint = "/"
// ErrLoginProtectorExists indicates that a user already has a login protector.
type ErrLoginProtectorExists struct {
User *user.User
}
func (err *ErrLoginProtectorExists) Error() string {
return fmt.Sprintf("user %q already has a login protector", err.User.Username)
}
// ErrLoginProtectorName indicates that a name was given for a login protector.
type ErrLoginProtectorName struct {
Name string
User *user.User
}
func (err *ErrLoginProtectorName) Error() string {
return fmt.Sprintf(`cannot assign name %q to new login protector for
user %q because login protectors are identified by user, not by name.`,
err.Name, err.User.Username)
}
// ErrMissingProtectorName indicates that a protector name is needed.
type ErrMissingProtectorName struct {
Source metadata.SourceType
}
func (err *ErrMissingProtectorName) Error() string {
return fmt.Sprintf("%s protectors must be named", err.Source)
}
// ErrProtectorNameExists indicates that a protector name already exists.
type ErrProtectorNameExists struct {
Name string
}
func (err *ErrProtectorNameExists) Error() string {
return fmt.Sprintf("there is already a protector named %q", err.Name)
}
// 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 &ErrProtectorNameExists{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 &ErrLoginProtectorExists{ctx.TargetUser}
}
}
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
ownerIfCreating *user.User
}
// 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, owner *user.User) (*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, &ErrLoginProtectorName{name, ctx.TargetUser}
}
} else {
// non-login protectors need a name (so we can distinguish between them)
if name == "" {
return nil, &ErrMissingProtectorName{ctx.Config.Source}
}
// 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,
ownerIfCreating: owner,
}
// 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, err = crypto.ComputeKeyDescriptor(protector.key, 1)
if err != nil {
protector.Lock()
return nil, err
}
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, ctx.TrustedUser)
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, ctx.TrustedUser}
}
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, protector.ownerIfCreating)
}

131
vendor/github.com/google/fscrypt/actions/recovery.go generated vendored Normal file
View File

@ -0,0 +1,131 @@
/*
* recovery.go - support for generating recovery passphrases
*
* Copyright 2019 Google LLC
*
* 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"
"os"
"strconv"
"github.com/google/fscrypt/crypto"
"github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/util"
)
// modifiedContextWithSource returns a copy of ctx with the protector source
// replaced by source.
func modifiedContextWithSource(ctx *Context, source metadata.SourceType) *Context {
modifiedConfig := *ctx.Config
modifiedConfig.Source = source
modifiedCtx := *ctx
modifiedCtx.Config = &modifiedConfig
return &modifiedCtx
}
// AddRecoveryPassphrase randomly generates a recovery passphrase and adds it as
// a custom_passphrase protector for the given Policy.
func AddRecoveryPassphrase(policy *Policy, dirname string) (*crypto.Key, *Protector, error) {
// 20 random characters in a-z is 94 bits of entropy, which is way more
// than enough for a passphrase which still goes through the usual
// passphrase hashing which makes it extremely costly to brute force.
passphrase, err := crypto.NewRandomPassphrase(20)
if err != nil {
return nil, nil, err
}
defer func() {
if err != nil {
passphrase.Wipe()
}
}()
getPassphraseFn := func(info ProtectorInfo, retry bool) (*crypto.Key, error) {
// CreateProtector() wipes the passphrase, but in this case we
// still need it for later, so make a copy.
return passphrase.Clone()
}
var recoveryProtector *Protector
customCtx := modifiedContextWithSource(policy.Context, metadata.SourceType_custom_passphrase)
seq := 1
for {
// Automatically generate a name for the recovery protector.
name := "Recovery passphrase for " + dirname
if seq != 1 {
name += " (" + strconv.Itoa(seq) + ")"
}
recoveryProtector, err = CreateProtector(customCtx, name, getPassphraseFn, policy.ownerIfCreating)
if err == nil {
break
}
if _, ok := err.(*ErrProtectorNameExists); !ok {
return nil, nil, err
}
seq++
}
if err := policy.AddProtector(recoveryProtector); err != nil {
recoveryProtector.Revert()
return nil, nil, err
}
return passphrase, recoveryProtector, nil
}
// WriteRecoveryInstructions writes a recovery passphrase and instructions to a
// file. This file should initially be located in the encrypted directory
// protected by the passphrase itself. It's up to the user to store the
// passphrase in a different location if they actually need it.
func WriteRecoveryInstructions(recoveryPassphrase *crypto.Key, recoveryProtector *Protector,
policy *Policy, path string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
defer file.Close()
str := fmt.Sprintf(
`fscrypt automatically generated a recovery passphrase for this directory:
%s
It did this because you chose to protect this directory with your login
passphrase, but this directory is not on the root filesystem.
Copy this passphrase to a safe place if you want to still be able to unlock this
directory if you re-install the operating system or connect this storage media
to a different system (which would result in your login protector being lost).
To unlock this directory using this recovery passphrase, run 'fscrypt unlock'
and select the protector named %q.
If you want to disable recovery passphrase generation (not recommended),
re-create this directory and pass the --no-recovery option to 'fscrypt encrypt'.
Alternatively, you can remove this recovery passphrase protector using:
fscrypt metadata remove-protector-from-policy --force --protector=%s:%s --policy=%s:%s
It is safe to keep it around though, as the recovery passphrase is high-entropy.
`, recoveryPassphrase.Data(), recoveryProtector.data.Name,
recoveryProtector.Context.Mount.Path, recoveryProtector.data.ProtectorDescriptor,
policy.Context.Mount.Path, policy.data.KeyDescriptor)
if _, err = file.WriteString(str); err != nil {
return err
}
if recoveryProtector.ownerIfCreating != nil {
if err = util.Chown(file, recoveryProtector.ownerIfCreating); err != nil {
return err
}
}
return file.Sync()
}