Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow empty password #4698

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/restic/cmd_init.go
Expand Up @@ -36,6 +36,7 @@ type InitOptions struct {
secondaryRepoOptions
CopyChunkerParameters bool
RepositoryVersion string
AllowEmptyPassword bool
}

var initOptions InitOptions
Expand All @@ -47,6 +48,7 @@ func init() {
initSecondaryRepoOptions(f, &initOptions.secondaryRepoOptions, "secondary", "to copy chunker parameters from")
f.BoolVar(&initOptions.CopyChunkerParameters, "copy-chunker-params", false, "copy chunker parameters from the secondary repository (useful with the copy command)")
f.StringVar(&initOptions.RepositoryVersion, "repository-version", "stable", "repository format version to use, allowed values are a format version, 'latest' and 'stable'")
f.BoolVar(&initOptions.AllowEmptyPassword, "allow-empty-password", false, "allow an empty password (feel beeing warned)")
}

func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string) error {
Expand Down Expand Up @@ -82,7 +84,8 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []

gopts.password, err = ReadPasswordTwice(gopts,
"enter password for new repository: ",
"enter password again: ")
"enter password again: ",
opts.AllowEmptyPassword)
if err != nil {
return err
}
Expand Down
15 changes: 9 additions & 6 deletions cmd/restic/cmd_key_add.go
Expand Up @@ -29,9 +29,10 @@ Exit status is 0 if the command is successful, and non-zero if there was any err
}

type KeyAddOptions struct {
NewPasswordFile string
Username string
Hostname string
NewPasswordFile string
AllowEmptyPassword bool
Username string
Hostname string
}

var keyAddOpts KeyAddOptions
Expand All @@ -41,6 +42,7 @@ func init() {

flags := cmdKeyAdd.Flags()
flags.StringVarP(&keyAddOpts.NewPasswordFile, "new-password-file", "", "", "`file` from which to read the new password")
flags.BoolVar(&keyAddOpts.AllowEmptyPassword, "allow-empty-password", false, "allow an empty password (feel beeing warned)")
flags.StringVarP(&keyAddOpts.Username, "user", "", "", "the username for new key")
flags.StringVarP(&keyAddOpts.Hostname, "host", "", "", "the hostname for new key")
}
Expand All @@ -65,7 +67,7 @@ func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, arg
}

func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyAddOptions) error {
pw, err := getNewPassword(gopts, opts.NewPasswordFile)
pw, err := getNewPassword(gopts, opts.NewPasswordFile, opts.AllowEmptyPassword)
if err != nil {
return err
}
Expand All @@ -88,7 +90,7 @@ func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOption
// testKeyNewPassword is used to set a new password during integration testing.
var testKeyNewPassword string

func getNewPassword(gopts GlobalOptions, newPasswordFile string) (string, error) {
func getNewPassword(gopts GlobalOptions, newPasswordFile string, allowAllowEmptyPassword bool) (string, error) {
if testKeyNewPassword != "" {
return testKeyNewPassword, nil
}
Expand All @@ -104,7 +106,8 @@ func getNewPassword(gopts GlobalOptions, newPasswordFile string) (string, error)

return ReadPasswordTwice(newopts,
"enter new password: ",
"enter password again: ")
"enter password again: ",
allowAllowEmptyPassword)
}

func loadPasswordFromFile(pwdFile string) (string, error) {
Expand Down
3 changes: 2 additions & 1 deletion cmd/restic/cmd_key_passwd.go
Expand Up @@ -38,6 +38,7 @@ func init() {

flags := cmdKeyPasswd.Flags()
flags.StringVarP(&keyPasswdOpts.NewPasswordFile, "new-password-file", "", "", "`file` from which to read the new password")
flags.BoolVar(&keyPasswdOpts.AllowEmptyPassword, "allow-empty-password", false, "allow an empty password (feel beeing warned)")
flags.StringVarP(&keyPasswdOpts.Username, "user", "", "", "the username for new key")
flags.StringVarP(&keyPasswdOpts.Hostname, "host", "", "", "the hostname for new key")
}
Expand All @@ -62,7 +63,7 @@ func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOption
}

func changePassword(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyPasswdOptions) error {
pw, err := getNewPassword(gopts, opts.NewPasswordFile)
pw, err := getNewPassword(gopts, opts.NewPasswordFile, opts.AllowEmptyPassword)
if err != nil {
return err
}
Expand Down
25 changes: 18 additions & 7 deletions cmd/restic/global.go
Expand Up @@ -313,7 +313,11 @@ func resolvePassword(opts GlobalOptions, envStr string) (string, error) {
if errors.Is(err, os.ErrNotExist) {
return "", errors.Fatalf("%s does not exist", opts.PasswordFile)
}
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile")
pwd := strings.TrimSpace(string(s))
if pwd == "" {
pwd = emptyPassword
}
return pwd, errors.Wrap(err, "Readfile")
}

if pwd := os.Getenv(envStr); pwd != "" {
Expand Down Expand Up @@ -348,9 +352,11 @@ func readPasswordTerminal(in *os.File, out io.Writer, prompt string) (password s
return password, nil
}

const emptyPassword = "\xff"

// ReadPassword reads the password from a password file, the environment
// variable RESTIC_PASSWORD or prompts the user.
func ReadPassword(opts GlobalOptions, prompt string) (string, error) {
func ReadPassword(opts GlobalOptions, prompt string, allowAllowEmptyPassword bool) (string, error) {
if opts.password != "" {
return opts.password, nil
}
Expand All @@ -372,21 +378,26 @@ func ReadPassword(opts GlobalOptions, prompt string) (string, error) {
}

if len(password) == 0 {
return "", errors.Fatal("an empty password is not a password")
if allowAllowEmptyPassword {
Warnf("using an empty password is bad security practice\n")
return emptyPassword, nil
} else {
return "", errors.Fatal("an empty password is not a password")
}
}

return password, nil
}

// ReadPasswordTwice calls ReadPassword two times and returns an error when the
// passwords don't match.
func ReadPasswordTwice(gopts GlobalOptions, prompt1, prompt2 string) (string, error) {
pw1, err := ReadPassword(gopts, prompt1)
func ReadPasswordTwice(gopts GlobalOptions, prompt1, prompt2 string, allowAllowEmptyPassword bool) (string, error) {
pw1, err := ReadPassword(gopts, prompt1, allowAllowEmptyPassword)
if err != nil {
return "", err
}
if stdinIsTerminal() {
pw2, err := ReadPassword(gopts, prompt2)
pw2, err := ReadPassword(gopts, prompt2, allowAllowEmptyPassword)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -469,7 +480,7 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi
}

for ; passwordTriesLeft > 0; passwordTriesLeft-- {
opts.password, err = ReadPassword(opts, "enter password for repository: ")
opts.password, err = ReadPassword(opts, "enter password for repository: ", true)
if err != nil && passwordTriesLeft > 1 {
opts.password = ""
fmt.Printf("%s. Try again\n", err)
Expand Down
3 changes: 2 additions & 1 deletion cmd/restic/secondary_repo.go
Expand Up @@ -109,7 +109,8 @@ func fillSecondaryGlobalOpts(opts secondaryRepoOptions, gopts GlobalOptions, rep
return GlobalOptions{}, false, err
}
}
dstGopts.password, err = ReadPassword(dstGopts, "enter password for "+repoPrefix+" repository: ")
//FIXME: constant true okay?
dstGopts.password, err = ReadPassword(dstGopts, "enter password for "+repoPrefix+" repository: ", true)
if err != nil {
return GlobalOptions{}, false, err
}
Expand Down
73 changes: 73 additions & 0 deletions doc/bash-completion.sh
Expand Up @@ -1442,6 +1442,7 @@ _restic_init()
two_word_flags+=("--from-password-file")
local_nonpersistent_flags+=("--from-password-file")
local_nonpersistent_flags+=("--from-password-file=")
flags+=("--allow-empty-password")
flags+=("--from-repo=")
two_word_flags+=("--from-repo")
local_nonpersistent_flags+=("--from-repo")
Expand Down Expand Up @@ -1512,6 +1513,10 @@ _restic_key()
command_aliases=()

commands=()
commands+=("add")
commands+=("list")
commands+=("passwd")
commands+=("remove")

flags=()
two_word_flags=()
Expand Down Expand Up @@ -1582,6 +1587,74 @@ _restic_key()
noun_aliases=()
}

_restic_key_add()
{
last_command="restic_key_add"

command_aliases=()

commands=()

flags_with_completion=()
flags_completion=()

flags+=("--allow-empty-password")

must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}

_restic_key_list()
{
last_command="restic_key_list"

command_aliases=()

commands=()

flags_with_completion=()
flags_completion=()

must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}

_restic_key_passwd()
{
last_command="restic_key_passwd"

command_aliases=()

commands=()

flags_with_completion=()
flags_completion=()

flags+=("--allow-empty-password")

must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}

_restic_key_remove()
{
last_command="restic_key_remove"

command_aliases=()

commands=()

flags_with_completion=()
flags_completion=()

must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}

_restic_list()
{
last_command="restic_list"
Expand Down