Example #1
0
// 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()
}
Example #2
0
// 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...)
}
Example #3
0
// 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)
}
Example #4
0
// 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)
}
Example #5
0
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
}
Example #6
0
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
}
Example #7
0
File: login.go Project: jak-atx/vic
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
}
Example #8
0
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
}
Example #9
0
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
}
Example #10
0
File: tuf.go Project: cyli/notary
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
}
Example #11
0
// 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
}
Example #12
0
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
}
Example #13
0
func (*DockerTerm) SaveState(fd uintptr) (*term.State, error) {
	return term.SaveState(fd)
}
Example #14
0
// 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
}
Example #15
0
// 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
	}
}
Example #16
0
// 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
}
Example #17
0
// 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
	}
}
Example #18
0
// 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
}