mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor: vendor fscrypt integration dependencies
Signed-off-by: Marcel Lauhoff <marcel.lauhoff@suse.com>
This commit is contained in:
committed by
mergify[bot]
parent
cfea8d7562
commit
f8faffac89
132
vendor/github.com/google/fscrypt/actions/callback.go
generated
vendored
Normal file
132
vendor/github.com/google/fscrypt/actions/callback.go
generated
vendored
Normal 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
293
vendor/github.com/google/fscrypt/actions/config.go
generated
vendored
Normal 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
184
vendor/github.com/google/fscrypt/actions/context.go
generated
vendored
Normal 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
622
vendor/github.com/google/fscrypt/actions/policy.go
generated
vendored
Normal 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
300
vendor/github.com/google/fscrypt/actions/protector.go
generated
vendored
Normal 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
131
vendor/github.com/google/fscrypt/actions/recovery.go
generated
vendored
Normal 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()
|
||||
}
|
Reference in New Issue
Block a user