getpasswd functionality in Go? - passwords

Situation:
I want to get a password entry from the stdin console - without echoing what the user types. Is there something comparable to getpasswd functionality in Go?
What I tried:
I tried using syscall.Read, but it echoes what is typed.

The following is one of best ways to get it done.
First get term package by go get golang.org/x/term
package main
import (
"bufio"
"fmt"
"os"
"strings"
"syscall"
"golang.org/x/term"
)
func main() {
username, password, _ := credentials()
fmt.Printf("Username: %s, Password: %s\n", username, password)
}
func credentials() (string, string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Username: ")
username, err := reader.ReadString('\n')
if err != nil {
return "", "", err
}
fmt.Print("Enter Password: ")
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", "", err
}
password := string(bytePassword)
return strings.TrimSpace(username), strings.TrimSpace(password), nil
}
http://play.golang.org/p/l-9IP1mrhA

Just saw a mail in #go-nuts maillist. There is someone who wrote quite a simple go package to be used. You can find it here: https://github.com/howeyc/gopass
It something like that:
package main
import "fmt"
import "github.com/howeyc/gopass"
func main() {
fmt.Printf("Password: ")
pass := gopass.GetPasswd()
// Do something with pass
}

Since Go ~v1.11 there is an official package golang.org/x/term which replaces the deprecated crypto/ssh/terminal. It has, among other things, the function term.ReadPassword.
Example usage:
package main
import (
"fmt"
"os"
"syscall"
"golang.org/x/term"
)
func main() {
fmt.Print("Password: ")
bytepw, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
os.Exit(1)
}
pass := string(bytepw)
fmt.Printf("\nYou've entered: %q\n", pass)
}

I had a similar usecase and the following code snippet works well for me. Feel free to try this if you are still stuck here.
import (
"fmt"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
fmt.Printf("Now, please type in the password (mandatory): ")
password, _ := terminal.ReadPassword(0)
fmt.Printf("Password is : %s", password)
}
Of course, you need to install terminal package using go get beforehand.

you can do this by execing stty -echo to turn off echo and then stty echo after reading in the password to turn it back on

Here is a solution that I developed using Go1.6.2 that you might find useful.
It only uses the following standard packages: bufio, fmt, os, strings and syscall. More specifically, it uses syscall.ForkExec() and syscall.Wait4() to invoke stty to disable/enable terminal echo.
I have tested it on Linux and BSD (Mac). It will not work on windows.
// getPassword - Prompt for password. Use stty to disable echoing.
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
fmt.Print(prompt)
// Common settings and variables for both stty calls.
attrs := syscall.ProcAttr{
Dir: "",
Env: []string{},
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil}
var ws syscall.WaitStatus
// Disable echoing.
pid, err := syscall.ForkExec(
"/bin/stty",
[]string{"stty", "-echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
// Echo is disabled, now grab the data.
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
// Re-enable echo.
pid, err = syscall.ForkExec(
"/bin/stty",
[]string{"stty", "echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
return strings.TrimSpace(text)
}

Required launching stty via Go ForkExec() function:
package main
import (
os "os"
bufio "bufio"
fmt "fmt"
str "strings"
)
func main() {
fmt.Println();
if passwd, err := Getpasswd("Enter password: "); err == nil {
fmt.Printf("\n\nPassword: '%s'\n",passwd)
}
}
func Getpasswd(prompt string) (passwd string, err os.Error) {
fmt.Print(prompt);
const stty_arg0 = "/bin/stty";
stty_argv_e_off := []string{"stty","-echo"};
stty_argv_e_on := []string{"stty","echo"};
const exec_cwdir = "";
fd := []*os.File{os.Stdin,os.Stdout,os.Stderr};
pid, err := os.ForkExec(stty_arg0,stty_argv_e_off,nil,exec_cwdir,fd);
if err != nil {
return passwd, os.NewError(fmt.Sprintf("Failed turning off console echo for password entry:\n\t%s",err))
}
rd := bufio.NewReader(os.Stdin);
os.Wait(pid,0);
line, err := rd.ReadString('\n');
if err == nil {
passwd = str.TrimSpace(line)
} else {
err = os.NewError(fmt.Sprintf("Failed during password entry: %s",err))
}
pid, e := os.ForkExec(stty_arg0,stty_argv_e_on,nil,exec_cwdir,fd);
if e == nil {
os.Wait(pid,0)
} else if err == nil {
err = os.NewError(fmt.Sprintf("Failed turning on console echo post password entry:\n\t%s",e))
}
return passwd, err
}

Here is a version specific to Linux:
func terminalEcho(show bool) {
// Enable or disable echoing terminal input. This is useful specifically for
// when users enter passwords.
// calling terminalEcho(true) turns on echoing (normal mode)
// calling terminalEcho(false) hides terminal input.
var termios = &syscall.Termios{}
var fd = os.Stdout.Fd()
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
if show {
termios.Lflag |= syscall.ECHO
} else {
termios.Lflag &^= syscall.ECHO
}
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
uintptr(syscall.TCSETS),
uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
}
So to use it:
fmt.Print("password: ")
terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")
It's the TCGETS syscall that is linux specific. There are different syscall values for OSX and Windows.

You could also use PasswordPrompt function of https://github.com/peterh/liner package.

Turning off echo before typing and turning on to turn it back on after typing.
Without third library, you can find ways to do with it on unix shown above. But it's difficult on Windows.
You can achieve it by method SetConsoleMode with windows kernel32.dll referring to the accepted answer from C: How to disable echo in windows console?
func GetPassword(prompt string) (err error, text string) {
var modeOn, modeOff uint32
stdin := syscall.Handle(os.Stdin.Fd())
err = syscall.GetConsoleMode(stdin, &modeOn)
if err != nil {
return
}
modeOff = modeOn &^ 0x0004
proc := syscall.MustLoadDLL("kernel32").MustFindProc("SetConsoleMode")
fmt.Print(prompt)
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOff))
_, err = fmt.Scanln(&text)
if err != nil {
return
}
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOn))
fmt.Println()
return nil, strings.TrimSpace(text)
}

You can get the behavior you want with the Read method from the os.File object (or the os.Stdin variable). The following sample program will read a line of text (terminated with by pressing the return key) but won't echo it until the fmt.Printf call.
package main
import "fmt"
import "os"
func main() {
var input []byte = make( []byte, 100 );
os.Stdin.Read( input );
fmt.Printf( "%s", input );
}
If you want more advanced behavior, you're probably going to have to use the Go C-wrapper utilities and create some wrappers for low-level api calls.

Related

Golang - Read Os.stdin input but don't echo it

In a golang program I'm reading the Os.Stdin input from a bufio.Reader.
After enter is pressed, the program reads the input and it is then printed onto the console. Is it possible to not print the input onto the console? After reading it, I process the input and reprint it (and no longer need the original input).
I read the data like this:
inputReader := bufio.NewReader(os.Stdin)
for {
outgoing, _ := inputReader.ReadString('\n')
outs <- outgoing
}
I cannot think to other methods than to use ANSI escape codes to clear the terminal and move the cursor to a specific location (in your case to column 1:row 1).
var screen *bytes.Buffer = new(bytes.Buffer)
var output *bufio.Writer = bufio.NewWriter(os.Stdout)
And here are some basic helper methods to ease your job working with terminal.
// Move cursor to given position
func moveCursor(x int, y int) {
fmt.Fprintf(screen, "\033[%d;%dH", x, y)
}
// Clear the terminal
func clearTerminal() {
output.WriteString("\033[2J")
}
Then inside your function you need to clear the terminal and move the cursor to the first column and first row of the terminal window. At the end you have to output the computed result.
for {
outgoing, err := input.ReadString('\n')
if err != nil {
break
}
if _, err := fmt.Sscanf(outgoing, "%f", input); err != nil {
fmt.Println("Input error!")
continue
}
// Clear console
clearTerminal()
moveCursor(1,1)
fmt.Println(outs) // prints the computed result
}
It seems you are looking for a terminal-specific function to disable echo. This is usually used when writing passwords on the terminal (you can type but you don't see the characters).
I suggest you give a try to terminal.ReadPassword it should work nicely and probably in the most cross-platform compatible way.
prompt := ""
t := terminal.NewTerminal(os.Stdin, prompt)
for {
outgoing, err := t.ReadPassword(prompt)
if err != nil {
log.Fatalln(err)
}
outs <- outgoing
}
other than crypto/ssh/terminal;
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
)
func raw(start bool) error {
r := "raw"
if !start {
r = "-raw"
}
rawMode := exec.Command("stty", r)
rawMode.Stdin = os.Stdin
err := rawMode.Run()
if err != nil {
return err
}
return rawMode.Wait()
}
// http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html
func main() {
var rs []rune
raw(true)
for {
inp := bufio.NewReader(os.Stdin)
r, _, err := inp.ReadRune()
if err != nil {
raw(false)
panic(err)
}
if r == '\x03' { // ctrl+c
break
} else if r == '\r' { // enter
fmt.Print(string(rs), "\n\r")
rs = []rune{}
continue
} else if r == '\u007f' { // backspace
fmt.Printf("\033[1D\033[K")
continue
}
rs = append(rs, r)
}
raw(false)
}

Golang SSH-Server: How to handle file transfer with scp?

I have written a small SSH-Server in golang with the crypto/ssh package.
It supports returning an interactive shell and immediate command execution.
Here is a minimal example of the server:
package main
import (
"fmt"
"io/ioutil"
"log"
"net"
"os/exec"
"golang.org/x/crypto/ssh"
)
func main() {
c := &ssh.ServerConfig{
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
if c.User() == "foo" && string(pass) == "bar" {
return nil, nil
}
return nil, fmt.Errorf("password rejected for %q", c.User())
},
}
keyBytes, _ := ioutil.ReadFile("key")
key, _ := ssh.ParsePrivateKey(keyBytes)
c.AddHostKey(key)
listener, _ := net.Listen("tcp", "0.0.0.0:2200")
for {
tcpConn, _ := listener.Accept()
_, chans, reqs, _ := ssh.NewServerConn(tcpConn, c)
go ssh.DiscardRequests(reqs)
go handleChannels(chans)
}
}
func handleChannels(chans <-chan ssh.NewChannel) {
for newChannel := range chans {
go handleChannel(newChannel)
}
}
func handleChannel(newChannel ssh.NewChannel) {
channel, requests, _ := newChannel.Accept()
for req := range requests {
switch req.Type {
case "shell":
go handleShell(channel)
case "exec":
go handleExec(channel, req)
}
}
}
func handleShell(c ssh.Channel) {}
func handleExec(c ssh.Channel, r *ssh.Request) {
cmdString, args, _ := parseCommand(r.Payload)
log.Printf("exec: %s\n", cmdString)
for i := range args {
log.Printf("arg %d: %s\n", i, args[i])
}
cmd := exec.Command(cmdString, args...)
cmd.Run()
}
func parseCommand(b []byte) (string, []string, error) {
cmdString := strings.TrimSpace(string(b))
cmdArray := strings.Split(cmdString, " ")
cmd := strings.Trim(cmdArray[0], " ")
args := cmdArray[1:]
return cmd, args, nil
}
If I run the server and execute scp as follows:
scp -P 2200 test.file foo#localhost:~/
the handleExec function is called.
The output of the cmdString shows:
2015/11/22 17:49:14 exec: scp
2015/11/22 17:49:14 arg 0: -t
2015/11/22 17:49:14 arg 1: ~/
But how can I implement the handleExec function to actually save the file/dir I passed via scp?
I just ran into the problem of executing scp and custom commands over my ssh server and as it is undocumented how to do this I pieced together some code from the tests in crypto.ssh (https://github.com/golang/crypto/blob/master/ssh/session.go and https://github.com/golang/crypto/blob/master/ssh/session_test.go) It works with OpenSSH and the crypto.ssh client. You can for instance call session.Run() on your client and handle e.g. scp or custom commands with it.
type exitStatusMsg struct {
Status uint32
}
// RFC 4254 Section 6.5.
type execMsg struct {
Command string
}
go func(in <-chan *ssh.Request, channel ssh.Channel) {
for req := range in {
if req.Type == "exec" {
var msg execMsg
if err := ssh.Unmarshal(req.Payload, &msg); err != nil {
log.Printf("error parsing ssh execMsg: %s\n", err)
req.Reply(false, nil)
return
}
go func(msg execMsg, ch ssh.Channel) {
// ch can be used as a ReadWriteCloser if there should be interactivity
runYourCommand(msg.Command, ch)
ex := exitStatusMsg{
Status: 0,
}
// return the status code
if _, err := ch.SendRequest("exit-status", false, ssh.Marshal(&ex)); err != nil {
log.Printf("unable to send status: %v", err)
}
ch.Close()
}(msg, channel)
req.Reply(true, nil) // tell the other end that we can run the request
} else {
req.Reply(req.Type == "shell", nil)
}
}
}(requests, channel)
You need to replace runYourCommand with whatever function then executes your command and set the exit code to whatever your command/process returns.

How do you add spaces to exec.command in golang

How do I use exec.command to start a file with spaces? Adding quotes doesn't seem to work, neither does " or %20 instead of the spaces.
package main
import (
"fmt"
"os/exec"
)
func main() {
StartProcess("C:\\File With Spaces.txt")
}
func StartProcess(Filename string) {
Filename = "\"" + Filename + "\""
cmd := exec.Command("cmd","/C start",Filename)
err := cmd.Start()
if err!=nil{
fmt.Println(err)
}
}
If you're trying to run an executable on Windows then you don't need command prompt. You just need to pass in the executable path to the shell and it will fire.
E.g:
func StartProcess(Filename string) {
// Filename = "cmd /C start \"" + Filename + "\""
cmd := exec.Command(Filename)
err := cmd.Start()
if err!=nil{
fmt.Println(err)
}
}
StartProcess("C:\\path to executable\\executable.exe")
That said, generally with all frameworks on Windows that start executables (Go appears to be included in this) you get in trouble when trying to concatenate your own arguments. That is why the args argument for exec.Command is variadic. So your original should have been this:
cmd := exec.Command("cmd", "/C", "start", Filename)
// ^^^^^^^^^^^^^^^ variadic arguments make Windows happy
This works, but only in windows
cmd := exec.Command("rundll32.exe", "url.dll,FileProtocolHandler", Filename)
you need to cmd.Wait and check for any errors returned by your methods
package main
import (
"bytes"
"log"
"os"
"os/exec"
)
func main() {
args := os.Args
cmd := exec.Command(args[1], args[2:]...)
var b bytes.Buffer
cmd.Stdout = &b
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
if _, err := os.Stdout.Write(b.Bytes()); err != nil {
log.Fatal(err)
}
}

Golang write input and get output from terminal process

I have a question regarding how to send input and receive output from a terminal subprocess such as ssh. An example in python would be something like this:
how to give subprocess a password and get stdout at the same time
I cannot find a simple example in Golang that is similar how the above work.
In Golang I would want to do something like this but it does not seem to work:
cmd := exec.Command("ssh", "user#x.x.x.x")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
stdin, _ := cmd.StdinPipe()
stdin.Write([]byte("password\n"))
cmd.Run()
However; I'm not sure how to do this in go because every time i exec this ssh command I am only able to get the output. I am unable to input my password automatically from code.
Does anyone have examples of writing to terminal processes such as ssh? If so, please share.
Thanks to the comments above, I was able to get ssh access working with a password. I used golang's ssh api library. It was fairly simple as I followed the examples from:
https://code.google.com/p/go/source/browse/ssh/example_test.go?repo=crypto
Specifically:
func ExampleDial() {
// An SSH client is represented with a ClientConn. Currently only
// the "password" authentication method is supported.
//
// To authenticate with the remote server you must pass at least one
// implementation of AuthMethod via the Auth field in ClientConfig.
config := &ClientConfig{
User: "username",
Auth: []AuthMethod{
Password("yourpassword"),
},
}
client, err := Dial("tcp", "yourserver.com:22", config)
if err != nil {
panic("Failed to dial: " + err.Error())
}
// Each ClientConn can support multiple interactive sessions,
// represented by a Session.
session, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer session.Close()
// Once a Session is created, you can execute a single command on
// the remote side using the Run method.
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("/usr/bin/whoami"); err != nil {
panic("Failed to run: " + err.Error())
}
fmt.Println(b.String())
}
This is a modified/complete version of above example https://godoc.org/golang.org/x/crypto/ssh#example-Dial
First get terminal package by go get golang.org/x/crypto/ssh
package main
import (
"bufio"
"bytes"
"fmt"
"os"
"strings"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
if len(os.Args) < 3 {
usage := "\n./remote-ssh {host} {port}"
fmt.Println(usage)
} else {
host := os.Args[1]
port := os.Args[2]
username, password := credentials()
config := &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
}
connectingMsg := fmt.Sprintf("\nConnecting to %s:%v remote server...", host, port)
fmt.Println(connectingMsg)
hostAddress := strings.Join([]string{host, port}, ":")
// fmt.Println("Host add %s ", hostAddress)
client, err := ssh.Dial("tcp", hostAddress, config)
if err != nil {
panic("Failed to dial: " + err.Error())
}
for {
session, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer session.Close()
// Once a Session is created, can execute a single command on remote side
var cmd string
str := "\nEnter command (e.g. /usr/bin/whoami OR enter 'exit' to return) : "
fmt.Print(str)
fmt.Scanf("%s", &cmd)
if cmd == "exit" || cmd == "EXIT" {
break
}
s := fmt.Sprintf("Wait for command '%s' run and response...", cmd)
fmt.Println(s)
var b bytes.Buffer
session.Stdout = &b
if err := session.Run(cmd); err != nil {
panic("Failed to run: " + err.Error())
}
fmt.Println(b.String())
}
}
}
func credentials() (string, string) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Username: ")
username, _ := reader.ReadString('\n')
fmt.Print("Enter Password: ")
bytePassword, err := terminal.ReadPassword(0)
if err != nil {
panic(err)
}
password := string(bytePassword)
return strings.TrimSpace(username), strings.TrimSpace(password)
}
https://play.golang.org/p/4Ad1vKNXmI

In golang how can I write the stdout of an exec.Cmd to a file?

I am trying to run a shell command, capture stdout and write that output to a file. However, I seem to be missing a few steps, as the file I am trying to write is empty when the program exists. How can I capture the stdout of the command and write that to a file?
package main
import (
"bufio"
"io"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("echo", "'WHAT THE HECK IS UP'")
// open the out file for writing
outfile, err := os.Create("./out.txt")
if err != nil {
panic(err)
}
defer outfile.Close()
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
writer := bufio.NewWriter(outfile)
err = cmd.Start()
if err != nil {
panic(err)
}
go io.Copy(writer, stdoutPipe)
cmd.Wait()
}
Thanks to KirkMcDonald on the #go-nuts irc channel, I solved this by assigning the output file to cmd.Stdout, which means that stdout writes directly to the file. It looks like this:
package main
import (
"os"
"os/exec"
)
func main() {
cmd := exec.Command("echo", "'WHAT THE HECK IS UP'")
// open the out file for writing
outfile, err := os.Create("./out.txt")
if err != nil {
panic(err)
}
defer outfile.Close()
cmd.Stdout = outfile
err = cmd.Start(); if err != nil {
panic(err)
}
cmd.Wait()
}
You can also use:
cmd.Stdout = os.Stdout
which will redirect all cmd output to the OS' standard output.
You need to flush the writer. Add the following:
writer := bufio.NewWriter(outfile)
defer writer.Flush()