// withSafeTTYAndInterrupts invokes the provided function after the terminal // state has been stored, and then on any error or termination attempts to // restore the terminal state to its prior behavior. It also eats signals // for the duration of the function. func withSafeTTYAndInterrupts(fn func() error) error { ch := make(chan os.Signal, 1) signal.Notify(ch, childSignals...) defer signal.Stop(ch) inFd := os.Stdin.Fd() if !term.IsTerminal(inFd) { if f, err := os.Open("/dev/tty"); err == nil { defer f.Close() inFd = f.Fd() } } if term.IsTerminal(inFd) { state, err := term.SaveState(inFd) if err != nil { return err } go func() { if _, ok := <-ch; !ok { return } term.RestoreTerminal(inFd, state) }() defer term.RestoreTerminal(inFd, state) return fn() } return fn() }
// PromptForPasswordString prompts for user input by disabling echo in terminal, useful for password prompt. func PromptForPasswordString(r io.Reader, w io.Writer, format string, a ...interface{}) string { if w == nil { w = os.Stdout } if file, ok := r.(*os.File); ok { inFd := file.Fd() if term.IsTerminal(inFd) { oldState, err := term.SaveState(inFd) if err != nil { glog.V(3).Infof("Unable to save terminal state") return PromptForString(r, w, format, a...) } fmt.Fprintf(w, format, a...) term.DisableEcho(inFd, oldState) input := readInput(r) defer term.RestoreTerminal(inFd, oldState) fmt.Fprintf(w, "\n") return input } glog.V(3).Infof("Stdin is not a terminal") return PromptForString(r, w, format, a...) } return PromptForString(r, w, format, a...) }
// Safe invokes the provided function and will attempt to ensure that when the // function returns (or a termination signal is sent) that the terminal state // is reset to the condition it was in prior to the function being invoked. If // t.Raw is true the terminal will be put into raw mode prior to calling the function. // If the input file descriptor is not a TTY and TryDev is true, the /dev/tty file // will be opened (if available). func (t TTY) Safe(fn SafeFunc) error { in := t.In var hasFd bool var inFd uintptr if desc, ok := in.(fd); ok && in != nil { inFd = desc.Fd() hasFd = true } if t.TryDev && (!hasFd || !term.IsTerminal(inFd)) { if f, err := os.Open("/dev/tty"); err == nil { defer f.Close() inFd = f.Fd() hasFd = true } } if !hasFd || !term.IsTerminal(inFd) { return fn() } var state *term.State var err error if t.Raw { state, err = term.MakeRaw(inFd) } else { state, err = term.SaveState(inFd) } if err != nil { return err } return interrupt.Chain(t.Parent, func() { term.RestoreTerminal(inFd, state) }).Run(fn) }
// Safe invokes the provided function and will attempt to ensure that when the // function returns (or a termination signal is sent) that the terminal state // is reset to the condition it was in prior to the function being invoked. If // t.Raw is true the terminal will be put into raw mode prior to calling the function. // If the input file descriptor is not a TTY and TryDev is true, the /dev/tty file // will be opened (if available). func (t TTY) Safe(fn SafeFunc) error { inFd, isTerminal := term.GetFdInfo(t.In) if !isTerminal && t.TryDev { if f, err := os.Open("/dev/tty"); err == nil { defer f.Close() inFd = f.Fd() isTerminal = term.IsTerminal(inFd) } } if !isTerminal { return fn() } var state *term.State var err error if t.Raw { state, err = term.MakeRaw(inFd) } else { state, err = term.SaveState(inFd) } if err != nil { return err } return interrupt.Chain(t.Parent, func() { if t.sizeQueue != nil { t.sizeQueue.stop() } term.RestoreTerminal(inFd, state) }).Run(fn) }
func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) { authconfig, err := getCredentials(cli.configFile, serverAddress) if err != nil { return authconfig, err } // Some links documenting this: // - https://code.google.com/archive/p/mintty/issues/56 // - https://github.com/docker/docker/issues/15272 // - https://mintty.github.io/ (compatibility) // Linux will hit this if you attempt `cat | docker login`, and Windows // will hit this if you attempt docker login from mintty where stdin // is a pipe, not a character based console. if flPassword == "" && !cli.isTerminalIn { return authconfig, fmt.Errorf("Error: Cannot perform an interactive logon from a non TTY device") } authconfig.Username = strings.TrimSpace(authconfig.Username) if flUser = strings.TrimSpace(flUser); flUser == "" { if isDefaultRegistry { // if this is a defauly registry (docker hub), then display the following message. fmt.Fprintln(cli.out, "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.") } cli.promptWithDefault("Username", authconfig.Username) flUser = readInput(cli.in, cli.out) flUser = strings.TrimSpace(flUser) if flUser == "" { flUser = authconfig.Username } } if flUser == "" { return authconfig, fmt.Errorf("Error: Non-null Username Required") } if flPassword == "" { oldState, err := term.SaveState(cli.inFd) if err != nil { return authconfig, err } fmt.Fprintf(cli.out, "Password: "******"\n") term.RestoreTerminal(cli.inFd, oldState) if flPassword == "" { return authconfig, fmt.Errorf("Error: Password Required") } } authconfig.Username = flUser authconfig.Password = flPassword authconfig.ServerAddress = serverAddress authconfig.IdentityToken = "" return authconfig, nil }
func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress string) (types.AuthConfig, error) { authconfig, ok := cli.configFile.AuthConfigs[serverAddress] if !ok { authconfig = types.AuthConfig{} } if flUser == "" { cli.promptWithDefault("Username", authconfig.Username) flUser = readInput(cli.in, cli.out) flUser = strings.TrimSpace(flUser) if flUser == "" { flUser = authconfig.Username } } if flPassword == "" { oldState, err := term.SaveState(cli.inFd) if err != nil { return authconfig, err } fmt.Fprintf(cli.out, "Password: "******"\n") term.RestoreTerminal(cli.inFd, oldState) if flPassword == "" { return authconfig, fmt.Errorf("Error : Password Required") } } // Assume that a different username means they may not want to use // the email from the config file, so prompt it if flUser != authconfig.Username { if flEmail == "" { cli.promptWithDefault("Email", authconfig.Email) flEmail = readInput(cli.in, cli.out) if flEmail == "" { flEmail = authconfig.Email } } } else { // However, if they don't override the username use the // email from the cmd line if specified. IOW, allow // then to change/override them. And if not specified, just // use what's in the config file if flEmail == "" { flEmail = authconfig.Email } } authconfig.Username = flUser authconfig.Password = flPassword authconfig.Email = flEmail authconfig.ServerAddress = serverAddress cli.configFile.AuthConfigs[serverAddress] = authconfig return authconfig, nil }
func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) { authconfig, err := getCredentials(cli.configFile, serverAddress) if err != nil { return authconfig, err } authconfig.Username = strings.TrimSpace(authconfig.Username) if flUser = strings.TrimSpace(flUser); flUser == "" { if isDefaultRegistry { // if this is a defauly registry (docker hub), then display the following message. fmt.Fprintln(cli.out, "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.") } cli.promptWithDefault("Username", authconfig.Username) flUser = readInput(cli.in, cli.out) flUser = strings.TrimSpace(flUser) if flUser == "" { flUser = authconfig.Username } } if flUser == "" { return authconfig, fmt.Errorf("Error: Non-null Username Required") } if flPassword == "" { oldState, err := term.SaveState(cli.inFd) if err != nil { return authconfig, err } fmt.Fprintf(cli.out, "Password: "******"\n") term.RestoreTerminal(cli.inFd, oldState) if flPassword == "" { return authconfig, fmt.Errorf("Error: Password Required") } } authconfig.Username = flUser authconfig.Password = flPassword authconfig.ServerAddress = serverAddress authconfig.IdentityToken = "" return authconfig, nil }
func (p *process) startInteractive() error { f, err := pty.Start(p.cmd) if err != nil { return err } p.pty = f if p.wire.Input == os.Stdin { // the current terminal shall pass everything to the console, make it ignores ctrl+C etc ... // this is done by making the terminal raw. The state is saved to reset user's terminal settings // when dock exits state, err := term.SetRawTerminal(os.Stdin.Fd()) if err != nil { return err } p.termState = &termState{ state: state, fd: os.Stdin.Fd(), } } else { // wire.Input is a socket (tcp, tls ...). Obvioulsy, we can't set the remote user's terminal in raw mode, however we can at least // disable echo on the console state, err := term.SaveState(p.pty.Fd()) if err != nil { return err } if err := term.DisableEcho(p.pty.Fd(), state); err != nil { return err } p.termState = &termState{ state: state, fd: p.pty.Fd(), } } p.resizePty() go io.Copy(p.wire, f) go io.Copy(f, p.wire) return nil }
func getPassphrase(role string, confirm bool) ([]byte, error) { if pass := os.Getenv(fmt.Sprintf("TUF_%s_PASSPHRASE", strings.ToUpper(role))); pass != "" { return []byte(pass), nil } state, err := term.SaveState(0) if err != nil { return nil, err } term.DisableEcho(0, state) defer term.RestoreTerminal(0, state) stdin := bufio.NewReader(os.Stdin) fmt.Printf("Enter %s keys passphrase: ", role) passphrase, err := stdin.ReadBytes('\n') fmt.Println() if err != nil { return nil, err } passphrase = passphrase[0 : len(passphrase)-1] if !confirm { return passphrase, nil } fmt.Printf("Repeat %s keys passphrase: ", role) confirmation, err := stdin.ReadBytes('\n') fmt.Println() if err != nil { return nil, err } confirmation = confirmation[0 : len(confirmation)-1] if !bytes.Equal(passphrase, confirmation) { return nil, errors.New("The entered passphrases do not match") } return passphrase, nil }
func (ps passwordStore) Basic(u *url.URL) (string, string) { if ps.anonymous { return "", "" } stdin := bufio.NewReader(os.Stdin) fmt.Fprintf(os.Stdout, "Enter username: "******"error processing username input: %s", err) return "", "" } username := strings.TrimSpace(string(userIn)) if term.IsTerminal(0) { state, err := term.SaveState(0) if err != nil { logrus.Errorf("error saving terminal state, cannot retrieve password: %s", err) return "", "" } term.DisableEcho(0, state) defer term.RestoreTerminal(0, state) } fmt.Fprintf(os.Stdout, "Enter password: "******"error processing password input: %s", err) return "", "" } password := strings.TrimSpace(string(userIn)) return username, password }
// ConfigureAuth returns an AuthConfig from the specified user, password and server. func ConfigureAuth(cli *DockerCli, flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) { // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210 if runtime.GOOS == "windows" { cli.in = NewInStream(os.Stdin) } if !isDefaultRegistry { serverAddress = registry.ConvertToHostname(serverAddress) } authconfig, err := cli.CredentialsStore(serverAddress).Get(serverAddress) if err != nil { return authconfig, err } // Some links documenting this: // - https://code.google.com/archive/p/mintty/issues/56 // - https://github.com/docker/docker/issues/15272 // - https://mintty.github.io/ (compatibility) // Linux will hit this if you attempt `cat | docker login`, and Windows // will hit this if you attempt docker login from mintty where stdin // is a pipe, not a character based console. if flPassword == "" && !cli.In().IsTerminal() { return authconfig, fmt.Errorf("Error: Cannot perform an interactive login from a non TTY device") } authconfig.Username = strings.TrimSpace(authconfig.Username) if flUser = strings.TrimSpace(flUser); flUser == "" { if isDefaultRegistry { // if this is a default registry (docker hub), then display the following message. fmt.Fprintln(cli.Out(), "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.") } promptWithDefault(cli.Out(), "Username", authconfig.Username) flUser = readInput(cli.In(), cli.Out()) flUser = strings.TrimSpace(flUser) if flUser == "" { flUser = authconfig.Username } } if flUser == "" { return authconfig, fmt.Errorf("Error: Non-null Username Required") } if flPassword == "" { oldState, err := term.SaveState(cli.In().FD()) if err != nil { return authconfig, err } fmt.Fprintf(cli.Out(), "Password: "******"\n") term.RestoreTerminal(cli.In().FD(), oldState) if flPassword == "" { return authconfig, fmt.Errorf("Error: Password Required") } } authconfig.Username = flUser authconfig.Password = flPassword authconfig.ServerAddress = serverAddress authconfig.IdentityToken = "" return authconfig, nil }
func (br *boundRetriever) requestPassphrase(keyName, alias string, createNew bool, numAttempts int) (string, bool, error) { // Figure out if we should display a different string for this alias displayAlias := alias if val, ok := br.aliasMap[alias]; ok { displayAlias = val } // If typing on the terminal, we do not want the terminal to echo the // password that is typed (so it doesn't display) if term.IsTerminal(os.Stdin.Fd()) { state, err := term.SaveState(os.Stdin.Fd()) if err != nil { return "", false, err } term.DisableEcho(os.Stdin.Fd(), state) defer term.RestoreTerminal(os.Stdin.Fd(), state) } indexOfLastSeparator := strings.LastIndex(keyName, string(filepath.Separator)) if indexOfLastSeparator == -1 { indexOfLastSeparator = 0 } var shortName string if len(keyName) > indexOfLastSeparator+idBytesToDisplay { if indexOfLastSeparator > 0 { keyNamePrefix := keyName[:indexOfLastSeparator] keyNameID := keyName[indexOfLastSeparator+1 : indexOfLastSeparator+idBytesToDisplay+1] shortName = keyNameID + " (" + keyNamePrefix + ")" } else { shortName = keyName[indexOfLastSeparator : indexOfLastSeparator+idBytesToDisplay] } } withID := fmt.Sprintf(" with ID %s", shortName) if shortName == "" { withID = "" } switch { case createNew: fmt.Fprintf(br.out, "Enter passphrase for new %s key%s: ", displayAlias, withID) case displayAlias == "yubikey": fmt.Fprintf(br.out, "Enter the %s for the attached Yubikey: ", keyName) default: fmt.Fprintf(br.out, "Enter passphrase for %s key%s: ", displayAlias, withID) } stdin := bufio.NewReader(br.in) passphrase, err := stdin.ReadBytes('\n') fmt.Fprintln(br.out) if err != nil { return "", false, err } retPass := strings.TrimSpace(string(passphrase)) if createNew { err = br.verifyAndConfirmPassword(stdin, retPass, displayAlias, withID) if err != nil { return "", false, err } } br.cachePassword(alias, retPass) return retPass, false, nil }
func (*DockerTerm) SaveState(fd uintptr) (*term.State, error) { return term.SaveState(fd) }
// CmdLogin logs in or registers a user to a Docker registry service. // // If no server is specified, the user will be logged into or registered to the registry's index server. // // Usage: docker login SERVER func (cli *DockerCli) CmdLogin(args ...string) error { cmd := cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true) cmd.Require(flag.Max, 1) var username, password, email string cmd.StringVar(&username, []string{"u", "-username"}, "", "Username") cmd.StringVar(&password, []string{"p", "-password"}, "", "Password") cmd.StringVar(&email, []string{"e", "-email"}, "", "Email") cmd.ParseFlags(args, true) serverAddress := registry.INDEXSERVER if len(cmd.Args()) > 0 { serverAddress = cmd.Arg(0) } promptDefault := func(prompt string, configDefault string) { if configDefault == "" { fmt.Fprintf(cli.out, "%s: ", prompt) } else { fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) } } readInput := func(in io.Reader, out io.Writer) string { reader := bufio.NewReader(in) line, _, err := reader.ReadLine() if err != nil { fmt.Fprintln(out, err.Error()) os.Exit(1) } return string(line) } authconfig, ok := cli.configFile.AuthConfigs[serverAddress] if !ok { authconfig = cliconfig.AuthConfig{} } if username == "" { promptDefault("Username", authconfig.Username) username = readInput(cli.in, cli.out) username = strings.Trim(username, " ") if username == "" { username = authconfig.Username } } // Assume that a different username means they may not want to use // the password or email from the config file, so prompt them if username != authconfig.Username { if password == "" { oldState, err := term.SaveState(cli.inFd) if err != nil { return err } fmt.Fprintf(cli.out, "Password: "******"\n") term.RestoreTerminal(cli.inFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } } if email == "" { promptDefault("Email", authconfig.Email) email = readInput(cli.in, cli.out) if email == "" { email = authconfig.Email } } } else { // However, if they don't override the username use the // password or email from the cmd line if specified. IOW, allow // then to change/override them. And if not specified, just // use what's in the config file if password == "" { password = authconfig.Password } if email == "" { email = authconfig.Email } } authconfig.Username = username authconfig.Password = password authconfig.Email = email authconfig.ServerAddress = serverAddress cli.configFile.AuthConfigs[serverAddress] = authconfig serverResp, err := cli.call("POST", "/auth", cli.configFile.AuthConfigs[serverAddress], nil) if serverResp.statusCode == 401 { delete(cli.configFile.AuthConfigs, serverAddress) if err2 := cli.configFile.Save(); err2 != nil { fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2) } return err } if err != nil { return err } defer serverResp.body.Close() var response types.AuthResponse if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil { // Upon error, remove entry delete(cli.configFile.AuthConfigs, serverAddress) return err } if err := cli.configFile.Save(); err != nil { return fmt.Errorf("Error saving config file: %v", err) } fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s\n", cli.configFile.Filename()) if response.Status != "" { fmt.Fprintf(cli.out, "%s\n", response.Status) } return nil }
// PromptRetrieverWithInOut returns a new Retriever which will provide a // prompt using the given in and out readers. The passphrase will be cached // such that subsequent prompts will produce the same passphrase. // aliasMap can be used to specify display names for TUF key aliases. If aliasMap // is nil, a sensible default will be used. func PromptRetrieverWithInOut(in io.Reader, out io.Writer, aliasMap map[string]string) Retriever { userEnteredTargetsSnapshotsPass := false targetsSnapshotsPass := "" userEnteredRootsPass := false rootsPass := "" return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) { if alias == tufRootAlias && createNew && numAttempts == 0 { fmt.Fprintln(out, tufRootKeyGenerationWarning) } if numAttempts > 0 { if !createNew { fmt.Fprintln(out, "Passphrase incorrect. Please retry.") } } // Figure out if we should display a different string for this alias displayAlias := alias if aliasMap != nil { if val, ok := aliasMap[alias]; ok { displayAlias = val } } // First, check if we have a password cached for this alias. if numAttempts == 0 { if userEnteredTargetsSnapshotsPass && (alias == tufSnapshotAlias || alias == tufTargetsAlias) { return targetsSnapshotsPass, false, nil } if userEnteredRootsPass && (alias == "root") { return rootsPass, false, nil } } if numAttempts > 3 && !createNew { return "", true, ErrTooManyAttempts } state, err := term.SaveState(0) if err != nil { return "", false, err } term.DisableEcho(0, state) defer term.RestoreTerminal(0, state) stdin := bufio.NewReader(in) indexOfLastSeparator := strings.LastIndex(keyName, string(filepath.Separator)) if indexOfLastSeparator == -1 { indexOfLastSeparator = 0 } var shortName string if len(keyName) > indexOfLastSeparator+idBytesToDisplay { if indexOfLastSeparator > 0 { keyNamePrefix := keyName[:indexOfLastSeparator] keyNameID := keyName[indexOfLastSeparator+1 : indexOfLastSeparator+idBytesToDisplay+1] shortName = keyNameID + " (" + keyNamePrefix + ")" } else { shortName = keyName[indexOfLastSeparator : indexOfLastSeparator+idBytesToDisplay] } } withID := fmt.Sprintf(" with ID %s", shortName) if shortName == "" { withID = "" } if createNew { fmt.Fprintf(out, "Enter passphrase for new %s key%s: ", displayAlias, withID) } else if displayAlias == "yubikey" { fmt.Fprintf(out, "Enter the %s for the attached Yubikey: ", keyName) } else { fmt.Fprintf(out, "Enter passphrase for %s key%s: ", displayAlias, withID) } passphrase, err := stdin.ReadBytes('\n') fmt.Fprintln(out) if err != nil { return "", false, err } retPass := strings.TrimSpace(string(passphrase)) if !createNew { if alias == tufSnapshotAlias || alias == tufTargetsAlias { userEnteredTargetsSnapshotsPass = true targetsSnapshotsPass = retPass } if alias == tufRootAlias { userEnteredRootsPass = true rootsPass = retPass } return retPass, false, nil } if len(retPass) < 8 { fmt.Fprintln(out, "Passphrase is too short. Please use a password manager to generate and store a good random passphrase.") return "", false, ErrTooShort } fmt.Fprintf(out, "Repeat passphrase for new %s key%s: ", displayAlias, withID) confirmation, err := stdin.ReadBytes('\n') fmt.Fprintln(out) if err != nil { return "", false, err } confirmationStr := strings.TrimSpace(string(confirmation)) if retPass != confirmationStr { fmt.Fprintln(out, "Passphrases do not match. Please retry.") return "", false, ErrDontMatch } if alias == tufSnapshotAlias || alias == tufTargetsAlias { userEnteredTargetsSnapshotsPass = true targetsSnapshotsPass = retPass } if alias == tufRootAlias { userEnteredRootsPass = true rootsPass = retPass } return retPass, false, nil } }
// Decrypt a io.Reader with the given secretKeyring. // You can optionally pass a defaultGPGKey to use for the // decryption, otherwise it will use the first entity. func Decrypt(f io.Reader, secretKeyring, defaultGPGKey string) (io.Reader, error) { // Open the private key file keyringFileBuffer, err := os.Open(secretKeyring) if err != nil { return nil, err } defer keyringFileBuffer.Close() entityList, err := openpgp.ReadKeyRing(keyringFileBuffer) if err != nil { return nil, err } var entity *openpgp.Entity if defaultGPGKey != "" { // loop through their keys until we find the one they want var foundKey bool for _, e := range entityList { // we can match on the fingerprint or the keyid because // why not? I bet no one knows the difference if e.PrimaryKey.KeyIdString() == defaultGPGKey || e.PrimaryKey.KeyIdShortString() == defaultGPGKey || fmt.Sprintf("%X", e.PrimaryKey.Fingerprint) == defaultGPGKey { foundKey = true entity = e break } } if !foundKey { // we didn't find the key they specified return nil, fmt.Errorf("Could not find private GPG Key with id: %s", defaultGPGKey) } } else { // they didn't set a default key // so let's hope it is the first one :/ // TODO(jessfraz): maybe prompt here if they have // more than one private key entity = entityList[0] } var identityString string for _, identity := range entity.Identities { identityString = fmt.Sprintf(" %s [%s]", identity.Name, entity.PrimaryKey.KeyIdString()) break } // Get the passphrase and read the private key. // Have not touched the encrypted string yet stdin, _, stderr := term.StdStreams() stdinFd, _ := term.GetFdInfo(stdin) oldState, err := term.SaveState(stdinFd) if err != nil { return nil, err } // prompt for passphrase fmt.Fprintf(stderr, "GPG Passphrase for key%s: ", identityString) term.DisableEcho(stdinFd, oldState) // read what they inputed passphrase := readInput(stdin, stderr) fmt.Fprint(stderr, "\n\n") // restore the terminal term.RestoreTerminal(stdinFd, oldState) logrus.Debugln("Decrypting private key using passphrase") entity.PrivateKey.Decrypt(passphrase) for _, subkey := range entity.Subkeys { subkey.PrivateKey.Decrypt(passphrase) } logrus.Debugln("Finished decrypting private key using passphrase") // base64 decode dec := base64.NewDecoder(base64.StdEncoding, f) // Decrypt it with the contents of the private key md, err := openpgp.ReadMessage(dec, entityList, nil, nil) if err != nil { return nil, fmt.Errorf("GPG ReadMessage failed: %v", err) } // return the body contents return md.UnverifiedBody, nil }
// PromptRetrieverWithInOut returns a new Retriever which will provide a // prompt using the given in and out readers. The passphrase will be cached // such that subsequent prompts will produce the same passphrase. func PromptRetrieverWithInOut(in io.Reader, out io.Writer) Retriever { userEnteredTargetsSnapshotsPass := false targetsSnapshotsPass := "" userEnteredRootsPass := false rootsPass := "" return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) { if alias == tufRootAlias && createNew && numAttempts == 0 { fmt.Fprintln(out, tufRootKeyGenerationWarning) } if numAttempts > 0 { if createNew { fmt.Fprintln(out, "Passphrases do not match. Please retry.") } else { fmt.Fprintln(out, "Passphrase incorrect. Please retry.") } } // First, check if we have a password cached for this alias. if numAttempts == 0 { if userEnteredTargetsSnapshotsPass && (alias == tufSnapshotAlias || alias == tufTargetsAlias) { return targetsSnapshotsPass, false, nil } if userEnteredRootsPass && (alias == "root") { return rootsPass, false, nil } } if numAttempts > 3 && !createNew { return "", true, errors.New("Too many attempts") } state, err := term.SaveState(0) if err != nil { return "", false, err } term.DisableEcho(0, state) defer term.RestoreTerminal(0, state) stdin := bufio.NewReader(in) indexOfLastSeparator := strings.LastIndex(keyName, string(filepath.Separator)) if len(keyName) > indexOfLastSeparator+idBytesToDisplay+1 { keyName = keyName[:indexOfLastSeparator+idBytesToDisplay+1] } if createNew { fmt.Fprintf(out, "Enter passphrase for new %s key with id %s: ", alias, keyName) } else { fmt.Fprintf(out, "Enter key passphrase for %s key with id %s: ", alias, keyName) } passphrase, err := stdin.ReadBytes('\n') fmt.Fprintln(out) if err != nil { return "", false, err } retPass := strings.TrimSpace(string(passphrase)) if !createNew { if alias == tufSnapshotAlias || alias == tufTargetsAlias { userEnteredTargetsSnapshotsPass = true targetsSnapshotsPass = retPass } if alias == tufRootAlias { userEnteredRootsPass = true rootsPass = retPass } return retPass, false, nil } if len(retPass) < 8 { fmt.Fprintln(out, "Please use a password manager to generate and store a good random passphrase.") return "", false, errors.New("Passphrase too short") } fmt.Fprintf(out, "Repeat passphrase for new %s key with id %s: ", alias, keyName) confirmation, err := stdin.ReadBytes('\n') fmt.Fprintln(out) if err != nil { return "", false, err } confirmationStr := strings.TrimSpace(string(confirmation)) if retPass != confirmationStr { return "", false, errors.New("The entered passphrases do not match") } if alias == tufSnapshotAlias || alias == tufTargetsAlias { userEnteredTargetsSnapshotsPass = true targetsSnapshotsPass = retPass } if alias == tufRootAlias { userEnteredRootsPass = true rootsPass = retPass } return retPass, false, nil } }
// CmdLogin logs in or registers a user to a Docker registry service. // // If no server is specified, the user will be logged into or registered to the registry's index server. // // Usage: docker login SERVER func (cli *DockerCli) CmdLogin(args ...string) error { cmd := Cli.Subcmd("login", []string{"[SERVER]"}, Cli.DockerCommands["login"].Description+".\nIf no server is specified \""+registry.IndexServer+"\" is the default.", true) cmd.Require(flag.Max, 1) var username, password, email string cmd.StringVar(&username, []string{"u", "-username"}, "", "Username") cmd.StringVar(&password, []string{"p", "-password"}, "", "Password") cmd.StringVar(&email, []string{"e", "-email"}, "", "Email") cmd.ParseFlags(args, true) // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210 if runtime.GOOS == "windows" { cli.in = os.Stdin } serverAddress := registry.IndexServer if len(cmd.Args()) > 0 { serverAddress = cmd.Arg(0) } promptDefault := func(prompt string, configDefault string) { if configDefault == "" { fmt.Fprintf(cli.out, "%s: ", prompt) } else { fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) } } readInput := func(in io.Reader, out io.Writer) string { reader := bufio.NewReader(in) line, _, err := reader.ReadLine() if err != nil { fmt.Fprintln(out, err.Error()) os.Exit(1) } return string(line) } authconfig, ok := cli.configFile.AuthConfigs[serverAddress] if !ok { authconfig = cliconfig.AuthConfig{} } if username == "" { promptDefault("Username", authconfig.Username) username = readInput(cli.in, cli.out) username = strings.TrimSpace(username) if username == "" { username = authconfig.Username } } // Assume that a different username means they may not want to use // the password or email from the config file, so prompt them if username != authconfig.Username { if password == "" { oldState, err := term.SaveState(cli.inFd) if err != nil { return err } fmt.Fprintf(cli.out, "Password: "******"\n") term.RestoreTerminal(cli.inFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } } if email == "" { promptDefault("Email", authconfig.Email) email = readInput(cli.in, cli.out) if email == "" { email = authconfig.Email } } } else { // However, if they don't override the username use the // password or email from the cmd line if specified. IOW, allow // then to change/override them. And if not specified, just // use what's in the config file if password == "" { password = authconfig.Password } if email == "" { email = authconfig.Email } } authconfig.Username = username authconfig.Password = password authconfig.Email = email authconfig.ServerAddress = serverAddress cli.configFile.AuthConfigs[serverAddress] = authconfig auth := cli.configFile.AuthConfigs[serverAddress] response, err := cli.client.RegistryLogin(auth) if err != nil { if lib.IsErrUnauthorized(err) { delete(cli.configFile.AuthConfigs, serverAddress) if err2 := cli.configFile.Save(); err2 != nil { fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2) } } return err } if err := cli.configFile.Save(); err != nil { return fmt.Errorf("Error saving config file: %v", err) } fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s\n", cli.configFile.Filename()) if response.Status != "" { fmt.Fprintf(cli.out, "%s\n", response.Status) } return nil }