Skip to content

x/crypto/ssh: listening on remote address blocks indefinitely when address doesn't contain a host part #33227

Open
@jeffwilliams

Description

@jeffwilliams

What version of Go are you using (go version)?

$ go version
go version go1.11.2 linux/amd64

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/kanobe/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/kanobe/src/go"
GOPROXY=""
GORACE=""
GOROOT="/home/kanobe/download/go"
GOTMPDIR=""
GOTOOLDIR="/home/kanobe/download/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build496899549=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Created an SSH connection using ssh.Dial, and opened a Listener to listen on a port without specifying a host address. Then connected to that port (i.e. using telnet).

This program demonstrates the issue:

package main

import (
  "io/ioutil"
  "log"
  "os"
  "path"

  "golang.org/x/crypto/ssh"
)

func buildSshConfig(user, keyfile string) (conf *ssh.ClientConfig) {
  key, err := ioutil.ReadFile(keyfile)
  if err != nil {
    log.Fatal("unable to build ssh config: ", err)
  }

  signer, err := ssh.ParsePrivateKey(key)
  if err != nil {
    log.Fatal("unable to build ssh config: ", err)
  }

  conf = &ssh.ClientConfig{
    User: user,
    Auth: []ssh.AuthMethod{
      // Use the PublicKeys method for remote authentication.
      ssh.PublicKeys(signer),
    },
    HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  }

  return
}

func main() {
  config := buildSshConfig(os.Getenv("USER"),
    path.Join(os.Getenv("HOME"), ".ssh/id_rsa"))

  conn, err := ssh.Dial("tcp", "localhost:22", config)
  if err != nil {
    log.Fatal("unable to connect: ", err)
  }
  defer conn.Close()

  // Request the remote side to open port 8080
  l, err := conn.Listen("tcp", ":8080")
  if err != nil {
    log.Fatal("unable to register tcp forward: ", err)
  }
  defer l.Close()

  log.Println("Calling Accept")
  l.Accept()
  log.Println("Accept returned")

}

What did you expect to see?

Calling Accept
Accept Returned

What did you see instead?

Calling Accept

Additional Info

It seems that if the local address is not specified in ssh.Client.Listen, the Accept call blocks indefinitely. For example this line fails in the above program:

  l, err := conn.Listen("tcp", ":8080")

But when changed to this it succeeds:

  l, err := conn.Listen("tcp", "127.0.0.1:8080")

The host may be omitted in net.Listen, so there is precedent for using no host. From net.Listen: "For TCP networks, if the host in the address parameter is empty or a literal unspecified IP address, Listen listens on all available unicast and anycast IP addresses of the local system."

I think at a minimum if host may not be omitted, the documentation should mention that it is not supported, and the Listen call should return an error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions