diff options
Diffstat (limited to 'cmd/fscrypt')
| -rw-r--r-- | cmd/fscrypt/commands.go | 746 | ||||
| -rw-r--r-- | cmd/fscrypt/errors.go | 21 | ||||
| -rw-r--r-- | cmd/fscrypt/flags.go | 27 | ||||
| -rw-r--r-- | cmd/fscrypt/fscrypt.go | 159 | ||||
| -rw-r--r-- | cmd/fscrypt/status.go | 37 |
5 files changed, 513 insertions, 477 deletions
diff --git a/cmd/fscrypt/commands.go b/cmd/fscrypt/commands.go index 4eec1e6..bbfcf2b 100644 --- a/cmd/fscrypt/commands.go +++ b/cmd/fscrypt/commands.go @@ -21,14 +21,13 @@ package main import ( - "fmt" "log" "os" "github.com/pkg/errors" - "github.com/urfave/cli" "github.com/google/fscrypt/actions" + "github.com/google/fscrypt/cmd" "github.com/google/fscrypt/metadata" ) @@ -193,7 +192,7 @@ func selectOrCreateProtector(ctx *actions.Context) (*actions.Protector, bool, er return protector, true, err } - shouldCreate, err := askQuestion("Should we create a new protector?", false) + shouldCreate, err := cmd.AskQuestion("Should we create a new protector?", false) if err != nil { return nil, false, err } @@ -316,423 +315,348 @@ func unlockPath(path string) error { // Action: statusAction, // } -// Metadata is a collection of commands for manipulating the metadata files. -var Metadata = cli.Command{ - Name: "metadata", - Usage: "[ADVANCED] manipulate the policy or protector metadata", - Description: `These commands allow a user to directly create, delete, or - change the metadata files. It is important to note that using - these commands, especially the destructive ones, can make files - encrypted with fscrypt unavailable. For instance, deleting a - policy effectively deletes all the contents of the corresponding - directory. Some example use cases include: - - (1) Directly creating protectors and policies using the "create" - subcommand. These can then be applied with "fscrypt encrypt". - - (2) Changing the passphrase for a passphrase protector using the - "change-passphrase" subcommand. - - (3) Creating a policy protected with multiple protectors using - the "create policy" and "add-protector-to-policy" subcommands. - - (4) Changing the protector protecting a policy using the - "add-protector-to-policy" and "remove-protector-from-policy" - subcommands.`, - Subcommands: []cli.Command{createMetadata, destoryMetadata, changePassphrase, - addProtectorToPolicy, removeProtectorFromPolicy, dumpMetadata}, -} - -var createMetadata = cli.Command{ - Name: "create", - ArgsUsage: fmt.Sprintf("[protector | policy] %s", mountpointArg), - Usage: "manually create new metadata on a filesystem", - Subcommands: []cli.Command{createProtector, createPolicy}, -} - -var createProtector = cli.Command{ - Name: "protector", - ArgsUsage: mountpointArg, - Usage: "create a new protector on a filesystem", - Description: fmt.Sprintf(`This command creates a new protector on %s - that does not (yet) protect any policy. After creation, the user - can use %s with "fscrypt encrypt" to protect a directory with - this new protector. The creation process is identical to the - first step of "fscrypt encrypt" when the user has requested to - create a new passphrase. The user will be prompted for the - source, name, and secret data for the new protector (when - applicable). As with "fscrypt encrypt", these prompts can be - disabled with the appropriate flags.`, mountpointArg, - shortDisplay(protectorFlag)), - Flags: []cli.Flag{sourceFlag, nameFlag, keyFileFlag, userFlag}, - Action: createProtectorAction, -} - -func createProtectorAction(c *cli.Context) error { - if c.NArg() != 1 { - return expectedArgsErr(c, 1, false) - } - - target, err := parseUserFlag(false) - 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) - } - - prompt := fmt.Sprintf("Create new protector on %q", ctx.Mount.Path) - if err := askConfirmation(prompt, true, ""); err != nil { - return newExitError(c, err) - } - - protector, err := createProtectorFromContext(ctx) - if err != nil { - return newExitError(c, err) - } - protector.Lock() - - fmt.Fprintf(c.App.Writer, "Protector %s created on filesystem %q.\n", - protector.Descriptor(), ctx.Mount.Path) - return nil -} - -var createPolicy = cli.Command{ - Name: "policy", - ArgsUsage: fmt.Sprintf("%s %s", mountpointArg, shortDisplay(protectorFlag)), - Usage: "create a new protector on a filesystem", - Description: fmt.Sprintf(`This command creates a new protector on %s - that has not (yet) been applied to any directory. After - creation, the user can use %s with "fscrypt encrypt" to encrypt - a directory with this new policy. As all policies must be - protected with at least one protector, this command requires - specifying one with %s. To create a policy protected by many - protectors, use this command and "fscrypt metadata - add-protector-to-policy".`, mountpointArg, - shortDisplay(policyFlag), shortDisplay(protectorFlag)), - Flags: []cli.Flag{protectorFlag, keyFileFlag}, - Action: createPolicyAction, -} - -func createPolicyAction(c *cli.Context) error { - if c.NArg() != 1 { - return expectedArgsErr(c, 1, false) - } - - ctx, err := actions.NewContextFromMountpoint(c.Args().Get(0), nil) - if err != nil { - return newExitError(c, err) - } - - if err := checkRequiredFlags(c, []*stringFlag{protectorFlag}); err != nil { - return err - } - protector, err := getProtectorFromFlag(protectorFlag.Value, ctx.TargetUser) - if err != nil { - return newExitError(c, err) - } - if err := protector.Unlock(existingKeyFn); err != nil { - return newExitError(c, err) - } - defer protector.Lock() - - prompt := fmt.Sprintf("Create new policy on %q", ctx.Mount.Path) - if err := askConfirmation(prompt, true, ""); err != nil { - return newExitError(c, err) - } - - policy, err := actions.CreatePolicy(ctx, protector) - if err != nil { - return newExitError(c, err) - } - policy.Lock() - - fmt.Fprintf(c.App.Writer, "Policy %s created on filesystem %q.\n", - policy.Descriptor(), ctx.Mount.Path) - return nil -} - -var destoryMetadata = cli.Command{ - Name: "destroy", - ArgsUsage: fmt.Sprintf("[%s | %s | %s]", shortDisplay(protectorFlag), - shortDisplay(policyFlag), mountpointArg), - Usage: "delete a filesystem's, protector's, or policy's metadata", - Description: fmt.Sprintf(`This command can be used to perform three - different destructive operations. Note that in all of these - cases, data will usually be lost, so use with care. - - (1) If used with %[1]s, this command deletes all the data - associated with that protector. This means all directories - protected with that protector will become PERMANENTLY - inaccessible (unless the policies were protected by multiple - protectors). - - (2) If used with %[2]s, this command deletes all the data - associated with that policy. This means all directories (usually - just one) using this policy will become PERMANENTLY - inaccessible. - - (3) If used with %[3]s, all the metadata on that filesystem will - be deleted, causing all directories on that filesystem using - fscrypt to become PERMANENTLY inaccessible. To start using this - directory again, "fscrypt setup %[3]s" will need to be rerun.`, - shortDisplay(protectorFlag), shortDisplay(policyFlag), - mountpointArg), - Flags: []cli.Flag{protectorFlag, policyFlag, forceFlag}, - Action: destoryMetadataAction, -} - -func destoryMetadataAction(c *cli.Context) error { - switch c.NArg() { - case 0: - switch { - case protectorFlag.Value != "": - // Case (1) - protector destroy - protector, err := getProtectorFromFlag(protectorFlag.Value, nil) - if err != nil { - return newExitError(c, err) - } - - prompt := fmt.Sprintf("Destroy protector %s on %q?", - protector.Descriptor(), protector.Context.Mount.Path) - warning := "All files protected only with this protector will be lost!!" - if err := askConfirmation(prompt, false, warning); err != nil { - return newExitError(c, err) - } - if err := protector.Destroy(); err != nil { - return newExitError(c, err) - } - - fmt.Fprintf(c.App.Writer, "Protector %s deleted from filesystem %q.\n", - protector.Descriptor(), protector.Context.Mount.Path) - case policyFlag.Value != "": - // Case (2) - policy destroy - policy, err := getPolicyFromFlag(policyFlag.Value, nil) - if err != nil { - return newExitError(c, err) - } - - prompt := fmt.Sprintf("Destroy policy %s on %q?", - policy.Descriptor(), policy.Context.Mount.Path) - warning := "All files using this policy will be lost!!" - if err := askConfirmation(prompt, false, warning); err != nil { - return newExitError(c, err) - } - if err := policy.Destroy(); err != nil { - return newExitError(c, err) - } - - fmt.Fprintf(c.App.Writer, "Policy %s deleted from filesystem %q.\n", - policy.Descriptor(), policy.Context.Mount.Path) - default: - message := fmt.Sprintf("Must specify one of: %s, %s, or %s", - mountpointArg, - shortDisplay(protectorFlag), - shortDisplay(policyFlag)) - return &usageError{c, message} - } - case 1: - // Case (3) - mountpoint destroy - path := c.Args().Get(0) - ctx, err := actions.NewContextFromMountpoint(path, nil) - if err != nil { - return newExitError(c, err) - } - - prompt := fmt.Sprintf("Destroy all the metadata on %q?", ctx.Mount.Path) - warning := "All the encrypted files on this filesystem will be lost!!" - if err := askConfirmation(prompt, false, warning); err != nil { - return newExitError(c, err) - } - if err := ctx.Mount.RemoveAllMetadata(); err != nil { - return newExitError(c, err) - } - - fmt.Fprintf(c.App.Writer, "All metadata on %q deleted.\n", ctx.Mount.Path) - default: - return expectedArgsErr(c, 1, true) - } - return nil -} - -var changePassphrase = cli.Command{ - Name: "change-passphrase", - ArgsUsage: shortDisplay(protectorFlag), - Usage: "change the passphrase used for a protector", - Description: `This command takes a specified passphrase protector and - changes the corresponding passphrase. Note that this does not - create or destroy any protectors.`, - Flags: []cli.Flag{protectorFlag}, - Action: changePassphraseAction, -} - -func changePassphraseAction(c *cli.Context) error { - if c.NArg() != 0 { - return expectedArgsErr(c, 0, false) - } - if err := checkRequiredFlags(c, []*stringFlag{protectorFlag}); err != nil { - return err - } - - protector, err := getProtectorFromFlag(protectorFlag.Value, nil) - if err != nil { - return newExitError(c, err) - } - if err := protector.Unlock(oldExistingKeyFn); err != nil { - return newExitError(c, err) - } - defer protector.Lock() - if err := protector.Rewrap(newCreateKeyFn); err != nil { - return newExitError(c, err) - } - - fmt.Fprintf(c.App.Writer, "Passphrase for protector %s successfully changed.\n", - protector.Descriptor()) - return nil -} - -var addProtectorToPolicy = cli.Command{ - Name: "add-protector-to-policy", - ArgsUsage: fmt.Sprintf("%s %s", shortDisplay(protectorFlag), shortDisplay(policyFlag)), - Usage: "start protecting a policy with some protector", - Description: `This command changes the specified policy to be - protected with the specified protector. This means that any - directories using this policy will now be accessible with this - protector. This command will fail if the policy is already - protected with this protector.`, - Flags: []cli.Flag{protectorFlag, policyFlag, unlockWithFlag, keyFileFlag}, - Action: addProtectorAction, -} - -func addProtectorAction(c *cli.Context) error { - if c.NArg() != 0 { - return expectedArgsErr(c, 0, false) - } - if err := checkRequiredFlags(c, []*stringFlag{protectorFlag, policyFlag}); err != nil { - return err - } +// var Metadata = cli.Command{ +// Name: "metadata", +// Usage: "[ADVANCED] manipulate the policy or protector metadata", +// Description: `These commands allow a user to directly create, delete, or +// change the metadata files. It is important to note that using +// these commands, especially the destructive ones, can make files +// encrypted with fscrypt unavailable. For instance, deleting a +// policy effectively deletes all the contents of the corresponding +// directory. Some example use cases include: + +// (1) Directly creating protectors and policies using the "create" +// subcommand. These can then be applied with "fscrypt encrypt". + +// (2) Changing the passphrase for a passphrase protector using the +// "change-passphrase" subcommand. + +// (3) Creating a policy protected with multiple protectors using +// the "create policy" and "add-protector-to-policy" subcommands. + +// (4) Changing the protector protecting a policy using the +// "add-protector-to-policy" and "remove-protector-from-policy" +// subcommands.`, +// Subcommands: []cli.Command{createMetadata, destoryMetadata, changePassphrase, +// addProtectorToPolicy, removeProtectorFromPolicy, dumpMetadata}, +// } - protector, err := getProtectorFromFlag(protectorFlag.Value, nil) - if err != nil { - return newExitError(c, err) - } - policy, err := getPolicyFromFlag(policyFlag.Value, protector.Context.TargetUser) - if err != nil { - return newExitError(c, err) - } - // Sanity check before unlocking everything - if err := policy.AddProtector(protector); errors.Cause(err) != actions.ErrLocked { - return newExitError(c, err) - } +// var createProtector = cli.Command{ +// Name: "protector", +// ArgsUsage: mountpointArg, +// Usage: "create a new protector on a filesystem", +// Description: fmt.Sprintf(`This command creates a new protector on %s +// that does not (yet) protect any policy. After creation, the user +// can use %s with "fscrypt encrypt" to protect a directory with +// this new protector. The creation process is identical to the +// first step of "fscrypt encrypt" when the user has requested to +// create a new passphrase. The user will be prompted for the +// source, name, and secret data for the new protector (when +// applicable). As with "fscrypt encrypt", these prompts can be +// disabled with the appropriate flags.`, mountpointArg, +// shortDisplay(protectorFlag)), +// Flags: []cli.Flag{sourceFlag, nameFlag, keyFileFlag, userFlag}, +// Action: createProtectorAction, +// } - prompt := fmt.Sprintf("Protect policy %s with protector %s?", - policy.Descriptor(), protector.Descriptor()) - warning := "All files using this policy will be accessible with this protector!!" - if err := askConfirmation(prompt, true, warning); err != nil { - return newExitError(c, err) - } +// var createPolicy = cli.Command{ +// Name: "policy", +// ArgsUsage: fmt.Sprintf("%s %s", mountpointArg, shortDisplay(protectorFlag)), +// Usage: "create a new protector on a filesystem", +// Description: fmt.Sprintf(`This command creates a new protector on %s +// that has not (yet) been applied to any directory. After +// creation, the user can use %s with "fscrypt encrypt" to encrypt +// a directory with this new policy. As all policies must be +// protected with at least one protector, this command requires +// specifying one with %s. To create a policy protected by many +// protectors, use this command and "fscrypt metadata +// add-protector-to-policy".`, mountpointArg, +// shortDisplay(policyFlag), shortDisplay(protectorFlag)), +// Flags: []cli.Flag{protectorFlag, keyFileFlag}, +// Action: createPolicyAction, +// } - if err := protector.Unlock(existingKeyFn); err != nil { - return newExitError(c, err) - } - if err := policy.Unlock(optionFn, existingKeyFn); err != nil { - return newExitError(c, err) - } - if err := policy.AddProtector(protector); err != nil { - return newExitError(c, err) - } +//// ***** WIP END/BEGIN HERE ***** + +// var destoryMetadata = cli.Command{ +// Name: "destroy", +// ArgsUsage: fmt.Sprintf("[%s | %s | %s]", shortDisplay(protectorFlag), +// shortDisplay(policyFlag), mountpointArg), +// Usage: "delete a filesystem's, protector's, or policy's metadata", +// Description: fmt.Sprintf(`This command can be used to perform three +// different destructive operations. Note that in all of these +// cases, data will usually be lost, so use with care. + +// (1) If used with %[1]s, this command deletes all the data +// associated with that protector. This means all directories +// protected with that protector will become PERMANENTLY +// inaccessible (unless the policies were protected by multiple +// protectors). + +// (2) If used with %[2]s, this command deletes all the data +// associated with that policy. This means all directories (usually +// just one) using this policy will become PERMANENTLY +// inaccessible. + +// (3) If used with %[3]s, all the metadata on that filesystem will +// be deleted, causing all directories on that filesystem using +// fscrypt to become PERMANENTLY inaccessible. To start using this +// directory again, "fscrypt setup %[3]s" will need to be rerun.`, +// shortDisplay(protectorFlag), shortDisplay(policyFlag), +// mountpointArg), +// Flags: []cli.Flag{protectorFlag, policyFlag, forceFlag}, +// Action: destoryMetadataAction, +// } - fmt.Fprintf(c.App.Writer, "Protector %s now protecting policy %s.\n", - protector.Descriptor(), policy.Descriptor()) - return nil -} +// func destoryMetadataAction(c *cli.Context) error { +// switch c.NArg() { +// case 0: +// switch { +// case protectorFlag.Value != "": +// // Case (1) - protector destroy +// protector, err := getProtectorFromFlag(protectorFlag.Value, nil) +// if err != nil { +// return newExitError(c, err) +// } + +// prompt := fmt.Sprintf("Destroy protector %s on %q?", +// protector.Descriptor(), protector.Context.Mount.Path) +// warning := "All files protected only with this protector will be lost!!" +// if err := askConfirmation(prompt, false, warning); err != nil { +// return newExitError(c, err) +// } +// if err := protector.Destroy(); err != nil { +// return newExitError(c, err) +// } + +// fmt.Fprintf(c.App.Writer, "Protector %s deleted from filesystem %q.\n", +// protector.Descriptor(), protector.Context.Mount.Path) +// case policyFlag.Value != "": +// // Case (2) - policy destroy +// policy, err := getPolicyFromFlag(policyFlag.Value, nil) +// if err != nil { +// return newExitError(c, err) +// } + +// prompt := fmt.Sprintf("Destroy policy %s on %q?", +// policy.Descriptor(), policy.Context.Mount.Path) +// warning := "All files using this policy will be lost!!" +// if err := askConfirmation(prompt, false, warning); err != nil { +// return newExitError(c, err) +// } +// if err := policy.Destroy(); err != nil { +// return newExitError(c, err) +// } + +// fmt.Fprintf(c.App.Writer, "Policy %s deleted from filesystem %q.\n", +// policy.Descriptor(), policy.Context.Mount.Path) +// default: +// message := fmt.Sprintf("Must specify one of: %s, %s, or %s", +// mountpointArg, +// shortDisplay(protectorFlag), +// shortDisplay(policyFlag)) +// return &usageError{c, message} +// } +// case 1: +// // Case (3) - mountpoint destroy +// path := c.Args().Get(0) +// ctx, err := actions.NewContextFromMountpoint(path, nil) +// if err != nil { +// return newExitError(c, err) +// } + +// prompt := fmt.Sprintf("Destroy all the metadata on %q?", ctx.Mount.Path) +// warning := "All the encrypted files on this filesystem will be lost!!" +// if err := askConfirmation(prompt, false, warning); err != nil { +// return newExitError(c, err) +// } +// if err := ctx.Mount.RemoveAllMetadata(); err != nil { +// return newExitError(c, err) +// } + +// fmt.Fprintf(c.App.Writer, "All metadata on %q deleted.\n", ctx.Mount.Path) +// default: +// return expectedArgsErr(c, 1, true) +// } +// return nil +// } -var removeProtectorFromPolicy = cli.Command{ - Name: "remove-protector-from-policy", - ArgsUsage: fmt.Sprintf("%s %s", shortDisplay(protectorFlag), shortDisplay(policyFlag)), - Usage: "stop protecting a policy with some protector", - Description: `This command changes the specified policy to no longer be - protected with the specified protector. This means that any - directories using this policy will cannot be accessed with this - protector. This command will fail if the policy not already - protected with this protector or if it is the policy's only - protector.`, - Flags: []cli.Flag{protectorFlag, policyFlag, forceFlag}, - Action: removeProtectorAction, -} +// var changePassphrase = cli.Command{ +// Name: "change-passphrase", +// ArgsUsage: shortDisplay(protectorFlag), +// Usage: "change the passphrase used for a protector", +// Description: `This command takes a specified passphrase protector and +// changes the corresponding passphrase. Note that this does not +// create or destroy any protectors.`, +// Flags: []cli.Flag{protectorFlag}, +// Action: changePassphraseAction, +// } -func removeProtectorAction(c *cli.Context) error { - if c.NArg() != 0 { - return expectedArgsErr(c, 0, false) - } - if err := checkRequiredFlags(c, []*stringFlag{protectorFlag, policyFlag}); err != nil { - return err - } +// func changePassphraseAction(c *cli.Context) error { +// if c.NArg() != 0 { +// return expectedArgsErr(c, 0, false) +// } +// if err := checkRequiredFlags(c, []*stringFlag{protectorFlag}); err != nil { +// return err +// } + +// protector, err := getProtectorFromFlag(protectorFlag.Value, nil) +// if err != nil { +// return newExitError(c, err) +// } +// if err := protector.Unlock(oldExistingKeyFn); err != nil { +// return newExitError(c, err) +// } +// defer protector.Lock() +// if err := protector.Rewrap(newCreateKeyFn); err != nil { +// return newExitError(c, err) +// } + +// fmt.Fprintf(c.App.Writer, "Passphrase for protector %s successfully changed.\n", +// protector.Descriptor()) +// return nil +// } - // We do not need to unlock anything for this operation - protector, err := getProtectorFromFlag(protectorFlag.Value, nil) - if err != nil { - return newExitError(c, err) - } - policy, err := getPolicyFromFlag(policyFlag.Value, protector.Context.TargetUser) - if err != nil { - return newExitError(c, err) - } +// var addProtectorToPolicy = cli.Command{ +// Name: "add-protector-to-policy", +// ArgsUsage: fmt.Sprintf("%s %s", shortDisplay(protectorFlag), shortDisplay(policyFlag)), +// Usage: "start protecting a policy with some protector", +// Description: `This command changes the specified policy to be +// protected with the specified protector. This means that any +// directories using this policy will now be accessible with this +// protector. This command will fail if the policy is already +// protected with this protector.`, +// Flags: []cli.Flag{protectorFlag, policyFlag, unlockWithFlag, keyFileFlag}, +// Action: addProtectorAction, +// } - prompt := fmt.Sprintf("Stop protecting policy %s with protector %s?", - policy.Descriptor(), protector.Descriptor()) - warning := "All files using this policy will NO LONGER be accessible with this protector!!" - if err := askConfirmation(prompt, false, warning); err != nil { - return newExitError(c, err) - } +// func addProtectorAction(c *cli.Context) error { +// if c.NArg() != 0 { +// return expectedArgsErr(c, 0, false) +// } +// if err := checkRequiredFlags(c, []*stringFlag{protectorFlag, policyFlag}); err != nil { +// return err +// } + +// protector, err := getProtectorFromFlag(protectorFlag.Value, nil) +// if err != nil { +// return newExitError(c, err) +// } +// policy, err := getPolicyFromFlag(policyFlag.Value, protector.Context.TargetUser) +// if err != nil { +// return newExitError(c, err) +// } +// // Sanity check before unlocking everything +// if err := policy.AddProtector(protector); errors.Cause(err) != actions.ErrLocked { +// return newExitError(c, err) +// } + +// prompt := fmt.Sprintf("Protect policy %s with protector %s?", +// policy.Descriptor(), protector.Descriptor()) +// warning := "All files using this policy will be accessible with this protector!!" +// if err := askConfirmation(prompt, true, warning); err != nil { +// return newExitError(c, err) +// } + +// if err := protector.Unlock(existingKeyFn); err != nil { +// return newExitError(c, err) +// } +// if err := policy.Unlock(optionFn, existingKeyFn); err != nil { +// return newExitError(c, err) +// } +// if err := policy.AddProtector(protector); err != nil { +// return newExitError(c, err) +// } + +// fmt.Fprintf(c.App.Writer, "Protector %s now protecting policy %s.\n", +// protector.Descriptor(), policy.Descriptor()) +// return nil +// } - if err := policy.RemoveProtector(protector); err != nil { - return newExitError(c, err) - } +// var removeProtectorFromPolicy = cli.Command{ +// Name: "remove-protector-from-policy", +// ArgsUsage: fmt.Sprintf("%s %s", shortDisplay(protectorFlag), shortDisplay(policyFlag)), +// Usage: "stop protecting a policy with some protector", +// Description: `This command changes the specified policy to no longer be +// protected with the specified protector. This means that any +// directories using this policy will cannot be accessed with this +// protector. This command will fail if the policy not already +// protected with this protector or if it is the policy's only +// protector.`, +// Flags: []cli.Flag{protectorFlag, policyFlag, forceFlag}, +// Action: removeProtectorAction, +// } - fmt.Fprintf(c.App.Writer, "Protector %s no longer protecting policy %s.\n", - protector.Descriptor(), policy.Descriptor()) - return nil -} +// func removeProtectorAction(c *cli.Context) error { +// if c.NArg() != 0 { +// return expectedArgsErr(c, 0, false) +// } +// if err := checkRequiredFlags(c, []*stringFlag{protectorFlag, policyFlag}); err != nil { +// return err +// } + +// // We do not need to unlock anything for this operation +// protector, err := getProtectorFromFlag(protectorFlag.Value, nil) +// if err != nil { +// return newExitError(c, err) +// } +// policy, err := getPolicyFromFlag(policyFlag.Value, protector.Context.TargetUser) +// if err != nil { +// return newExitError(c, err) +// } + +// prompt := fmt.Sprintf("Stop protecting policy %s with protector %s?", +// policy.Descriptor(), protector.Descriptor()) +// warning := "All files using this policy will NO LONGER be accessible with this protector!!" +// if err := askConfirmation(prompt, false, warning); err != nil { +// return newExitError(c, err) +// } + +// if err := policy.RemoveProtector(protector); err != nil { +// return newExitError(c, err) +// } + +// fmt.Fprintf(c.App.Writer, "Protector %s no longer protecting policy %s.\n", +// protector.Descriptor(), policy.Descriptor()) +// return nil +// } -var dumpMetadata = cli.Command{ - Name: "dump", - ArgsUsage: fmt.Sprintf("[%s | %s]", shortDisplay(protectorFlag), shortDisplay(policyFlag)), - Usage: "print debug data for a policy or protector", - Description: fmt.Sprintf(`This commands dumps all of the debug data for - a protector (if %s is used) or policy (if %s is used). This data - includes the data pulled from the %q config file, the - appropriate mountpoint data, and any options for the policy or - hashing costs for the protector. Any cryptographic keys are - wiped and are not printed out.`, shortDisplay(protectorFlag), - shortDisplay(policyFlag), actions.ConfigFileLocation), - Flags: []cli.Flag{protectorFlag, policyFlag}, - Action: dumpMetadataAction, -} +// var dumpMetadata = cli.Command{ +// Name: "dump", +// ArgsUsage: fmt.Sprintf("[%s | %s]", shortDisplay(protectorFlag), shortDisplay(policyFlag)), +// Usage: "print debug data for a policy or protector", +// Description: fmt.Sprintf(`This commands dumps all of the debug data for +// a protector (if %s is used) or policy (if %s is used). This data +// includes the data pulled from the %q config file, the +// appropriate mountpoint data, and any options for the policy or +// hashing costs for the protector. Any cryptographic keys are +// wiped and are not printed out.`, shortDisplay(protectorFlag), +// shortDisplay(policyFlag), actions.ConfigFileLocation), +// Flags: []cli.Flag{protectorFlag, policyFlag}, +// Action: dumpMetadataAction, +// } -func dumpMetadataAction(c *cli.Context) error { - switch { - case protectorFlag.Value != "": - // Case (1) - protector print - 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, nil) - if err != nil { - return newExitError(c, err) - } - fmt.Fprintln(c.App.Writer, policy) - default: - message := fmt.Sprintf("Must specify one of: %s or %s", - shortDisplay(protectorFlag), - shortDisplay(policyFlag)) - return &usageError{c, message} - } - return nil -} +// func dumpMetadataAction(c *cli.Context) error { +// switch { +// case protectorFlag.Value != "": +// // Case (1) - protector print +// 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, nil) +// if err != nil { +// return newExitError(c, err) +// } +// fmt.Fprintln(c.App.Writer, policy) +// default: +// message := fmt.Sprintf("Must specify one of: %s or %s", +// shortDisplay(protectorFlag), +// shortDisplay(policyFlag)) +// return &usageError{c, message} +// } +// return nil +// } diff --git a/cmd/fscrypt/errors.go b/cmd/fscrypt/errors.go index 1c7ee75..4489fe5 100644 --- a/cmd/fscrypt/errors.go +++ b/cmd/fscrypt/errors.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" "github.com/google/fscrypt/actions" + "github.com/google/fscrypt/cmd" "github.com/google/fscrypt/crypto" "github.com/google/fscrypt/filesystem" "github.com/google/fscrypt/metadata" @@ -56,7 +57,7 @@ var ( ErrSpecifyUser = errors.New("user must be specified when run as root") ) -var loadHelpText = fmt.Sprintf("You may need to mount a linked filesystem. Run with %s for more information.", shortDisplay(verboseFlag)) +var loadHelpText = fmt.Sprintf("You may need to mount a linked filesystem. Run with %s for more information.", cmd.VerboseFlag) var fscryptHelpTextMap = map[error]string{ actions.ErrBadConfigFile: `Run "sudo fscrypt setup" to recreate the file.`, @@ -67,7 +68,7 @@ var fscryptHelpTextMap = map[error]string{ func getErrorSuggestions(err error) string { switch errors.Cause(err) { case filesystem.ErrNotSetup: - return fmt.Sprintf(`Run "fscrypt setup %s" to use fscrypt on this filesystem.`, mountpointArg) + return fmt.Sprintf(`Run "fscrypt setup %s" to use fscrypt on this filesystem.`, unusedMountpointArg) case crypto.ErrKeyLock: return `Too much memory was requested to be locked in RAM. The current limit for this user can be checked with "ulimit @@ -89,7 +90,7 @@ func getErrorSuggestions(err error) string { case security.ErrAccessUserKeyring: return fmt.Sprintf(`You can only use %s to access the user keyring of another user if you are running as root.`, - shortDisplay(userFlag)) + userFlag) case actions.ErrNoConfigFile: return `Run "sudo fscrypt setup" to create the file.` case actions.ErrMissingPolicyMetadata: @@ -101,13 +102,13 @@ func getErrorSuggestions(err error) string { inconsistent state. This most likely means the filesystem metadata is corrupted.` case actions.ErrMissingProtectorName: - return fmt.Sprintf("Use %s to specify a protector name.", shortDisplay(nameFlag)) - case ErrNoDesctructiveOps: - return fmt.Sprintf("Use %s to automatically run destructive operations.", shortDisplay(forceFlag)) + return fmt.Sprintf("Use %s to specify a protector name.", nameFlag) + case cmd.ErrMustForce: + return fmt.Sprintf("Use %s to automatically run destructive operations.", cmd.ForceFlag) case ErrSpecifyProtector: - return fmt.Sprintf("Use %s to specify a protector.", shortDisplay(protectorFlag)) + return fmt.Sprintf("Use %s to specify a protector.", protectorFlag) case ErrSpecifyKeyFile: - return fmt.Sprintf("Use %s to specify a key file.", shortDisplay(keyFileFlag)) + return fmt.Sprintf("Use %s to specify a key file.", keyFileFlag) case ErrBadOwners: return `Encryption can only be setup on directories you own, even if you have write permission for the directory.` @@ -120,7 +121,7 @@ func getErrorSuggestions(err error) string { return fmt.Sprintf(`Either this command should be run as root to 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)) + in an accessible state).`, dropCachesFlag) case ErrSpecifyUser: return fmt.Sprintf(`When running this command as root, you usually still want to provision/remove keys for a normal @@ -128,7 +129,7 @@ func getErrorSuggestions(err error) string { 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()) + --%s=root.`, userFlag, userFlag.Name) case ErrAllLoadsFailed: return loadHelpText default: diff --git a/cmd/fscrypt/flags.go b/cmd/fscrypt/flags.go index 2bf7f73..5983053 100644 --- a/cmd/fscrypt/flags.go +++ b/cmd/fscrypt/flags.go @@ -100,16 +100,17 @@ var ( Usage: `Specifiy which user should be used for login passphrases or to which user's keyring keys should be provisioned.`, } - protectorFlag = &cmd.StringFlag{ + mountpointIDArg = "MOUNTPOINT:ID" + protectorFlag = &cmd.StringFlag{ Name: "protector", - ArgName: "MOUNTPOINT:ID", + ArgName: mountpointIDArg, Usage: `Specify an existing protector on filesystem MOUNTPOINT with protector descriptor ID which should be used in the command.`, } unlockWithFlag = &cmd.StringFlag{ Name: "unlock-with", - ArgName: "MOUNTPOINT:ID", + ArgName: mountpointIDArg, Usage: `Specify an existing protector on filesystem MOUNTPOINT with protector descriptor ID which should be used to unlock a policy (usually specified with --policy). This @@ -119,7 +120,7 @@ var ( } policyFlag = &cmd.StringFlag{ Name: "policy", - ArgName: "MOUNTPOINT:ID", + ArgName: mountpointIDArg, Usage: `Specify an existing policy on filesystem MOUNTPOINT with key descriptor ID which should be used in the command.`, } @@ -175,19 +176,21 @@ func getPolicyFromFlag(flagValue string, target *user.User) (*actions.Policy, er // there are no problems accessing the user keyring. func parseUserFlag(checkKeyring bool) (targetUser *user.User, err error) { if userFlag.Value != "" { - targetUser, err = user.Lookup(userFlag.Value) + if targetUser, err = user.Lookup(userFlag.Value); err != nil { + return nil, err + } } else { - if util.IsUserRoot() { + targetID := util.CurrentUserID() + if targetID == 0 { return nil, ErrSpecifyUser } - targetUser, err = util.EffectiveUser() - } - if err != nil { - return nil, err + targetUser = util.GetUser(targetID) } if checkKeyring { - _, err = security.UserKeyringID(targetUser, true) + if _, err = security.UserKeyringID(targetUser, true); err != nil { + return nil, err + } } - return targetUser, err + return targetUser, nil } diff --git a/cmd/fscrypt/fscrypt.go b/cmd/fscrypt/fscrypt.go index 65da7cb..4348567 100644 --- a/cmd/fscrypt/fscrypt.go +++ b/cmd/fscrypt/fscrypt.go @@ -39,24 +39,26 @@ import ( // Arguments used in fscrypt commands. var ( unusedMountpointArg = &cmd.Argument{ - Name: "mountpoint", - Usage: "path to a mountpoint on which to setup fscrypt", + ArgName: "mountpoint", + Usage: "path to a mountpoint on which to setup fscrypt", } usedMountpointArg = &cmd.Argument{ - Name: "mountpoint", - Usage: "path to a mountpoint being used with fscrypt", + ArgName: "mountpoint", + Usage: "path to a mountpoint being used with fscrypt", } directoryToEncryptArg = &cmd.Argument{ - Name: "directory", - Usage: "path to an empty directory to encrypt with fscrypt", + ArgName: "directory", + Usage: "path to an empty directory to encrypt with fscrypt", } encryptedPathArg = &cmd.Argument{ - Name: "path", - Usage: "file or directory encrypted with fscrypt", + ArgName: "path", + Usage: "file or directory encrypted with fscrypt", } ) -func main() { fscryptCommand.Run() } +func main() { fscryptCommand.Run(fscryptHelpTextMap) } + +var baseFlags = []cmd.Flag{cmd.VerboseFlag, cmd.QuietFlag, cmd.HelpFlag} var fscryptCommand = cmd.Command{ Title: "manage linux filesystem encryption", @@ -65,16 +67,9 @@ var fscryptCommand = cmd.Command{ cmd.VerboseFlag, cmd.QuietFlag), cmd.VersionUsage, }, - SubCommands: []*Command{ - setupCommand, - encryptCommand, - unlockCommand, - purgeCommand, - // statusCommand, - // metadataCommand, - cmd.VersionCommand, - }, - Flags: []cmd.Flag{cmd.VerboseFlag, cmd.QuietFlag, cmd.HelpFlag}, + SubCommands: []*cmd.Command{setupCommand, encryptCommand, unlockCommand, + purgeCommand, statusCommand, metadataCommand, cmd.VersionCommand}, + Flags: baseFlags, ManPage: &cmd.ManPage{Name: "fscrypt", Section: 8}, } @@ -88,7 +83,7 @@ var setupCommand = &cmd.Command{ }, Arguments: []*cmd.Argument{unusedMountpointArg}, InheritFlags: true, - Flags: []cmd.Flag{configFileFlag, targetFlag, legacyFlag, cmd.ForceFlag}, + Flags: []cmd.Flag{timeTargetFlag, legacyFlag, cmd.ForceFlag}, ManPage: &cmd.ManPage{Name: "fscrypt-setup", Section: 8}, Action: setupAction, } @@ -97,7 +92,7 @@ func setupAction(c *cmd.Context) error { switch len(c.Args) { case 0: // Case (1) - global setup - return createGlobalConfig(configFileFlag.Value) + return createGlobalConfig(actions.ConfigFileLocation) case 1: // Case (2) - filesystem setup return setupFilesystem(c.Args[0]) @@ -110,7 +105,7 @@ func setupAction(c *cmd.Context) error { var encryptCommand = &cmd.Command{ Name: "encrypt", Title: "start encrypting an empty directory", - UsageLines: nil, // TODO(joerichey) + UsageLines: []string{"???"}, // TODO(joerichey) Arguments: []*cmd.Argument{directoryToEncryptArg}, InheritFlags: true, Flags: []cmd.Flag{sourceFlag, nameFlag, protectorFlag, policyFlag, @@ -143,7 +138,7 @@ func encryptAction(c *cmd.Context) error { var unlockCommand = &cmd.Command{ Name: "unlock", Title: "unlock an encrypted file or directory", - UsageLines: nil, // TODO(joerichey) + UsageLines: []string{"???"}, // TODO(joerichey) Arguments: []*cmd.Argument{encryptedPathArg}, InheritFlags: true, Flags: []cmd.Flag{protectorFlag, policyFlag, keyFileFlag, userFlag}, @@ -192,7 +187,7 @@ func purgeAction(c *cmd.Context) error { if err != nil { return err } - ctx, err := actions.NewContextFromMountpoint(c.Args[0], target) + ctx, err := actions.NewContextFromMountpoint(c.Args[0], targetUser) if err != nil { return err } @@ -211,7 +206,7 @@ func purgeAction(c *cmd.Context) error { fmt.Fprintf(cmd.Output, "Policies purged from filesystem %q.\n", ctx.Mount.Path) if !dropCachesFlag.Value { - fmt.Fprintf(cmd.Output, "Filesystem %q should now be unmounted.\n", cmd.Mount.Path) + fmt.Fprintf(cmd.Output, "Filesystem %q should now be unmounted.\n", ctx.Mount.Path) return nil } if err = security.DropFilesystemCache(); err != nil { @@ -251,6 +246,118 @@ func statusAction(c *cmd.Context) error { return mntErr } default: - return expectedArgsErr(c, 1, true) + return cmd.CheckExpectedArgs(c, 1, true) + } +} + +// metadata is a collection of commands for manipulating the metadata files. +var metadataCommand = &cmd.Command{ + Name: "metadata", + Title: "manipulate fscrypt metadata directly", + UsageLines: []string{fmt.Sprintf("<command> [command options] [%s] [%s]", + protectorFlag, policyFlag)}, + SubCommands: []*cmd.Command{createCommand}, // destroyCommand, changeCommand, + // addProtectorCommand, removeProtectorCommand, dumpCommand}, + InheritFlags: true, + Flags: []cmd.Flag{protectorFlag, policyFlag}, + ManPage: &cmd.ManPage{Name: "fscrypt-metadata", Section: 8}, +} + +var createCommand = &cmd.Command{ + Name: "create", + Title: "manually create metadata on a filesystem", + UsageLines: []string{ + fmt.Sprintf("protector %s", usedMountpointArg), + fmt.Sprintf("policy %s, %s", usedMountpointArg, protectorFlag), + }, + SubCommands: []*cmd.Command{createProtectorCommand, createPolicyCommand}, + Arguments: []*cmd.Argument{usedMountpointArg}, + Flags: baseFlags, +} + +var createProtectorCommand = &cmd.Command{ + Name: "protector", + Title: "create a protector without creating a policy", + UsageLines: []string{"???"}, // TODO(joerichey) + InheritArguments: true, + InheritFlags: true, + Flags: []cmd.Flag{sourceFlag, nameFlag, keyFileFlag, userFlag}, + Action: createProtectorAction, +} + +func createProtectorAction(c *cmd.Context) error { + if err := cmd.CheckExpectedArgs(c, 1, false); err != nil { + return err + } + + targetUser, err := parseUserFlag(true) + if err != nil { + return err + } + ctx, err := actions.NewContextFromMountpoint(c.Args[0], targetUser) + if err != nil { + return err + } + + prompt := fmt.Sprintf("Create new protector on %q", ctx.Mount.Path) + if err := cmd.AskConfirmation(prompt, "", true); err != nil { + return err + } + + protector, err := createProtectorFromContext(ctx) + if err != nil { + return err + } + protector.Lock() + + fmt.Fprintf(cmd.Output, "Protector %s created on filesystem %q.\n", + protector.Descriptor(), ctx.Mount.Path) + return nil +} + +var createPolicyCommand = &cmd.Command{ + Name: "policy", + Title: "create a policy using an existing protector", + UsageLines: []string{fmt.Sprintf("%s %s [%s]", + usedMountpointArg, protectorFlag, keyFileFlag)}, + InheritArguments: true, + InheritFlags: true, + Flags: []cmd.Flag{protectorFlag, keyFileFlag}, + Action: createPolicyAction, +} + +func createPolicyAction(c *cmd.Context) error { + if err := cmd.CheckExpectedArgs(c, 1, false); err != nil { + return err + } + if err := cmd.CheckRequiredFlags([]*cmd.StringFlag{protectorFlag}); err != nil { + return err + } + + ctx, err := actions.NewContextFromMountpoint(c.Args[0], nil) + if err != nil { + return err + } + protector, err := getProtectorFromFlag(protectorFlag.Value, ctx.TargetUser) + if err != nil { + return err + } + if err := protector.Unlock(existingKeyFn); err != nil { + return err } + defer protector.Lock() + + prompt := fmt.Sprintf("Create new policy on %q", ctx.Mount.Path) + if err := cmd.AskConfirmation(prompt, "", true); err != nil { + return err + } + policy, err := actions.CreatePolicy(ctx, protector) + if err != nil { + return err + } + policy.Lock() + + fmt.Fprintf(cmd.Output, "Policy %s created on filesystem %q.\n", + policy.Descriptor(), ctx.Mount.Path) + return nil } diff --git a/cmd/fscrypt/status.go b/cmd/fscrypt/status.go index 1465a4e..87799a8 100644 --- a/cmd/fscrypt/status.go +++ b/cmd/fscrypt/status.go @@ -30,6 +30,7 @@ import ( "github.com/pkg/errors" "github.com/google/fscrypt/actions" + "github.com/google/fscrypt/cmd" "github.com/google/fscrypt/filesystem" "github.com/google/fscrypt/metadata" ) @@ -66,7 +67,7 @@ func yesNoString(b bool) string { } // writeGlobalStatus prints all the filesystem that use (or could use) fscrypt. -func writeGlobalStatus(w io.Writer) error { +func writeGlobalStatus() error { mounts, err := filesystem.AllFilesystems() if err != nil { return err @@ -75,7 +76,7 @@ func writeGlobalStatus(w io.Writer) error { supportCount := 0 useCount := 0 - t := makeTableWriter(w, "MOUNTPOINT\tDEVICE\tFILESYSTEM\tENCRYPTION\tFSCRYPT") + t := makeTableWriter(cmd.Output, "MOUNTPOINT\tDEVICE\tFILESYSTEM\tENCRYPTION\tFSCRYPT") for _, mount := range mounts { // Only print mountpoints backed by devices or using fscrypt. usingFscrypt := mount.CheckSetup() == nil @@ -102,14 +103,14 @@ func writeGlobalStatus(w io.Writer) error { } } - fmt.Fprintf(w, "filesystems supporting encryption: %d\n", supportCount) - fmt.Fprintf(w, "filesystems with fscrypt metadata: %d\n\n", useCount) + fmt.Fprintf(cmd.Output, "filesystems supporting encryption: %d\n", supportCount) + fmt.Fprintf(cmd.Output, "filesystems with fscrypt metadata: %d\n\n", useCount) return t.Flush() } // writeOptions writes a table of the status for a slice of protector options. -func writeOptions(w io.Writer, options []*actions.ProtectorOption) { - t := makeTableWriter(w, "PROTECTOR\tLINKED\tDESCRIPTION") +func writeOptions(options []*actions.ProtectorOption) { + t := makeTableWriter(cmd.Output, "PROTECTOR\tLINKED\tDESCRIPTION") for _, option := range options { if option.LoadError != nil { fmt.Fprintf(t, "%s\t\t[%s]\n", option.Descriptor(), option.LoadError) @@ -128,7 +129,7 @@ func writeOptions(w io.Writer, options []*actions.ProtectorOption) { t.Flush() } -func writeFilesystemStatus(w io.Writer, ctx *actions.Context) error { +func writeFilesystemStatus(ctx *actions.Context) error { options, err := ctx.ProtectorOptions() if err != nil { return err @@ -143,15 +144,15 @@ func writeFilesystemStatus(w io.Writer, ctx *actions.Context) error { pluralize(len(options), "protector"), pluralize(len(policyDescriptors), "policy")) if len(options) > 0 { - writeOptions(w, options) + writeOptions(options) } if len(policyDescriptors) == 0 { return nil } - fmt.Fprintln(w) - t := makeTableWriter(w, "POLICY\tUNLOCKED\tPROTECTORS") + fmt.Fprintln(cmd.Output) + t := makeTableWriter(cmd.Output, "POLICY\tUNLOCKED\tPROTECTORS") for _, descriptor := range policyDescriptors { policy, err := actions.GetPolicy(ctx, descriptor) if err != nil { @@ -165,7 +166,7 @@ func writeFilesystemStatus(w io.Writer, ctx *actions.Context) error { return t.Flush() } -func writePathStatus(w io.Writer, path string) error { +func writePathStatus(path string) error { ctx, err := actions.NewContextFromPath(path, nil) if err != nil { return err @@ -175,14 +176,14 @@ func writePathStatus(w io.Writer, path string) error { return err } - fmt.Fprintf(w, "%q is encrypted with fscrypt.\n", path) - fmt.Fprintln(w) - fmt.Fprintf(w, "Policy: %s\n", policy.Descriptor()) - fmt.Fprintf(w, "Unlocked: %s\n", yesNoString(policy.IsProvisioned())) - fmt.Fprintln(w) + fmt.Fprintf(cmd.Output, "%q is encrypted with fscrypt.\n", path) + fmt.Fprintln(cmd.Output) + fmt.Fprintf(cmd.Output, "Policy: %s\n", policy.Descriptor()) + fmt.Fprintf(cmd.Output, "Unlocked: %s\n", yesNoString(policy.IsProvisioned())) + fmt.Fprintln(cmd.Output) options := policy.ProtectorOptions() - fmt.Fprintf(w, "Protected with %s:\n", pluralize(len(options), "protector")) - writeOptions(w, options) + fmt.Fprintf(cmd.Output, "Protected with %s:\n", pluralize(len(options), "protector")) + writeOptions(cmd.Output, options) return nil } |