mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			135 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
package terminal
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/containers/storage/pkg/homedir"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
	"golang.org/x/crypto/ssh"
 | 
						|
	"golang.org/x/crypto/ssh/knownhosts"
 | 
						|
	"golang.org/x/crypto/ssh/terminal"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	passPhrase   []byte
 | 
						|
	phraseSync   sync.Once
 | 
						|
	password     []byte
 | 
						|
	passwordSync sync.Once
 | 
						|
)
 | 
						|
 | 
						|
// ReadPassword prompts for a secret and returns value input by user from stdin
 | 
						|
// Unlike terminal.ReadPassword(), $(echo $SECRET | podman...) is supported.
 | 
						|
// Additionally, all input after `<secret>/n` is queued to podman command.
 | 
						|
func ReadPassword(prompt string) (pw []byte, err error) {
 | 
						|
	fd := int(os.Stdin.Fd())
 | 
						|
	if terminal.IsTerminal(fd) {
 | 
						|
		fmt.Fprint(os.Stderr, prompt)
 | 
						|
		pw, err = terminal.ReadPassword(fd)
 | 
						|
		fmt.Fprintln(os.Stderr)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	var b [1]byte
 | 
						|
	for {
 | 
						|
		n, err := os.Stdin.Read(b[:])
 | 
						|
		// terminal.ReadPassword discards any '\r', so we do the same
 | 
						|
		if n > 0 && b[0] != '\r' {
 | 
						|
			if b[0] == '\n' {
 | 
						|
				return pw, nil
 | 
						|
			}
 | 
						|
			pw = append(pw, b[0])
 | 
						|
			// limit size, so that a wrong input won't fill up the memory
 | 
						|
			if len(pw) > 1024 {
 | 
						|
				err = errors.New("password too long, 1024 byte limit")
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			// terminal.ReadPassword accepts EOF-terminated passwords
 | 
						|
			// if non-empty, so we do the same
 | 
						|
			if err == io.EOF && len(pw) > 0 {
 | 
						|
				err = nil
 | 
						|
			}
 | 
						|
			return pw, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func PublicKey(path string, passphrase []byte) (ssh.Signer, error) {
 | 
						|
	key, err := ioutil.ReadFile(path)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	signer, err := ssh.ParsePrivateKey(key)
 | 
						|
	if err != nil {
 | 
						|
		if _, ok := err.(*ssh.PassphraseMissingError); !ok {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if len(passphrase) == 0 {
 | 
						|
			passphrase = ReadPassphrase()
 | 
						|
		}
 | 
						|
		return ssh.ParsePrivateKeyWithPassphrase(key, passphrase)
 | 
						|
	}
 | 
						|
	return signer, nil
 | 
						|
}
 | 
						|
 | 
						|
func ReadPassphrase() []byte {
 | 
						|
	phraseSync.Do(func() {
 | 
						|
		secret, err := ReadPassword("Key Passphrase: ")
 | 
						|
		if err != nil {
 | 
						|
			secret = []byte{}
 | 
						|
		}
 | 
						|
		passPhrase = secret
 | 
						|
	})
 | 
						|
	return passPhrase
 | 
						|
}
 | 
						|
 | 
						|
func ReadLogin() []byte {
 | 
						|
	passwordSync.Do(func() {
 | 
						|
		secret, err := ReadPassword("Login password: ")
 | 
						|
		if err != nil {
 | 
						|
			secret = []byte{}
 | 
						|
		}
 | 
						|
		password = secret
 | 
						|
	})
 | 
						|
	return password
 | 
						|
}
 | 
						|
 | 
						|
func HostKey(host string) ssh.PublicKey {
 | 
						|
	// parse OpenSSH known_hosts file
 | 
						|
	// ssh or use ssh-keyscan to get initial key
 | 
						|
	knownHosts := filepath.Join(homedir.Get(), ".ssh", "known_hosts")
 | 
						|
	fd, err := os.Open(knownHosts)
 | 
						|
	if err != nil {
 | 
						|
		logrus.Error(err)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// support -H parameter for ssh-keyscan
 | 
						|
	hashhost := knownhosts.HashHostname(host)
 | 
						|
 | 
						|
	scanner := bufio.NewScanner(fd)
 | 
						|
	for scanner.Scan() {
 | 
						|
		_, hosts, key, _, _, err := ssh.ParseKnownHosts(scanner.Bytes())
 | 
						|
		if err != nil {
 | 
						|
			logrus.Errorf("Failed to parse known_hosts: %s", scanner.Text())
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		for _, h := range hosts {
 | 
						|
			if h == host || h == hashhost {
 | 
						|
				return key
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |