From 3beb00998be0dd84c217841dc6c3a10a0c15fbca Mon Sep 17 00:00:00 2001 From: sawka Date: Fri, 30 Sep 2022 17:22:28 -0700 Subject: [PATCH] support sending password to remote --- cmd/main-server.go | 10 +++--- pkg/cmdrunner/cmdrunner.go | 12 +++++-- pkg/remote/remote.go | 73 ++++++++++++++++++++++++++++++++++++-- pkg/sstore/sstore.go | 1 + 4 files changed, 86 insertions(+), 10 deletions(-) diff --git a/cmd/main-server.go b/cmd/main-server.go index 14e7d4c6c..e3a6e4c38 100644 --- a/cmd/main-server.go +++ b/cmd/main-server.go @@ -378,11 +378,11 @@ func main() { fmt.Printf("[error] ensuring test01 remote: %v\n", err) return } - err = sstore.AddTest02Remote(context.Background()) - if err != nil { - fmt.Printf("[error] ensuring test02 remote: %v\n", err) - return - } + //err = sstore.AddTest02Remote(context.Background()) + //if err != nil { + // fmt.Printf("[error] ensuring test02 remote: %v\n", err) + // return + //} _, err = sstore.EnsureDefaultSession(context.Background()) if err != nil { fmt.Printf("[error] ensuring default session: %v\n", err) diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 2871ed29c..0fdaa7518 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -593,6 +593,9 @@ func RemoteNewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss } remoteOpts.Color = color } + if pk.Kwargs["password"] != "" { + sshOpts.SSHPassword = pk.Kwargs["password"] + } r := &sstore.RemoteType{ RemoteId: scbase.GenSCUUID(), PhysicalId: "", @@ -737,12 +740,15 @@ func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up if newRemote == "" { return nil, nil } - remoteName, rptr, _, _, err := resolveRemote(ctx, newRemote, ids.SessionId, ids.WindowId) + remoteName, rptr, _, rstate, err := resolveRemote(ctx, newRemote, ids.SessionId, ids.WindowId) if err != nil { return nil, err } if rptr == nil { - return nil, fmt.Errorf("/cr error: remote [%s] not found", newRemote) + return nil, fmt.Errorf("/cr error: remote %q not found", newRemote) + } + if rstate.Archived { + return nil, fmt.Errorf("/cr error: remote %q cannot switch to archived remote", newRemote) } err = sstore.UpdateCurRemote(ctx, ids.SessionId, ids.WindowId, *rptr) if err != nil { @@ -755,7 +761,7 @@ func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up CurRemote: *rptr, }, Info: &sstore.InfoMsgType{ - InfoMsg: fmt.Sprintf("current remote = %s", remoteName), + InfoMsg: fmt.Sprintf("current remote = %q", remoteName), TimeoutMs: 2000, }, } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index 31abc29ca..f5e56fce6 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -15,6 +15,7 @@ import ( "strings" "sync" "syscall" + "time" "github.com/armon/circbuf" "github.com/creack/pty" @@ -111,10 +112,11 @@ type RemoteRuntimeState struct { DefaultState *sstore.RemoteState `json:"defaultstate"` ConnectMode string `json:"connectmode"` AutoInstall bool `json:"autoinstall"` - Archived bool `json:"archived"` + Archived bool `json:"archived,omitempty"` RemoteIdx int64 `json:"remoteidx"` UName string `json:"uname"` MShellVersion string `json:"mshellversion"` + WaitingForPassword bool `json:"waitingforpassword,omitempty"` } func (state RemoteRuntimeState) IsConnected() bool { @@ -391,6 +393,9 @@ func (msh *MShellProc) GetRemoteRuntimeState() RemoteRuntimeState { if msh.InstallErr != nil { state.InstallErrorStr = msh.InstallErr.Error() } + if msh.Status == StatusConnecting { + state.WaitingForPassword = msh.isWaitingForPassword_nolock() + } local := (msh.Remote.SSHOpts == nil || msh.Remote.SSHOpts.Local) vars := make(map[string]string) vars["user"] = msh.Remote.RemoteUser @@ -659,8 +664,25 @@ func sendRemotePtyUpdate(remoteId string, dataOffset int64, data []byte) { sstore.MainBus.SendUpdate("", update) } +func (msh *MShellProc) isWaitingForPassword_nolock() bool { + barr := msh.PtyBuffer.Bytes() + if len(barr) == 0 { + return false + } + nlIdx := bytes.LastIndex(barr, []byte{'\n'}) + var lastLine string + if nlIdx == -1 { + lastLine = string(barr) + } else { + lastLine = string(barr[nlIdx+1:]) + } + pwIdx := strings.Index(lastLine, "assword") + return pwIdx != -1 +} + func (msh *MShellProc) RunPtyReadLoop(cmdPty *os.File) { buf := make([]byte, PtyReadBufSize) + var isWaiting bool for { n, readErr := cmdPty.Read(buf) if readErr == io.EOF { @@ -670,11 +692,55 @@ func (msh *MShellProc) RunPtyReadLoop(cmdPty *os.File) { msh.WriteToPtyBuffer("*error reading from controlling-pty: %v\n", readErr) break } + var newIsWaiting bool msh.WithLock(func() { curOffset := msh.PtyBuffer.TotalWritten() msh.PtyBuffer.Write(buf[0:n]) sendRemotePtyUpdate(msh.Remote.RemoteId, curOffset, buf[0:n]) + newIsWaiting = msh.isWaitingForPassword_nolock() }) + if newIsWaiting != isWaiting { + isWaiting = newIsWaiting + go msh.NotifyRemoteUpdate() + } + } +} + +func (msh *MShellProc) WaitAndSendPassword(pw string) { + var numWaits int + for { + var isWaiting bool + var isConnecting bool + msh.WithLock(func() { + isWaiting = msh.isWaitingForPassword_nolock() + isConnecting = msh.Status == StatusConnecting + }) + if !isConnecting { + break + } + if !isWaiting { + numWaits = 0 + time.Sleep(100 * time.Millisecond) + continue + } + numWaits++ + if numWaits < 10 { + time.Sleep(100 * time.Millisecond) + } else { + // send password + msh.WithLock(func() { + if msh.ControllingPty == nil { + return + } + pwBytes := []byte(pw + "\r") + msh.writeToPtyBuffer_nolock("~[sent password]\r\n") + _, err := msh.ControllingPty.Write(pwBytes) + if err != nil { + msh.writeToPtyBuffer_nolock("*cannot write password to controlling pty: %v\n", err) + } + }) + break + } } } @@ -770,7 +836,7 @@ func (msh *MShellProc) Launch() { msh.WriteToPtyBuffer("connecting to %s...\n", remoteCopy.RemoteCanonicalName) sshOpts := convertSSHOpts(remoteCopy.SSHOpts) sshOpts.SSHErrorsToTty = true - if remoteCopy.ConnectMode != sstore.ConnectModeManual { + if remoteCopy.ConnectMode != sstore.ConnectModeManual && remoteCopy.SSHOpts.SSHPassword == "" { sshOpts.BatchMode = true } cmdStr := MakeServerCommandStr() @@ -788,6 +854,9 @@ func (msh *MShellProc) Launch() { } }() go msh.RunPtyReadLoop(cmdPty) + if remoteCopy.SSHOpts.SSHPassword != "" { + go msh.WaitAndSendPassword(remoteCopy.SSHOpts.SSHPassword) + } makeClientCtx, makeClientCancelFn := context.WithCancel(context.Background()) defer makeClientCancelFn() msh.WithLock(func() { diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 23ee71f5f..d6b6f94fb 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -476,6 +476,7 @@ type SSHOpts struct { SSHOptsStr string `json:"sshopts,omitempty"` SSHIdentity string `json:"sshidentity,omitempty"` SSHPort int `json:"sshport,omitempty"` + SSHPassword string `json:"sshpassword,omitempty"` } type RemoteOptsType struct {