Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/crypto/ssh: can not open session to huawei CE5810 switch (got error "ssh: short read") #23058

Closed
duhaifeng opened this issue Dec 8, 2017 · 12 comments

Comments

@duhaifeng
Copy link

@duhaifeng duhaifeng commented Dec 8, 2017

Please answer these questions before submitting your issue. Thanks!

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

1.9.2

Does this issue reproduce with the latest release?

Yes

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

windows / amd64 (MacOS also)

What did you do?

If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.

Sample code as follows:

import (
	"golang.org/x/crypto/ssh"
	"net"
	"testing"
	"time"
)

func TestSssh(t *testing.T) {
	_, err := ssh.Dial("tcp", "192.168.1.1:22", &ssh.ClientConfig{
		User: "user_xxx",
		Auth: []ssh.AuthMethod{
			ssh.Password("password_xxx"),
		},
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
		Timeout: 20 * time.Second,
		Config: ssh.Config{
			Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com",
				"arcfour256", "arcfour128", "aes128-cbc", "aes192-cbc", "aes256-cbc", "3des-cbc", "des-cbc",
			},
		},
	})
	if err != nil {
		t.Error("SSH Dial err:%s", err.Error())  //always get a error : handshake failed: ssh: short read
	}
}

What did you expect to see?

No error

What did you see instead?

When dial to Huawei CE5810 switch the connection can not be created.
The function ssh.Dial() always returns a error "handshake failed: ssh: short read".
I have set debugMux=true but no any more output.
I found that this error was output in function handshakeTransport.readOnePacket(first=true) {err = <-kex.done}

@gopherbot gopherbot added this to the Unreleased milestone Dec 8, 2017
@duhaifeng
Copy link
Author

@duhaifeng duhaifeng commented Dec 8, 2017

If I use python netmiko library to connect the same Huawei CE5810 switch it's very OK.
Python code as follows:

import logging
import netmiko
import time

class BaseSshDriver(object):
    def __init__(self, ip, port, user, password, brand):
        self.ip = ip
        self.port = port
        self.user = user
        self.password = password
        self.brand = brand
        self.timestamp = None
        self.conn = None

    def open_session(self):
        try:
            device = {
                'device_type': self.brand,
                'ip': self.ip,
                'username': self.user,
                'password': self.password,
                'port': self.port,
            }
            # open ssh session based on netmiko lib
            self.conn = netmiko.ConnectHandler(**device)
        except Exception as e:
            logging.error("Failed open ssh session: %s" % str(e))
            return False
        self.timestamp = time.time()
        return True

    def is_session_opened(self):
        if not self.conn:
            return False
        try:
            connected = self.conn.find_prompt()
        except Exception as e:
            logging.debug("Find ssh prompt error: %s" % str(e))
            return False

        return connected

    def send_cmd(self, commands):
        if not self.is_session_opened():
            return "SSH session to %s is invalid!" % self.ip, 400
        try:
            output = ""
            for command in commands:
                result = self.conn.send_command(command, strip_prompt=False, max_loops=800)
                output = output + "\n" + result
        except Exception as e:
            logging.error("SSH get error. %s" % repr(e))
            return str(e), 400

        return str(output), 200
@rasky
Copy link
Member

@rasky rasky commented Dec 8, 2017

Can you try without specifying a cipher list?
Can you try to debug and see where the short read error is generated?

@duhaifeng
Copy link
Author

@duhaifeng duhaifeng commented Dec 11, 2017

Hi, if connect to the switch without specifying a cipher list, I got a error: ssh: handshake failed: ssh: no common algorithm for client to server cipher; client offered: [], server offered: [aes256-cbc aes128-cbc 3des-cbc aes256-cbc des-cbc].
If I use [aes256-cbc aes128-cbc 3des-cbc aes256-cbc des-cbc] for handshake got the same short read error.
I found the short read error is generated in "func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error)" which is defined in "x/crypto/ssh/kex.go".
The error is caused by "Unmarshal(packet, &kexDHReply)" (the value of param packet is "[31]"). It seems that the packet is too short so it makes Unmarshal() function throw an error.

func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
	hashFunc := crypto.SHA1

	var x *big.Int
	for {
		var err error
		if x, err = rand.Int(randSource, group.pMinus1); err != nil {
			return nil, err
		}
		if x.Sign() > 0 {
			break
		}
	}

	X := new(big.Int).Exp(group.g, x, group.p)
	kexDHInit := kexDHInitMsg{
		X: X,
	}
	if err := c.writePacket(Marshal(&kexDHInit)); err != nil {
		return nil, err
	}

	packet, err := c.readPacket()
	if err != nil {
		return nil, err
	}

	var kexDHReply kexDHReplyMsg
	if err = Unmarshal(packet, &kexDHReply); err != nil {
		///////////////////////////////////////////////////
		// the console output: >>>>> [31] ssh: short read. Seems the packet is too short so unmarshal failed.
		///////////////////////////////////////////////////
		fmt.Println(">>>>>", packet, err)
		return nil, err
	}

	ki, err := group.diffieHellman(kexDHReply.Y, x)
	if err != nil {
		return nil, err
	}

	h := hashFunc.New()
	magics.write(h)
	writeString(h, kexDHReply.HostKey)
	writeInt(h, X)
	writeInt(h, kexDHReply.Y)
	K := make([]byte, intLength(ki))
	marshalInt(K, ki)
	h.Write(K)

	return &kexResult{
		H:         h.Sum(nil),
		K:         K,
		HostKey:   kexDHReply.HostKey,
		Signature: kexDHReply.Signature,
		Hash:      crypto.SHA1,
	}, nil
}

The follows is a analysis for function Unmarshal() which is called by function Client():

func Unmarshal(data []byte, out interface{}) error {
	......
		case reflect.Slice:
			switch t.Elem().Kind() {
			case reflect.Uint8:
				if structType.Field(i).Tag.Get("ssh") == "rest" {
					field.Set(reflect.ValueOf(data))
					data = nil
				} else {
					var s []byte
					///////////////////////////////////////////////////
					// the error is created at here. 
					// parseString() asks len(data) > 4 (in fact len(data) = 1) so it returns false.
					///////////////////////////////////////////////////
					if s, data, ok = parseString(data); !ok { 
						return errShortRead
					}
					field.Set(reflect.ValueOf(s))
				}
	......
}
@duhaifeng
Copy link
Author

@duhaifeng duhaifeng commented Dec 14, 2017

@rasky Hi rasky. How long can this bug be fixed. I have more than 200 CE5810 switches to connect. We need your help.

@bradfitz
Copy link
Contributor

@bradfitz bradfitz commented Dec 14, 2017

/cc @hanwen

@hanwen
Copy link
Contributor

@hanwen hanwen commented Dec 14, 2017

can you try to get a debug dump of both the python code and the golang one? There is a debugHandshake var you can set.

I suspect the python code is using a different kex algorithm.

@rasky
Copy link
Member

@rasky rasky commented Dec 17, 2017

@duhaifeng also, if you could expose one of those devices on a public IP address without security concerns, that would also help debugging.

@duhaifeng
Copy link
Author

@duhaifeng duhaifeng commented Dec 18, 2017

@rasky Thanks rasky. I asked my leader to bind a public IP for the switch. But he told me it's insecurity and refused my request. Now I'm trying to compare the go and python code to find the problem. The bad thing is when I updated the python netmiko lib to Version 2.0 it throws an error too. I'm rolling back the python lib version to get the right kex algorithm. If I made it I will upload the code as soon as quickly.

@duhaifeng duhaifeng closed this Dec 21, 2017
@hanwen
Copy link
Contributor

@hanwen hanwen commented Dec 21, 2017

you could try to set different values for config.KeyExchanges and see if there is any algorithm that works for you.

the algorithms supported are listed here:

https://go.googlesource.com/crypto/+/d585fd2cc9195196078f516b69daff6744ef5e84/ssh/kex.go#20

I suspect you are trying to use dh group14 now and your device only works with dh group1.

@rasky rasky reopened this Dec 21, 2017
@duhaifeng
Copy link
Author

@duhaifeng duhaifeng commented Dec 25, 2017

Sorry. All of the algorithms can not work, the switch returns an error: "SSH Dial err: ssh: handshake failed: ssh: no common algorithm for client to server cipher; client offered: [], server offered: [aes256-cbc aes128-cbc 3des-cbc aes256-cbc des-cbc] " . If I use [aes256-cbc aes128-cbc 3des-cbc aes256-cbc des-cbc] then short read error again.

@duhaifeng
Copy link
Author

@duhaifeng duhaifeng commented Jan 2, 2018

@hanwen @rasky I updated the switch's OS and the problem resolved. Thanks.

@duhaifeng duhaifeng closed this Jan 2, 2018
@Greyh4t
Copy link

@Greyh4t Greyh4t commented Apr 13, 2018

Hi, @rasky , i have the same problem as duhaifeng , and i have a public IP to reappear this.
my go verison is go1.10 windows/amd64

code

package main

import (
	"fmt"
	"net"
	"time"

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

func main() {
	config := new(ssh.ClientConfig)
	config.SetDefaults()
	config.User = "user"
	config.Auth = []ssh.AuthMethod{ssh.Password("passwd")}
	config.Timeout = time.Second * 5
	config.Ciphers = append(config.Ciphers, "aes256-cbc", "aes128-cbc",
		"3des-cbc", "aes256-cbc", "des-cbc")
	config.HostKeyCallback = func(hostname string, remote net.Addr,
		key ssh.PublicKey) error {
		return nil
	}
	client, err := ssh.Dial("tcp", "106.39.177.129:22", config)
	if err != nil {
		fmt.Println(err)
	} else {
		client.Close()
	}
}
@golang golang locked and limited conversation to collaborators Apr 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.