use ssh.Unmarshal

Thanks to Merovius for the hint.
This commit is contained in:
Michael Stapelberg 2018-10-29 18:42:56 +01:00
parent 77088e03a7
commit c11ed6a015

87
ssh.go
View File

@ -86,67 +86,81 @@ type session struct {
channel ssh.Channel channel ssh.Channel
} }
func stringFromPayload(payload []byte, offset int) (string, int, error) { // ptyreq is a Pseudo-Terminal request as per RFC4254 6.2.
if got, want := len(payload), offset+4; got < want { type ptyreq struct {
return "", 0, fmt.Errorf("request payload too short: got %d, want >= %d", got, want) TERM string // e.g. vt100
} WidthCharacters uint32
namelen := binary.BigEndian.Uint32(payload[offset : offset+4]) HeightRows uint32
if got, want := len(payload), offset+4+int(namelen); got < want { WidthPixels uint32
return "", 0, fmt.Errorf("request payload too short: got %d, want >= %d", got, want) HeightPixels uint32
} Modes string
name := payload[offset+4 : offset+4+int(namelen)] }
return string(name), offset + 4 + int(namelen), nil
// windowchange is a Window Dimension Change as per RFC4254 6.7.
type windowchange struct {
WidthColumns uint32
HeightRows uint32
WidthPixels uint32
HeightPixels uint32
}
// env is a Environment Variable request as per RFC4254 6.4.
type env struct {
VariableName string
VariableValue string
}
// execR is a Command request as per RFC4254 6.5.
type execR struct {
Command string
} }
func (s *session) request(ctx context.Context, req *ssh.Request) error { func (s *session) request(ctx context.Context, req *ssh.Request) error {
switch req.Type { switch req.Type {
case "pty-req": case "pty-req":
var r ptyreq
if err := ssh.Unmarshal(req.Payload, &r); err != nil {
return err
}
var err error var err error
s.ptyf, s.ttyf, err = pty.Open() s.ptyf, s.ttyf, err = pty.Open()
if err != nil { if err != nil {
return err return err
} }
_, next, err := stringFromPayload(req.Payload, 0)
if err != nil {
return err
}
if got, want := len(req.Payload), next+4+4; got < want {
return fmt.Errorf("request payload too short: got %d, want >= %d", got, want)
}
w, h := parseDims(req.Payload[next:]) SetWinsize(s.ptyf.Fd(), r.WidthCharacters, r.HeightRows)
SetWinsize(s.ptyf.Fd(), w, h)
// Responding true (OK) here will let the client // Responding true (OK) here will let the client
// know we have a pty ready for input // know we have a pty ready for input
req.Reply(true, nil) req.Reply(true, nil)
case "window-change": case "window-change":
w, h := parseDims(req.Payload) var r windowchange
SetWinsize(s.ptyf.Fd(), w, h) if err := ssh.Unmarshal(req.Payload, &r); err != nil {
return err
}
SetWinsize(s.ptyf.Fd(), r.WidthColumns, r.HeightRows)
case "env": case "env":
name, next, err := stringFromPayload(req.Payload, 0) var r env
if err != nil { if err := ssh.Unmarshal(req.Payload, &r); err != nil {
return err return err
} }
value, _, err := stringFromPayload(req.Payload, next) s.env = append(s.env, fmt.Sprintf("%s=%s", r.VariableName, r.VariableValue))
if err != nil {
return err
}
s.env = append(s.env, fmt.Sprintf("%s=%s", name, value))
case "shell": case "shell":
req.Payload = []byte("\x00\x00\x00\x00sh") req.Payload = []byte("\x00\x00\x00\x00sh")
fallthrough fallthrough
case "exec": case "exec":
if got, want := len(req.Payload), 4; got < want { var r execR
return fmt.Errorf("exec request payload too short: got %d, want >= %d", got, want) if err := ssh.Unmarshal(req.Payload, &r); err != nil {
return err
} }
cmdline, err := shlex.Split(string(req.Payload[4:])) cmdline, err := shlex.Split(r.Command)
if err != nil { if err != nil {
return err return err
} }
@ -157,7 +171,7 @@ func (s *session) request(ctx context.Context, req *ssh.Request) error {
var cmd *exec.Cmd var cmd *exec.Cmd
if _, err := exec.LookPath("sh"); err == nil { if _, err := exec.LookPath("sh"); err == nil {
cmd = exec.CommandContext(ctx, "sh", "-c", string(req.Payload[4:])) cmd = exec.CommandContext(ctx, "sh", "-c", r.Command)
} else { } else {
cmd = exec.CommandContext(ctx, cmdline[0], cmdline[1:]...) cmd = exec.CommandContext(ctx, cmdline[0], cmdline[1:]...)
} }
@ -253,13 +267,6 @@ func (s *session) request(ctx context.Context, req *ssh.Request) error {
return nil return nil
} }
// parseDims extracts terminal dimensions (width x height) from the provided buffer.
func parseDims(b []byte) (uint32, uint32) {
w := binary.BigEndian.Uint32(b)
h := binary.BigEndian.Uint32(b[4:])
return w, h
}
// Winsize stores the Height and Width of a terminal. // Winsize stores the Height and Width of a terminal.
type Winsize struct { type Winsize struct {
Height uint16 Height uint16