aboutsummaryrefslogtreecommitdiff
path: root/cmd/fscrypt
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/fscrypt')
-rw-r--r--cmd/fscrypt/commands.go70
-rw-r--r--cmd/fscrypt/errors.go10
-rw-r--r--cmd/fscrypt/flags.go57
-rw-r--r--cmd/fscrypt/prompt.go2
-rw-r--r--cmd/fscrypt/protector.go4
-rw-r--r--cmd/fscrypt/setup.go5
-rw-r--r--cmd/fscrypt/status.go2
7 files changed, 104 insertions, 46 deletions
diff --git a/cmd/fscrypt/commands.go b/cmd/fscrypt/commands.go
index 3e8bc98..43c9cb0 100644
--- a/cmd/fscrypt/commands.go
+++ b/cmd/fscrypt/commands.go
@@ -25,8 +25,6 @@ import (
"log"
"os"
- "golang.org/x/sys/unix"
-
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -34,6 +32,7 @@ import (
"github.com/google/fscrypt/filesystem"
"github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/security"
+ "github.com/google/fscrypt/util"
)
// Setup is a command which can to global or per-filesystem initialization.
@@ -60,7 +59,6 @@ var Setup = cli.Command{
func setupAction(c *cli.Context) error {
var err error
-
switch c.NArg() {
case 0:
// Case (1) - global setup
@@ -92,7 +90,7 @@ var Encrypt = cli.Command{
immediately be used.`, directoryArg, shortDisplay(policyFlag),
shortDisplay(protectorFlag), mountpointArg),
Flags: []cli.Flag{policyFlag, unlockWithFlag, protectorFlag, sourceFlag,
- nameFlag, keyFileFlag, skipUnlockFlag},
+ userFlag, nameFlag, keyFileFlag, skipUnlockFlag},
Action: encryptAction,
}
@@ -121,7 +119,11 @@ func encryptAction(c *cli.Context) error {
// keyring unless --skip-unlock is used. On failure, an error is returned, any
// metadata creation is reverted, and the directory is unmodified.
func encryptPath(path string) (err error) {
- ctx, err := actions.NewContextFromPath(path)
+ target, err := parseUserFlag()
+ if err != nil {
+ return
+ }
+ ctx, err := actions.NewContextFromPath(path, target)
if err != nil {
return
}
@@ -133,7 +135,7 @@ func encryptPath(path string) (err error) {
if policyFlag.Value != "" {
log.Printf("getting policy for %q", path)
- policy, err = getPolicyFromFlag(policyFlag.Value)
+ policy, err = getPolicyFromFlag(policyFlag.Value, ctx.TargetUser)
} else {
log.Printf("creating policy for %q", path)
@@ -220,7 +222,7 @@ func checkEncryptable(ctx *actions.Context, path string) error {
// created a new protector.
func selectOrCreateProtector(ctx *actions.Context) (*actions.Protector, bool, error) {
if protectorFlag.Value != "" {
- protector, err := getProtectorFromFlag(protectorFlag.Value)
+ protector, err := getProtectorFromFlag(protectorFlag.Value, ctx.TargetUser)
return protector, false, err
}
@@ -263,7 +265,7 @@ var Unlock = cli.Command{
locked again upon reboot, or after running "fscrypt purge" and
unmounting the corresponding filesystem.`, directoryArg,
shortDisplay(unlockWithFlag)),
- Flags: []cli.Flag{unlockWithFlag, keyFileFlag},
+ Flags: []cli.Flag{unlockWithFlag, keyFileFlag, userFlag},
Action: unlockAction,
}
@@ -272,8 +274,12 @@ func unlockAction(c *cli.Context) error {
return expectedArgsErr(c, 1, false)
}
+ target, err := parseUserFlag()
+ if err != nil {
+ return newExitError(c, err)
+ }
path := c.Args().Get(0)
- ctx, err := actions.NewContextFromPath(path)
+ ctx, err := actions.NewContextFromPath(path, target)
if err != nil {
return newExitError(c, err)
}
@@ -336,7 +342,7 @@ var Purge = cli.Command{
can be eliminated by cycling the power or mitigated by using
page cache and slab cache poisoning.`, mountpointArg,
shortDisplay(dropCachesFlag)),
- Flags: []cli.Flag{forceFlag, dropCachesFlag},
+ Flags: []cli.Flag{forceFlag, dropCachesFlag, userFlag},
Action: purgeAction,
}
@@ -346,12 +352,17 @@ func purgeAction(c *cli.Context) error {
}
if dropCachesFlag.Value {
- if unix.Geteuid() != 0 {
+ if !util.IsUserRoot() {
return newExitError(c, ErrDropCachesPerm)
}
}
- ctx, err := actions.NewContextFromMountpoint(c.Args().Get(0))
+ target, err := parseUserFlag()
+ if err != nil {
+ return newExitError(c, err)
+ }
+ mountpoint := c.Args().Get(0)
+ ctx, err := actions.NewContextFromMountpoint(mountpoint, target)
if err != nil {
return newExitError(c, err)
}
@@ -417,7 +428,7 @@ func statusAction(c *cli.Context) error {
err = writeGlobalStatus(c.App.Writer)
case 1:
path := c.Args().Get(0)
- ctx, mntErr := actions.NewContextFromMountpoint(path)
+ ctx, mntErr := actions.NewContextFromMountpoint(path, nil)
switch errors.Cause(mntErr) {
case nil:
@@ -487,7 +498,7 @@ var createProtector = cli.Command{
applicable). As with "fscrypt encrypt", these prompts can be
disabled with the appropriate flags.`, mountpointArg,
shortDisplay(protectorFlag)),
- Flags: []cli.Flag{sourceFlag, nameFlag, keyFileFlag},
+ Flags: []cli.Flag{sourceFlag, nameFlag, keyFileFlag, userFlag},
Action: createProtectorAction,
}
@@ -496,7 +507,12 @@ func createProtectorAction(c *cli.Context) error {
return expectedArgsErr(c, 1, false)
}
- ctx, err := actions.NewContextFromMountpoint(c.Args().Get(0))
+ target, err := parseUserFlag()
+ if err != nil {
+ return newExitError(c, err)
+ }
+ mountpoint := c.Args().Get(0)
+ ctx, err := actions.NewContextFromMountpoint(mountpoint, target)
if err != nil {
return newExitError(c, err)
}
@@ -539,7 +555,7 @@ func createPolicyAction(c *cli.Context) error {
return expectedArgsErr(c, 1, false)
}
- ctx, err := actions.NewContextFromMountpoint(c.Args().Get(0))
+ ctx, err := actions.NewContextFromMountpoint(c.Args().Get(0), nil)
if err != nil {
return newExitError(c, err)
}
@@ -547,7 +563,7 @@ func createPolicyAction(c *cli.Context) error {
if err := checkRequiredFlags(c, []*stringFlag{protectorFlag}); err != nil {
return err
}
- protector, err := getProtectorFromFlag(protectorFlag.Value)
+ protector, err := getProtectorFromFlag(protectorFlag.Value, ctx.TargetUser)
if err != nil {
return newExitError(c, err)
}
@@ -608,7 +624,7 @@ func destoryMetadataAction(c *cli.Context) error {
switch {
case protectorFlag.Value != "":
// Case (1) - protector destroy
- protector, err := getProtectorFromFlag(protectorFlag.Value)
+ protector, err := getProtectorFromFlag(protectorFlag.Value, nil)
if err != nil {
return newExitError(c, err)
}
@@ -627,7 +643,7 @@ func destoryMetadataAction(c *cli.Context) error {
protector.Descriptor(), protector.Context.Mount.Path)
case policyFlag.Value != "":
// Case (2) - policy destroy
- policy, err := getPolicyFromFlag(policyFlag.Value)
+ policy, err := getPolicyFromFlag(policyFlag.Value, nil)
if err != nil {
return newExitError(c, err)
}
@@ -654,7 +670,7 @@ func destoryMetadataAction(c *cli.Context) error {
case 1:
// Case (3) - mountpoint destroy
path := c.Args().Get(0)
- ctx, err := actions.NewContextFromMountpoint(path)
+ ctx, err := actions.NewContextFromMountpoint(path, nil)
if err != nil {
return newExitError(c, err)
}
@@ -694,7 +710,7 @@ func changePassphraseAction(c *cli.Context) error {
return err
}
- protector, err := getProtectorFromFlag(protectorFlag.Value)
+ protector, err := getProtectorFromFlag(protectorFlag.Value, nil)
if err != nil {
return newExitError(c, err)
}
@@ -732,11 +748,11 @@ func addProtectorAction(c *cli.Context) error {
return err
}
- protector, err := getProtectorFromFlag(protectorFlag.Value)
+ protector, err := getProtectorFromFlag(protectorFlag.Value, nil)
if err != nil {
return newExitError(c, err)
}
- policy, err := getPolicyFromFlag(policyFlag.Value)
+ policy, err := getPolicyFromFlag(policyFlag.Value, protector.Context.TargetUser)
if err != nil {
return newExitError(c, err)
}
@@ -790,11 +806,11 @@ func removeProtectorAction(c *cli.Context) error {
}
// We do not need to unlock anything for this operation
- protector, err := getProtectorFromFlag(protectorFlag.Value)
+ protector, err := getProtectorFromFlag(protectorFlag.Value, nil)
if err != nil {
return newExitError(c, err)
}
- policy, err := getPolicyFromFlag(policyFlag.Value)
+ policy, err := getPolicyFromFlag(policyFlag.Value, protector.Context.TargetUser)
if err != nil {
return newExitError(c, err)
}
@@ -834,14 +850,14 @@ func dumpMetadataAction(c *cli.Context) error {
switch {
case protectorFlag.Value != "":
// Case (1) - protector print
- protector, err := getProtectorFromFlag(protectorFlag.Value)
+ protector, err := getProtectorFromFlag(protectorFlag.Value, nil)
if err != nil {
return newExitError(c, err)
}
fmt.Fprintln(c.App.Writer, protector)
case policyFlag.Value != "":
// Case (2) - policy print
- policy, err := getPolicyFromFlag(policyFlag.Value)
+ policy, err := getPolicyFromFlag(policyFlag.Value, nil)
if err != nil {
return newExitError(c, err)
}
diff --git a/cmd/fscrypt/errors.go b/cmd/fscrypt/errors.go
index b2aa57e..88525d1 100644
--- a/cmd/fscrypt/errors.go
+++ b/cmd/fscrypt/errors.go
@@ -60,6 +60,8 @@ var (
ErrNotPassphrase = errors.New("protector does not use a passphrase")
ErrUnknownUser = errors.New("unknown user")
ErrDropCachesPerm = errors.New("inode cache can only be dropped as root")
+ ErrSpecifyUser = errors.New("user must be specified when run as root")
+ ErrSpecifyNonRootUser = errors.New("non-root user must be specified")
)
var loadHelpText = fmt.Sprintf("You may need to mount a linked filesystem. Run with %s for more information.", shortDisplay(verboseFlag))
@@ -125,6 +127,14 @@ func getErrorSuggestions(err error) string {
properly clear the inode cache, or it should be run with
%s=false (this may leave encrypted files and directories
in an accessible state).`, shortDisplay(dropCachesFlag))
+ case ErrSpecifyUser:
+ return fmt.Sprintf(`When running this command as root, you
+ usually still want to provision/remove keys for a normal
+ user's keyring and use a normal user's login passphrase
+ as a protector (so the corresponding files will be
+ accessible for that user). This can be done with %s. To
+ use the root user's keyring or passphrase, use
+ --%s=root.`, shortDisplay(userFlag), userFlag.GetName())
case ErrAllLoadsFailed:
return loadHelpText
default:
diff --git a/cmd/fscrypt/flags.go b/cmd/fscrypt/flags.go
index a06b952..e883a6d 100644
--- a/cmd/fscrypt/flags.go
+++ b/cmd/fscrypt/flags.go
@@ -25,6 +25,7 @@ import (
"flag"
"fmt"
"log"
+ "os/user"
"regexp"
"strconv"
"time"
@@ -32,6 +33,7 @@ import (
"github.com/urfave/cli"
"github.com/google/fscrypt/actions"
+ "github.com/google/fscrypt/util"
)
// We define the types boolFlag, durationFlag, and stringFlag here instead of
@@ -204,6 +206,12 @@ var (
formatted as raw binary and should be exactly 32 bytes
long.`,
}
+ userFlag = &stringFlag{
+ Name: "user",
+ ArgName: "USERNAME",
+ Usage: `Specifiy which user should be used for login passphrases
+ or to which user's keyring keys should be provisioned.`,
+ }
protectorFlag = &stringFlag{
Name: "protector",
ArgName: "MOUNTPOINT:ID",
@@ -233,27 +241,31 @@ var (
// group is required and corresponds to the descriptor.
var idFlagRegex = regexp.MustCompile("^([[:print:]]+):([[:alnum:]]+)$")
+func matchMetadataFlag(flagValue string) (mountpoint, descriptor string, err error) {
+ matches := idFlagRegex.FindStringSubmatch(flagValue)
+ if matches == nil {
+ return "", "", fmt.Errorf("flag value %q does not have format %s",
+ flagValue, mountpointIDArg)
+ }
+ log.Printf("parsed flag: mountpoint=%q descriptor=%s", matches[1], matches[2])
+ return matches[1], matches[2], nil
+}
+
// parseMetadataFlag takes the value of either protectorFlag or policyFlag
// formatted as MOUNTPOINT:DESCRIPTOR, and returns a context for the mountpoint
// and a string for the descriptor.
-func parseMetadataFlag(flagValue string) (*actions.Context, string, error) {
- matches := idFlagRegex.FindStringSubmatch(flagValue)
- if matches == nil {
- err := fmt.Errorf("flag value %q does not have format %s", flagValue, mountpointIDArg)
+func parseMetadataFlag(flagValue string, target *user.User) (*actions.Context, string, error) {
+ mountpoint, descriptor, err := matchMetadataFlag(flagValue)
+ if err != nil {
return nil, "", err
}
-
- mountpoint := matches[1]
- descriptor := matches[2]
- log.Printf("parsed flag: mountpoint=%q descriptor=%s", mountpoint, descriptor)
-
- ctx, err := actions.NewContextFromMountpoint(mountpoint)
+ ctx, err := actions.NewContextFromMountpoint(mountpoint, target)
return ctx, descriptor, err
}
// getProtectorFromFlag gets an existing locked protector from protectorFlag.
-func getProtectorFromFlag(flagValue string) (*actions.Protector, error) {
- ctx, descriptor, err := parseMetadataFlag(flagValue)
+func getProtectorFromFlag(flagValue string, target *user.User) (*actions.Protector, error) {
+ ctx, descriptor, err := parseMetadataFlag(flagValue, target)
if err != nil {
return nil, err
}
@@ -261,10 +273,27 @@ func getProtectorFromFlag(flagValue string) (*actions.Protector, error) {
}
// getPolicyFromFlag gets an existing locked policy from policyFlag.
-func getPolicyFromFlag(flagValue string) (*actions.Policy, error) {
- ctx, descriptor, err := parseMetadataFlag(flagValue)
+func getPolicyFromFlag(flagValue string, target *user.User) (*actions.Policy, error) {
+ ctx, descriptor, err := parseMetadataFlag(flagValue, target)
if err != nil {
return nil, err
}
return actions.GetPolicy(ctx, descriptor)
}
+
+// parseUserFlag returns the user specified by userFlag or the current effective
+// user if the flag value is missing. If the effective user is root, however, a
+// user must specified in the flag.
+func parseUserFlag() (*user.User, error) {
+ if userFlag.Value != "" {
+ return user.Lookup(userFlag.Value)
+ }
+ effectiveUser, err := util.EffectiveUser()
+ if err != nil {
+ return nil, err
+ }
+ if util.IsUserRoot() {
+ return nil, ErrSpecifyUser
+ }
+ return effectiveUser, nil
+}
diff --git a/cmd/fscrypt/prompt.go b/cmd/fscrypt/prompt.go
index b882c08..0031e8f 100644
--- a/cmd/fscrypt/prompt.go
+++ b/cmd/fscrypt/prompt.go
@@ -308,7 +308,7 @@ func optionFn(policyDescriptor string, options []*actions.ProtectorOption) (int,
// protector to unlock the policy.
if unlockWithFlag.Value != "" {
log.Printf("optionFn(%s) w/ unlock flag", policyDescriptor)
- protector, err := getProtectorFromFlag(unlockWithFlag.Value)
+ protector, err := getProtectorFromFlag(unlockWithFlag.Value, nil)
if err != nil {
return 0, err
}
diff --git a/cmd/fscrypt/protector.go b/cmd/fscrypt/protector.go
index f54d3a4..32ba4ab 100644
--- a/cmd/fscrypt/protector.go
+++ b/cmd/fscrypt/protector.go
@@ -119,5 +119,7 @@ func modifiedContext(ctx *actions.Context) (*actions.Context, error) {
return nil, err
}
- return &actions.Context{Config: ctx.Config, Mount: mnt}, nil
+ modifiedCtx := *ctx
+ modifiedCtx.Mount = mnt
+ return &modifiedCtx, nil
}
diff --git a/cmd/fscrypt/setup.go b/cmd/fscrypt/setup.go
index 6433b0c..72dfbdb 100644
--- a/cmd/fscrypt/setup.go
+++ b/cmd/fscrypt/setup.go
@@ -26,11 +26,12 @@ import (
"os"
"github.com/google/fscrypt/actions"
+ "github.com/google/fscrypt/util"
)
// createGlobalConfig creates (or overwrites) the global config file
func createGlobalConfig(w io.Writer, path string) error {
- if os.Getuid() != 0 {
+ if !util.IsUserRoot() {
return ErrMustBeRoot
}
@@ -61,7 +62,7 @@ func createGlobalConfig(w io.Writer, path string) error {
// setupFilesystem creates the directories for a filesystem to use fscrypt.
func setupFilesystem(w io.Writer, path string) error {
- ctx, err := actions.NewContextFromMountpoint(path)
+ ctx, err := actions.NewContextFromMountpoint(path, nil)
if err != nil {
return err
}
diff --git a/cmd/fscrypt/status.go b/cmd/fscrypt/status.go
index c2adad7..1465a4e 100644
--- a/cmd/fscrypt/status.go
+++ b/cmd/fscrypt/status.go
@@ -166,7 +166,7 @@ func writeFilesystemStatus(w io.Writer, ctx *actions.Context) error {
}
func writePathStatus(w io.Writer, path string) error {
- ctx, err := actions.NewContextFromPath(path)
+ ctx, err := actions.NewContextFromPath(path, nil)
if err != nil {
return err
}