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
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@duhaifeng
Copy link

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

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 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?

@rasky rasky added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Dec 8, 2017
@duhaifeng
Copy link
Author

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

@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

/cc @hanwen

@hanwen
Copy link
Contributor

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 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

@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.

@hanwen
Copy link
Contributor

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

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 commented Jan 2, 2018

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

@Greyh4t
Copy link

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.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

6 participants