diff options
Diffstat (limited to 'vendor/golang.org/x/crypto/ssh')
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/channel.go | 4 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/client.go | 6 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/client_auth.go | 4 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/client_auth_test.go | 21 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/common.go | 15 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/handshake.go | 416 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/handshake_test.go | 366 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/keys.go | 31 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/keys_test.go | 18 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/mux.go | 4 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/server.go | 12 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/terminal/terminal.go | 73 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go | 59 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/terminal/util.go | 38 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/terminal/util_windows.go | 35 | ||||
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/transport.go | 18 |
16 files changed, 722 insertions, 398 deletions
diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go index 6d709b50b..195530ea0 100644 --- a/vendor/golang.org/x/crypto/ssh/channel.go +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -461,8 +461,8 @@ func (m *mux) newChannel(chanType string, direction channelDirection, extraData pending: newBuffer(), extPending: newBuffer(), direction: direction, - incomingRequests: make(chan *Request, 16), - msg: make(chan interface{}, 16), + incomingRequests: make(chan *Request, chanSize), + msg: make(chan interface{}, chanSize), chanType: chanType, extraData: extraData, mux: m, diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index 0212a20c9..c97f2978e 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -40,7 +40,7 @@ func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { return nil } - ch = make(chan NewChannel, 16) + ch = make(chan NewChannel, chanSize) c.channelHandlers[channelType] = ch return ch } @@ -97,13 +97,11 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e c.transport = newClientTransport( newTransport(c.sshConn.conn, config.Rand, true /* is client */), c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) - if err := c.transport.requestInitialKeyChange(); err != nil { + if err := c.transport.waitSession(); err != nil { return err } - // We just did the key change, so the session ID is established. c.sessionID = c.transport.getSessionID() - return c.clientAuthenticate(config) } diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index 294af0d48..fd1ec5dda 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -30,8 +30,10 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error { // then any untried methods suggested by the server. tried := make(map[string]bool) var lastMethods []string + + sessionID := c.transport.getSessionID() for auth := AuthMethod(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(c.transport.getSessionID(), config.User, c.transport, config.Rand) + ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand) if err != nil { return err } diff --git a/vendor/golang.org/x/crypto/ssh/client_auth_test.go b/vendor/golang.org/x/crypto/ssh/client_auth_test.go index 1409276ec..1d9681a06 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth_test.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth_test.go @@ -77,7 +77,6 @@ func tryAuth(t *testing.T, config *ClientConfig) error { return nil, errors.New("keyboard-interactive failed") }, AuthLogCallback: func(conn ConnMetadata, method string, err error) { - t.Logf("user %q, method %q: %v", conn.User(), method, err) }, } serverConfig.AddHostKey(testSigners["rsa"]) @@ -278,18 +277,18 @@ func TestClientLoginCert(t *testing.T) { } clientConfig.Auth = append(clientConfig.Auth, PublicKeys(certSigner)) - t.Log("should succeed") + // should succeed if err := tryAuth(t, clientConfig); err != nil { t.Errorf("cert login failed: %v", err) } - t.Log("corrupted signature") + // corrupted signature cert.Signature.Blob[0]++ if err := tryAuth(t, clientConfig); err == nil { t.Errorf("cert login passed with corrupted sig") } - t.Log("revoked") + // revoked cert.Serial = 666 cert.SignCert(rand.Reader, testSigners["ecdsa"]) if err := tryAuth(t, clientConfig); err == nil { @@ -297,13 +296,13 @@ func TestClientLoginCert(t *testing.T) { } cert.Serial = 1 - t.Log("sign with wrong key") + // sign with wrong key cert.SignCert(rand.Reader, testSigners["dsa"]) if err := tryAuth(t, clientConfig); err == nil { t.Errorf("cert login passed with non-authoritative key") } - t.Log("host cert") + // host cert cert.CertType = HostCert cert.SignCert(rand.Reader, testSigners["ecdsa"]) if err := tryAuth(t, clientConfig); err == nil { @@ -311,14 +310,14 @@ func TestClientLoginCert(t *testing.T) { } cert.CertType = UserCert - t.Log("principal specified") + // principal specified cert.ValidPrincipals = []string{"user"} cert.SignCert(rand.Reader, testSigners["ecdsa"]) if err := tryAuth(t, clientConfig); err != nil { t.Errorf("cert login failed: %v", err) } - t.Log("wrong principal specified") + // wrong principal specified cert.ValidPrincipals = []string{"fred"} cert.SignCert(rand.Reader, testSigners["ecdsa"]) if err := tryAuth(t, clientConfig); err == nil { @@ -326,21 +325,21 @@ func TestClientLoginCert(t *testing.T) { } cert.ValidPrincipals = nil - t.Log("added critical option") + // added critical option cert.CriticalOptions = map[string]string{"root-access": "yes"} cert.SignCert(rand.Reader, testSigners["ecdsa"]) if err := tryAuth(t, clientConfig); err == nil { t.Errorf("cert login passed with unrecognized critical option") } - t.Log("allowed source address") + // allowed source address cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24"} cert.SignCert(rand.Reader, testSigners["ecdsa"]) if err := tryAuth(t, clientConfig); err != nil { t.Errorf("cert login with source-address failed: %v", err) } - t.Log("disallowed source address") + // disallowed source address cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42"} cert.SignCert(rand.Reader, testSigners["ecdsa"]) if err := tryAuth(t, clientConfig); err == nil { diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go index 2c72ab544..faabb7ef9 100644 --- a/vendor/golang.org/x/crypto/ssh/common.go +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -104,6 +104,21 @@ type directionAlgorithms struct { Compression string } +// rekeyBytes returns a rekeying intervals in bytes. +func (a *directionAlgorithms) rekeyBytes() int64 { + // According to RFC4344 block ciphers should rekey after + // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is + // 128. + switch a.Cipher { + case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID: + return 16 * (1 << 32) + + } + + // For others, stick with RFC4253 recommendation to rekey after 1 Gb of data. + return 1 << 30 +} + type algorithms struct { kex string hostKey string diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 37d42e47f..57f2d3daf 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -19,6 +19,11 @@ import ( // messages are wrong when using ECDH. const debugHandshake = false +// chanSize sets the amount of buffering SSH connections. This is +// primarily for testing: setting chanSize=0 uncovers deadlocks more +// quickly. +const chanSize = 16 + // keyingTransport is a packet based transport that supports key // changes. It need not be thread-safe. It should pass through // msgNewKeys in both directions. @@ -53,34 +58,58 @@ type handshakeTransport struct { incoming chan []byte readError error + mu sync.Mutex + writeError error + sentInitPacket []byte + sentInitMsg *kexInitMsg + pendingPackets [][]byte // Used when a key exchange is in progress. + + // If the read loop wants to schedule a kex, it pings this + // channel, and the write loop will send out a kex + // message. The boolean is whether this is the first request or not. + requestKex chan bool + + // If the other side requests or confirms a kex, its kexInit + // packet is sent here for the write loop to find it. + startKex chan *pendingKex + // data for host key checking hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error dialAddress string remoteAddr net.Addr - readSinceKex uint64 + // Algorithms agreed in the last key exchange. + algorithms *algorithms - // Protects the writing side of the connection - mu sync.Mutex - cond *sync.Cond - sentInitPacket []byte - sentInitMsg *kexInitMsg - writtenSinceKex uint64 - writeError error + readPacketsLeft uint32 + readBytesLeft int64 + + writePacketsLeft uint32 + writeBytesLeft int64 // The session ID or nil if first kex did not complete yet. sessionID []byte } +type pendingKex struct { + otherInit []byte + done chan error +} + func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { t := &handshakeTransport{ conn: conn, serverVersion: serverVersion, clientVersion: clientVersion, - incoming: make(chan []byte, 16), - config: config, + incoming: make(chan []byte, chanSize), + requestKex: make(chan bool, 1), + startKex: make(chan *pendingKex, 1), + + config: config, } - t.cond = sync.NewCond(&t.mu) + + // We always start with a mandatory key exchange. + t.requestKex <- true return t } @@ -95,6 +124,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt t.hostKeyAlgorithms = supportedHostKeyAlgos } go t.readLoop() + go t.kexLoop() return t } @@ -102,6 +132,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) t.hostKeys = config.hostKeys go t.readLoop() + go t.kexLoop() return t } @@ -109,6 +140,20 @@ func (t *handshakeTransport) getSessionID() []byte { return t.sessionID } +// waitSession waits for the session to be established. This should be +// the first thing to call after instantiating handshakeTransport. +func (t *handshakeTransport) waitSession() error { + p, err := t.readPacket() + if err != nil { + return err + } + if p[0] != msgNewKeys { + return fmt.Errorf("ssh: first packet should be msgNewKeys") + } + + return nil +} + func (t *handshakeTransport) id() string { if len(t.hostKeys) > 0 { return "server" @@ -116,6 +161,19 @@ func (t *handshakeTransport) id() string { return "client" } +func (t *handshakeTransport) printPacket(p []byte, write bool) { + action := "got" + if write { + action = "sent" + } + if p[0] == msgChannelData || p[0] == msgChannelExtendedData { + log.Printf("%s %s data (packet %d bytes)", t.id(), action, len(p)) + } else { + msg, err := decode(p) + log.Printf("%s %s %T %v (%v)", t.id(), action, msg, msg, err) + } +} + func (t *handshakeTransport) readPacket() ([]byte, error) { p, ok := <-t.incoming if !ok { @@ -125,8 +183,10 @@ func (t *handshakeTransport) readPacket() ([]byte, error) { } func (t *handshakeTransport) readLoop() { + first := true for { - p, err := t.readOnePacket() + p, err := t.readOnePacket(first) + first = false if err != nil { t.readError = err close(t.incoming) @@ -138,67 +198,198 @@ func (t *handshakeTransport) readLoop() { t.incoming <- p } - // If we can't read, declare the writing part dead too. + // Stop writers too. + t.recordWriteError(t.readError) + + // Unblock the writer should it wait for this. + close(t.startKex) + + // Don't close t.requestKex; it's also written to from writePacket. +} + +func (t *handshakeTransport) pushPacket(p []byte) error { + if debugHandshake { + t.printPacket(p, true) + } + return t.conn.writePacket(p) +} + +func (t *handshakeTransport) getWriteError() error { t.mu.Lock() defer t.mu.Unlock() - if t.writeError == nil { - t.writeError = t.readError + return t.writeError +} + +func (t *handshakeTransport) recordWriteError(err error) { + t.mu.Lock() + defer t.mu.Unlock() + if t.writeError == nil && err != nil { + t.writeError = err } - t.cond.Broadcast() } -func (t *handshakeTransport) readOnePacket() ([]byte, error) { - if t.readSinceKex > t.config.RekeyThreshold { - if err := t.requestKeyChange(); err != nil { - return nil, err +func (t *handshakeTransport) requestKeyExchange() { + select { + case t.requestKex <- false: + default: + // something already requested a kex, so do nothing. + } +} + +func (t *handshakeTransport) kexLoop() { + firstSent := false + +write: + for t.getWriteError() == nil { + var request *pendingKex + var sent bool + + for request == nil || !sent { + var ok bool + select { + case request, ok = <-t.startKex: + if !ok { + break write + } + case requestFirst := <-t.requestKex: + // For the first key exchange, both + // sides will initiate a key exchange, + // and both channels will fire. To + // avoid doing two key exchanges in a + // row, ignore our own request for an + // initial kex if we have already sent + // it out. + if firstSent && requestFirst { + + continue + } + } + + if !sent { + if err := t.sendKexInit(); err != nil { + t.recordWriteError(err) + break + } + firstSent = true + sent = true + } } + + if err := t.getWriteError(); err != nil { + if request != nil { + request.done <- err + } + break + } + + // We're not servicing t.requestKex, but that is OK: + // we never block on sending to t.requestKex. + + // We're not servicing t.startKex, but the remote end + // has just sent us a kexInitMsg, so it can't send + // another key change request. + + err := t.enterKeyExchange(request.otherInit) + + t.mu.Lock() + t.writeError = err + t.sentInitPacket = nil + t.sentInitMsg = nil + t.writePacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.writeBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.writeBytesLeft = t.algorithms.w.rekeyBytes() + } + request.done <- t.writeError + + // kex finished. Push packets that we received while + // the kex was in progress. Don't look at t.startKex + // and don't increment writtenSinceKex: if we trigger + // another kex while we are still busy with the last + // one, things will become very confusing. + for _, p := range t.pendingPackets { + t.writeError = t.pushPacket(p) + if t.writeError != nil { + break + } + } + t.pendingPackets = t.pendingPackets[0:] + t.mu.Unlock() } + // drain startKex channel. We don't service t.requestKex + // because nobody does blocking sends there. + go func() { + for init := range t.startKex { + init.done <- t.writeError + } + }() + + // Unblock reader. + t.conn.Close() +} + +// The protocol uses uint32 for packet counters, so we can't let them +// reach 1<<32. We will actually read and write more packets than +// this, though: the other side may send more packets, and after we +// hit this limit on writing we will send a few more packets for the +// key exchange itself. +const packetRekeyThreshold = (1 << 31) + +func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { p, err := t.conn.readPacket() if err != nil { return nil, err } - t.readSinceKex += uint64(len(p)) + if t.readPacketsLeft > 0 { + t.readPacketsLeft-- + } else { + t.requestKeyExchange() + } + + if t.readBytesLeft > 0 { + t.readBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() + } + if debugHandshake { - if p[0] == msgChannelData || p[0] == msgChannelExtendedData { - log.Printf("%s got data (packet %d bytes)", t.id(), len(p)) - } else { - msg, err := decode(p) - log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err) - } + t.printPacket(p, false) } + + if first && p[0] != msgKexInit { + return nil, fmt.Errorf("ssh: first packet should be msgKexInit") + } + if p[0] != msgKexInit { return p, nil } - t.mu.Lock() - firstKex := t.sessionID == nil - err = t.enterKeyExchangeLocked(p) - if err != nil { - // drop connection - t.conn.Close() - t.writeError = err + kex := pendingKex{ + done: make(chan error, 1), + otherInit: p, } + t.startKex <- &kex + err = <-kex.done if debugHandshake { log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err) } - // Unblock writers. - t.sentInitMsg = nil - t.sentInitPacket = nil - t.cond.Broadcast() - t.writtenSinceKex = 0 - t.mu.Unlock() - if err != nil { return nil, err } - t.readSinceKex = 0 + t.readPacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.readBytesLeft = int64(t.config.RekeyThreshold) + } else { + t.readBytesLeft = t.algorithms.r.rekeyBytes() + } // By default, a key exchange is hidden from higher layers by // translating it into msgIgnore. @@ -213,61 +404,16 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) { return successPacket, nil } -// keyChangeCategory describes whether a key exchange is the first on a -// connection, or a subsequent one. -type keyChangeCategory bool - -const ( - firstKeyExchange keyChangeCategory = true - subsequentKeyExchange keyChangeCategory = false -) - -// sendKexInit sends a key change message, and returns the message -// that was sent. After initiating the key change, all writes will be -// blocked until the change is done, and a failed key change will -// close the underlying transport. This function is safe for -// concurrent use by multiple goroutines. -func (t *handshakeTransport) sendKexInit(isFirst keyChangeCategory) error { - var err error - +// sendKexInit sends a key change message. +func (t *handshakeTransport) sendKexInit() error { t.mu.Lock() - // If this is the initial key change, but we already have a sessionID, - // then do nothing because the key exchange has already completed - // asynchronously. - if !isFirst || t.sessionID == nil { - _, _, err = t.sendKexInitLocked(isFirst) - } - t.mu.Unlock() - if err != nil { - return err - } - if isFirst { - if packet, err := t.readPacket(); err != nil { - return err - } else if packet[0] != msgNewKeys { - return unexpectedMessageError(msgNewKeys, packet[0]) - } - } - return nil -} - -func (t *handshakeTransport) requestInitialKeyChange() error { - return t.sendKexInit(firstKeyExchange) -} - -func (t *handshakeTransport) requestKeyChange() error { - return t.sendKexInit(subsequentKeyExchange) -} - -// sendKexInitLocked sends a key change message. t.mu must be locked -// while this happens. -func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) { - // kexInits may be sent either in response to the other side, - // or because our side wants to initiate a key change, so we - // may have already sent a kexInit. In that case, don't send a - // second kexInit. + defer t.mu.Unlock() if t.sentInitMsg != nil { - return t.sentInitMsg, t.sentInitPacket, nil + // kexInits may be sent either in response to the other side, + // or because our side wants to initiate a key change, so we + // may have already sent a kexInit. In that case, don't send a + // second kexInit. + return nil } msg := &kexInitMsg{ @@ -295,53 +441,65 @@ func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexI packetCopy := make([]byte, len(packet)) copy(packetCopy, packet) - if err := t.conn.writePacket(packetCopy); err != nil { - return nil, nil, err + if err := t.pushPacket(packetCopy); err != nil { + return err } t.sentInitMsg = msg t.sentInitPacket = packet - return msg, packet, nil + + return nil } func (t *handshakeTransport) writePacket(p []byte) error { + switch p[0] { + case msgKexInit: + return errors.New("ssh: only handshakeTransport can send kexInit") + case msgNewKeys: + return errors.New("ssh: only handshakeTransport can send newKeys") + } + t.mu.Lock() defer t.mu.Unlock() + if t.writeError != nil { + return t.writeError + } - if t.writtenSinceKex > t.config.RekeyThreshold { - t.sendKexInitLocked(subsequentKeyExchange) + if t.sentInitMsg != nil { + // Copy the packet so the writer can reuse the buffer. + cp := make([]byte, len(p)) + copy(cp, p) + t.pendingPackets = append(t.pendingPackets, cp) + return nil } - for t.sentInitMsg != nil && t.writeError == nil { - t.cond.Wait() + + if t.writeBytesLeft > 0 { + t.writeBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() } - if t.writeError != nil { - return t.writeError + + if t.writePacketsLeft > 0 { + t.writePacketsLeft-- + } else { + t.requestKeyExchange() } - t.writtenSinceKex += uint64(len(p)) - switch p[0] { - case msgKexInit: - return errors.New("ssh: only handshakeTransport can send kexInit") - case msgNewKeys: - return errors.New("ssh: only handshakeTransport can send newKeys") - default: - return t.conn.writePacket(p) + if err := t.pushPacket(p); err != nil { + t.writeError = err } + + return nil } func (t *handshakeTransport) Close() error { return t.conn.Close() } -// enterKeyExchange runs the key exchange. t.mu must be held while running this. -func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error { +func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { if debugHandshake { log.Printf("%s entered key exchange", t.id()) } - myInit, myInitPacket, err := t.sendKexInitLocked(subsequentKeyExchange) - if err != nil { - return err - } otherInit := &kexInitMsg{} if err := Unmarshal(otherInitPacket, otherInit); err != nil { @@ -352,20 +510,20 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro clientVersion: t.clientVersion, serverVersion: t.serverVersion, clientKexInit: otherInitPacket, - serverKexInit: myInitPacket, + serverKexInit: t.sentInitPacket, } clientInit := otherInit - serverInit := myInit + serverInit := t.sentInitMsg if len(t.hostKeys) == 0 { - clientInit = myInit - serverInit = otherInit + clientInit, serverInit = serverInit, clientInit - magics.clientKexInit = myInitPacket + magics.clientKexInit = t.sentInitPacket magics.serverKexInit = otherInitPacket } - algs, err := findAgreedAlgorithms(clientInit, serverInit) + var err error + t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit) if err != nil { return err } @@ -388,16 +546,16 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro } } - kex, ok := kexAlgoMap[algs.kex] + kex, ok := kexAlgoMap[t.algorithms.kex] if !ok { - return fmt.Errorf("ssh: unexpected key exchange algorithm %v", algs.kex) + return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex) } var result *kexResult if len(t.hostKeys) > 0 { - result, err = t.server(kex, algs, &magics) + result, err = t.server(kex, t.algorithms, &magics) } else { - result, err = t.client(kex, algs, &magics) + result, err = t.client(kex, t.algorithms, &magics) } if err != nil { @@ -409,7 +567,7 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro } result.SessionID = t.sessionID - t.conn.prepareKeyChange(algs, result) + t.conn.prepareKeyChange(t.algorithms, result) if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { return err } diff --git a/vendor/golang.org/x/crypto/ssh/handshake_test.go b/vendor/golang.org/x/crypto/ssh/handshake_test.go index da53d3a0d..e61348fea 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake_test.go +++ b/vendor/golang.org/x/crypto/ssh/handshake_test.go @@ -9,6 +9,7 @@ import ( "crypto/rand" "errors" "fmt" + "io" "net" "reflect" "runtime" @@ -58,14 +59,46 @@ func netPipe() (net.Conn, net.Conn, error) { return c1, c2, nil } -func handshakePair(clientConf *ClientConfig, addr string) (client *handshakeTransport, server *handshakeTransport, err error) { +// noiseTransport inserts ignore messages to check that the read loop +// and the key exchange filters out these messages. +type noiseTransport struct { + keyingTransport +} + +func (t *noiseTransport) writePacket(p []byte) error { + ignore := []byte{msgIgnore} + if err := t.keyingTransport.writePacket(ignore); err != nil { + return err + } + debug := []byte{msgDebug, 1, 2, 3} + if err := t.keyingTransport.writePacket(debug); err != nil { + return err + } + + return t.keyingTransport.writePacket(p) +} + +func addNoiseTransport(t keyingTransport) keyingTransport { + return &noiseTransport{t} +} + +// handshakePair creates two handshakeTransports connected with each +// other. If the noise argument is true, both transports will try to +// confuse the other side by sending ignore and debug messages. +func handshakePair(clientConf *ClientConfig, addr string, noise bool) (client *handshakeTransport, server *handshakeTransport, err error) { a, b, err := netPipe() if err != nil { return nil, nil, err } - trC := newTransport(a, rand.Reader, true) - trS := newTransport(b, rand.Reader, false) + var trC, trS keyingTransport + + trC = newTransport(a, rand.Reader, true) + trS = newTransport(b, rand.Reader, false) + if noise { + trC = addNoiseTransport(trC) + trS = addNoiseTransport(trS) + } clientConf.SetDefaults() v := []byte("version") @@ -77,6 +110,13 @@ func handshakePair(clientConf *ClientConfig, addr string) (client *handshakeTran serverConf.SetDefaults() server = newServerTransport(trS, v, v, serverConf) + if err := server.waitSession(); err != nil { + return nil, nil, fmt.Errorf("server.waitSession: %v", err) + } + if err := client.waitSession(); err != nil { + return nil, nil, fmt.Errorf("client.waitSession: %v", err) + } + return client, server, nil } @@ -84,8 +124,9 @@ func TestHandshakeBasic(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("see golang.org/issue/7237") } - checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") + + checker := &syncChecker{make(chan int, 10)} + trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr", false) if err != nil { t.Fatalf("handshakePair: %v", err) } @@ -93,7 +134,13 @@ func TestHandshakeBasic(t *testing.T) { defer trC.Close() defer trS.Close() + <-checker.called + + clientDone := make(chan int, 0) + gotHalf := make(chan int, 0) + go func() { + defer close(clientDone) // Client writes a bunch of stuff, and does a key // change in the middle. This should not confuse the // handshake in progress @@ -103,219 +150,144 @@ func TestHandshakeBasic(t *testing.T) { t.Fatalf("sendPacket: %v", err) } if i == 5 { + <-gotHalf // halfway through, we request a key change. - err := trC.sendKexInit(subsequentKeyExchange) - if err != nil { - t.Fatalf("sendKexInit: %v", err) - } + trC.requestKeyExchange() + + // Wait until we can be sure the key + // change has really started before we + // write more. + <-checker.called } } - trC.Close() }() // Server checks that client messages come in cleanly i := 0 - for { - p, err := trS.readPacket() + err = nil + for ; i < 10; i++ { + var p []byte + p, err = trS.readPacket() if err != nil { break } - if p[0] == msgNewKeys { - continue + if i == 5 { + gotHalf <- 1 } + want := []byte{msgRequestSuccess, byte(i)} if bytes.Compare(p, want) != 0 { t.Errorf("message %d: got %q, want %q", i, p, want) } - i++ + } + <-clientDone + if err != nil && err != io.EOF { + t.Fatalf("server error: %v", err) } if i != 10 { t.Errorf("received %d messages, want 10.", i) } - // If all went well, we registered exactly 1 key change. - if len(checker.calls) != 1 { - t.Fatalf("got %d host key checks, want 1", len(checker.calls)) - } - - pub := testSigners["ecdsa"].PublicKey() - want := fmt.Sprintf("%s %v %s %x", "addr", trC.remoteAddr, pub.Type(), pub.Marshal()) - if want != checker.calls[0] { - t.Errorf("got %q want %q for host key check", checker.calls[0], want) + close(checker.called) + if _, ok := <-checker.called; ok { + // If all went well, we registered exactly 2 key changes: one + // that establishes the session, and one that we requested + // additionally. + t.Fatalf("got another host key checks after 2 handshakes") } } -func TestHandshakeError(t *testing.T) { +func TestForceFirstKex(t *testing.T) { + // like handshakePair, but must access the keyingTransport. checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "bad") + clientConf := &ClientConfig{HostKeyCallback: checker.Check} + a, b, err := netPipe() if err != nil { - t.Fatalf("handshakePair: %v", err) + t.Fatalf("netPipe: %v", err) } - defer trC.Close() - defer trS.Close() - // send a packet - packet := []byte{msgRequestSuccess, 42} - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } + var trC, trS keyingTransport - // Now request a key change. - err = trC.sendKexInit(subsequentKeyExchange) - if err != nil { - t.Errorf("sendKexInit: %v", err) - } + trC = newTransport(a, rand.Reader, true) - // the key change will fail, and afterwards we can't write. - if err := trC.writePacket([]byte{msgRequestSuccess, 43}); err == nil { - t.Errorf("writePacket after botched rekey succeeded.") - } + // This is the disallowed packet: + trC.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})) - readback, err := trS.readPacket() - if err != nil { - t.Fatalf("server closed too soon: %v", err) - } - if bytes.Compare(readback, packet) != 0 { - t.Errorf("got %q want %q", readback, packet) - } - readback, err = trS.readPacket() - if err == nil { - t.Errorf("got a message %q after failed key change", readback) - } -} + // Rest of the setup. + trS = newTransport(b, rand.Reader, false) + clientConf.SetDefaults() -func TestForceFirstKex(t *testing.T) { - checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") - if err != nil { - t.Fatalf("handshakePair: %v", err) - } + v := []byte("version") + client := newClientTransport(trC, v, v, clientConf, "addr", a.RemoteAddr()) - defer trC.Close() - defer trS.Close() + serverConf := &ServerConfig{} + serverConf.AddHostKey(testSigners["ecdsa"]) + serverConf.AddHostKey(testSigners["rsa"]) + serverConf.SetDefaults() + server := newServerTransport(trS, v, v, serverConf) - trC.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})) + defer client.Close() + defer server.Close() // We setup the initial key exchange, but the remote side // tries to send serviceRequestMsg in cleartext, which is // disallowed. - err = trS.sendKexInit(firstKeyExchange) - if err == nil { + if err := server.waitSession(); err == nil { t.Errorf("server first kex init should reject unexpected packet") } } -func TestHandshakeTwice(t *testing.T) { - checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") +func TestHandshakeAutoRekeyWrite(t *testing.T) { + checker := &syncChecker{make(chan int, 10)} + clientConf := &ClientConfig{HostKeyCallback: checker.Check} + clientConf.RekeyThreshold = 500 + trC, trS, err := handshakePair(clientConf, "addr", false) if err != nil { t.Fatalf("handshakePair: %v", err) } - defer trC.Close() defer trS.Close() - // Both sides should ask for the first key exchange first. - err = trS.sendKexInit(firstKeyExchange) - if err != nil { - t.Errorf("server sendKexInit: %v", err) - } - - err = trC.sendKexInit(firstKeyExchange) - if err != nil { - t.Errorf("client sendKexInit: %v", err) - } - - sent := 0 - // send a packet - packet := make([]byte, 5) - packet[0] = msgRequestSuccess - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - sent++ - - // Send another packet. Use a fresh one, since writePacket destroys. - packet = make([]byte, 5) - packet[0] = msgRequestSuccess - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - sent++ - - // 2nd key change. - err = trC.sendKexInit(subsequentKeyExchange) - if err != nil { - t.Errorf("sendKexInit: %v", err) - } - - packet = make([]byte, 5) - packet[0] = msgRequestSuccess - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - sent++ - - packet = make([]byte, 5) - packet[0] = msgRequestSuccess - for i := 0; i < sent; i++ { - msg, err := trS.readPacket() - if err != nil { - t.Fatalf("server closed too soon: %v", err) + done := make(chan int, 1) + const numPacket = 5 + go func() { + defer close(done) + j := 0 + for ; j < numPacket; j++ { + if _, err := trS.readPacket(); err != nil { + break + } } - if bytes.Compare(msg, packet) != 0 { - t.Errorf("packet %d: got %q want %q", i, msg, packet) + if j != numPacket { + t.Errorf("got %d, want 5 messages", j) } - } - if len(checker.calls) != 2 { - t.Errorf("got %d key changes, want 2", len(checker.calls)) - } -} + }() -func TestHandshakeAutoRekeyWrite(t *testing.T) { - checker := &testChecker{} - clientConf := &ClientConfig{HostKeyCallback: checker.Check} - clientConf.RekeyThreshold = 500 - trC, trS, err := handshakePair(clientConf, "addr") - if err != nil { - t.Fatalf("handshakePair: %v", err) - } - defer trC.Close() - defer trS.Close() + <-checker.called - for i := 0; i < 5; i++ { + for i := 0; i < numPacket; i++ { packet := make([]byte, 251) packet[0] = msgRequestSuccess if err := trC.writePacket(packet); err != nil { t.Errorf("writePacket: %v", err) } - } - - j := 0 - for ; j < 5; j++ { - _, err := trS.readPacket() - if err != nil { - break + if i == 2 { + // Make sure the kex is in progress. + <-checker.called } - } - if j != 5 { - t.Errorf("got %d, want 5 messages", j) - } - - if len(checker.calls) != 2 { - t.Errorf("got %d key changes, wanted 2", len(checker.calls)) } + <-done } type syncChecker struct { called chan int } -func (t *syncChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error { - t.called <- 1 +func (c *syncChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error { + c.called <- 1 return nil } @@ -326,7 +298,7 @@ func TestHandshakeAutoRekeyRead(t *testing.T) { } clientConf.RekeyThreshold = 500 - trC, trS, err := handshakePair(clientConf, "addr") + trC, trS, err := handshakePair(clientConf, "addr", false) if err != nil { t.Fatalf("handshakePair: %v", err) } @@ -338,12 +310,19 @@ func TestHandshakeAutoRekeyRead(t *testing.T) { if err := trS.writePacket(packet); err != nil { t.Fatalf("writePacket: %v", err) } + // While we read out the packet, a key change will be // initiated. - if _, err := trC.readPacket(); err != nil { - t.Fatalf("readPacket(client): %v", err) - } + done := make(chan int, 1) + go func() { + defer close(done) + if _, err := trC.readPacket(); err != nil { + t.Fatalf("readPacket(client): %v", err) + } + + }() + <-done <-sync.called } @@ -357,6 +336,7 @@ type errorKeyingTransport struct { func (n *errorKeyingTransport) prepareKeyChange(*algorithms, *kexResult) error { return nil } + func (n *errorKeyingTransport) getSessionID() []byte { return nil } @@ -383,20 +363,32 @@ func (n *errorKeyingTransport) readPacket() ([]byte, error) { func TestHandshakeErrorHandlingRead(t *testing.T) { for i := 0; i < 20; i++ { - testHandshakeErrorHandlingN(t, i, -1) + testHandshakeErrorHandlingN(t, i, -1, false) } } func TestHandshakeErrorHandlingWrite(t *testing.T) { for i := 0; i < 20; i++ { - testHandshakeErrorHandlingN(t, -1, i) + testHandshakeErrorHandlingN(t, -1, i, false) + } +} + +func TestHandshakeErrorHandlingReadCoupled(t *testing.T) { + for i := 0; i < 20; i++ { + testHandshakeErrorHandlingN(t, i, -1, true) + } +} + +func TestHandshakeErrorHandlingWriteCoupled(t *testing.T) { + for i := 0; i < 20; i++ { + testHandshakeErrorHandlingN(t, -1, i, true) } } // testHandshakeErrorHandlingN runs handshakes, injecting errors. If // handshakeTransport deadlocks, the go runtime will detect it and // panic. -func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int) { +func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int, coupled bool) { msg := Marshal(&serviceRequestMsg{strings.Repeat("x", int(minRekeyThreshold)/4)}) a, b := memPipe() @@ -409,37 +401,57 @@ func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int) { serverConn := newHandshakeTransport(&errorKeyingTransport{a, readLimit, writeLimit}, &serverConf, []byte{'a'}, []byte{'b'}) serverConn.hostKeys = []Signer{key} go serverConn.readLoop() + go serverConn.kexLoop() clientConf := Config{RekeyThreshold: 10 * minRekeyThreshold} clientConf.SetDefaults() clientConn := newHandshakeTransport(&errorKeyingTransport{b, -1, -1}, &clientConf, []byte{'a'}, []byte{'b'}) clientConn.hostKeyAlgorithms = []string{key.PublicKey().Type()} go clientConn.readLoop() + go clientConn.kexLoop() var wg sync.WaitGroup - wg.Add(4) for _, hs := range []packetConn{serverConn, clientConn} { - go func(c packetConn) { - for { - err := c.writePacket(msg) - if err != nil { - break + if !coupled { + wg.Add(2) + go func(c packetConn) { + for i := 0; ; i++ { + str := fmt.Sprintf("%08x", i) + strings.Repeat("x", int(minRekeyThreshold)/4-8) + err := c.writePacket(Marshal(&serviceRequestMsg{str})) + if err != nil { + break + } } - } - wg.Done() - }(hs) - go func(c packetConn) { - for { - _, err := c.readPacket() - if err != nil { - break + wg.Done() + c.Close() + }(hs) + go func(c packetConn) { + for { + _, err := c.readPacket() + if err != nil { + break + } } - } - wg.Done() - }(hs) - } + wg.Done() + }(hs) + } else { + wg.Add(1) + go func(c packetConn) { + for { + _, err := c.readPacket() + if err != nil { + break + } + if err := c.writePacket(msg); err != nil { + break + } + } + wg.Done() + }(hs) + } + } wg.Wait() } @@ -448,7 +460,7 @@ func TestDisconnect(t *testing.T) { t.Skip("see golang.org/issue/7237") } checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") + trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr", false) if err != nil { t.Fatalf("handshakePair: %v", err) } diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index f2fc9b6c9..f38de9898 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -10,10 +10,13 @@ import ( "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" + "crypto/md5" "crypto/rsa" + "crypto/sha256" "crypto/x509" "encoding/asn1" "encoding/base64" + "encoding/hex" "encoding/pem" "errors" "fmt" @@ -795,8 +798,8 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { P *big.Int Q *big.Int G *big.Int - Priv *big.Int Pub *big.Int + Priv *big.Int } rest, err := asn1.Unmarshal(der, &k) if err != nil { @@ -813,9 +816,9 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { Q: k.Q, G: k.G, }, - Y: k.Priv, + Y: k.Pub, }, - X: k.Pub, + X: k.Priv, }, nil } @@ -878,3 +881,25 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { copy(pk, pk1.Priv) return &pk, nil } + +// FingerprintLegacyMD5 returns the user presentation of the key's +// fingerprint as described by RFC 4716 section 4. +func FingerprintLegacyMD5(pubKey PublicKey) string { + md5sum := md5.Sum(pubKey.Marshal()) + hexarray := make([]string, len(md5sum)) + for i, c := range md5sum { + hexarray[i] = hex.EncodeToString([]byte{c}) + } + return strings.Join(hexarray, ":") +} + +// FingerprintSHA256 returns the user presentation of the key's +// fingerprint as unpadded base64 encoded sha256 hash. +// This format was introduced from OpenSSH 6.8. +// https://www.openssh.com/txt/release-6.8 +// https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding) +func FingerprintSHA256(pubKey PublicKey) string { + sha256sum := sha256.Sum256(pubKey.Marshal()) + hash := base64.RawStdEncoding.EncodeToString(sha256sum[:]) + return "SHA256:" + hash +} diff --git a/vendor/golang.org/x/crypto/ssh/keys_test.go b/vendor/golang.org/x/crypto/ssh/keys_test.go index 0739c6627..a65e87e53 100644 --- a/vendor/golang.org/x/crypto/ssh/keys_test.go +++ b/vendor/golang.org/x/crypto/ssh/keys_test.go @@ -454,3 +454,21 @@ func TestKnownHostsParsing(t *testing.T) { } } } + +func TestFingerprintLegacyMD5(t *testing.T) { + pub, _ := getTestKey() + fingerprint := FingerprintLegacyMD5(pub) + want := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // ssh-keygen -lf -E md5 rsa + if fingerprint != want { + t.Errorf("got fingerprint %q want %q", fingerprint, want) + } +} + +func TestFingerprintSHA256(t *testing.T) { + pub, _ := getTestKey() + fingerprint := FingerprintSHA256(pub) + want := "SHA256:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // ssh-keygen -lf rsa + if fingerprint != want { + t.Errorf("got fingerprint %q want %q", fingerprint, want) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/mux.go b/vendor/golang.org/x/crypto/ssh/mux.go index f3a3ddd78..27a527c10 100644 --- a/vendor/golang.org/x/crypto/ssh/mux.go +++ b/vendor/golang.org/x/crypto/ssh/mux.go @@ -116,9 +116,9 @@ func (m *mux) Wait() error { func newMux(p packetConn) *mux { m := &mux{ conn: p, - incomingChannels: make(chan NewChannel, 16), + incomingChannels: make(chan NewChannel, chanSize), globalResponses: make(chan interface{}, 1), - incomingRequests: make(chan *Request, 16), + incomingRequests: make(chan *Request, chanSize), errCond: newCond(), } if debugMux { diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 37df1b302..28b109a9c 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -188,7 +188,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) - if err := s.transport.requestInitialKeyChange(); err != nil { + if err := s.transport.waitSession(); err != nil { return nil, err } @@ -242,7 +242,7 @@ func checkSourceAddress(addr net.Addr, sourceAddr string) error { } if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { - if bytes.Equal(allowedIP, tcpAddr.IP) { + if allowedIP.Equal(tcpAddr.IP) { return nil } } else { @@ -260,7 +260,7 @@ func checkSourceAddress(addr net.Addr, sourceAddr string) error { } func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { - var err error + sessionID := s.transport.getSessionID() var cache pubKeyCache var perms *Permissions @@ -385,7 +385,7 @@ userAuthLoop: if !isAcceptableAlgo(sig.Format) { break } - signedData := buildDataSignedForAuth(s.transport.getSessionID(), userAuthReq, algoBytes, pubKeyData) + signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData) if err := pubKey.Verify(signedData, sig); err != nil { return nil, err @@ -421,12 +421,12 @@ userAuthLoop: return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") } - if err = s.transport.writePacket(Marshal(&failureMsg)); err != nil { + if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil { return nil, err } } - if err = s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { + if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { return nil, err } return perms, nil diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go index 741eeb13f..18379a935 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go @@ -132,8 +132,11 @@ const ( keyPasteEnd ) -var pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} -var pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +var ( + crlf = []byte{'\r', '\n'} + pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} + pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +) // bytesToKey tries to parse a key sequence from b. If successful, it returns // the key and the remainder of the input. Otherwise it returns utf8.RuneError. @@ -333,7 +336,7 @@ func (t *Terminal) advanceCursor(places int) { // So, if we are stopping at the end of a line, we // need to write a newline so that our cursor can be // advanced to the next line. - t.outBuf = append(t.outBuf, '\n') + t.outBuf = append(t.outBuf, '\r', '\n') } } @@ -593,6 +596,35 @@ func (t *Terminal) writeLine(line []rune) { } } +// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n. +func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { + for len(buf) > 0 { + i := bytes.IndexByte(buf, '\n') + todo := len(buf) + if i >= 0 { + todo = i + } + + var nn int + nn, err = w.Write(buf[:todo]) + n += nn + if err != nil { + return n, err + } + buf = buf[todo:] + + if i >= 0 { + if _, err = w.Write(crlf); err != nil { + return n, err + } + n += 1 + buf = buf[1:] + } + } + + return n, nil +} + func (t *Terminal) Write(buf []byte) (n int, err error) { t.lock.Lock() defer t.lock.Unlock() @@ -600,7 +632,7 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { if t.cursorX == 0 && t.cursorY == 0 { // This is the easy case: there's nothing on the screen that we // have to move out of the way. - return t.c.Write(buf) + return writeWithCRLF(t.c, buf) } // We have a prompt and possibly user input on the screen. We @@ -620,7 +652,7 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { } t.outBuf = t.outBuf[:0] - if n, err = t.c.Write(buf); err != nil { + if n, err = writeWithCRLF(t.c, buf); err != nil { return } @@ -740,8 +772,6 @@ func (t *Terminal) readLine() (line string, err error) { t.remainder = t.inBuf[:n+len(t.remainder)] } - - panic("unreachable") // for Go 1.0. } // SetPrompt sets the prompt to be used when reading subsequent lines. @@ -890,3 +920,32 @@ func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { } return s.entries[index], true } + +// readPasswordLine reads from reader until it finds \n or io.EOF. +// The slice returned does not include the \n. +// readPasswordLine also ignores any \r it finds. +func readPasswordLine(reader io.Reader) ([]byte, error) { + var buf [1]byte + var ret []byte + + for { + n, err := reader.Read(buf[:]) + if n > 0 { + switch buf[0] { + case '\n': + return ret, nil + case '\r': + // remove \r from passwords on Windows + default: + ret = append(ret, buf[0]) + } + continue + } + if err != nil { + if err == io.EOF && len(ret) > 0 { + return ret, nil + } + return ret, err + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go index 6bdefb4ec..901c72ab3 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go @@ -5,6 +5,7 @@ package terminal import ( + "bytes" "io" "os" "testing" @@ -269,6 +270,50 @@ func TestTerminalSetSize(t *testing.T) { } } +func TestReadPasswordLineEnd(t *testing.T) { + var tests = []struct { + input string + want string + }{ + {"\n", ""}, + {"\r\n", ""}, + {"test\r\n", "test"}, + {"testtesttesttes\n", "testtesttesttes"}, + {"testtesttesttes\r\n", "testtesttesttes"}, + {"testtesttesttesttest\n", "testtesttesttesttest"}, + {"testtesttesttesttest\r\n", "testtesttesttesttest"}, + } + for _, test := range tests { + buf := new(bytes.Buffer) + if _, err := buf.WriteString(test.input); err != nil { + t.Fatal(err) + } + + have, err := readPasswordLine(buf) + if err != nil { + t.Errorf("readPasswordLine(%q) failed: %v", test.input, err) + continue + } + if string(have) != test.want { + t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want) + continue + } + + if _, err = buf.WriteString(test.input); err != nil { + t.Fatal(err) + } + have, err = readPasswordLine(buf) + if err != nil { + t.Errorf("readPasswordLine(%q) failed: %v", test.input, err) + continue + } + if string(have) != test.want { + t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want) + continue + } + } +} + func TestMakeRawState(t *testing.T) { fd := int(os.Stdout.Fd()) if !IsTerminal(fd) { @@ -289,3 +334,17 @@ func TestMakeRawState(t *testing.T) { t.Errorf("states do not match; was %v, expected %v", raw, st) } } + +func TestOutputNewlines(t *testing.T) { + // \n should be changed to \r\n in terminal output. + buf := new(bytes.Buffer) + term := NewTerminal(buf, ">") + + term.Write([]byte("1\n2\n")) + output := string(buf.Bytes()) + const expected = "1\r\n2\r\n" + + if output != expected { + t.Errorf("incorrect output: was %q, expected %q", output, expected) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go index c869213ec..d01919614 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go @@ -17,7 +17,6 @@ package terminal // import "golang.org/x/crypto/ssh/terminal" import ( - "io" "syscall" "unsafe" ) @@ -72,8 +71,10 @@ func GetState(fd int) (*State, error) { // Restore restores the terminal connected to the given file descriptor to a // previous state. func Restore(fd int, state *State) error { - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) - return err + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0); err != 0 { + return err + } + return nil } // GetSize returns the dimensions of the given terminal. @@ -86,6 +87,13 @@ func GetSize(fd int) (width, height int, err error) { return int(dimensions[1]), int(dimensions[0]), nil } +// passwordReader is an io.Reader that reads from a specific file descriptor. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return syscall.Read(int(r), buf) +} + // ReadPassword reads a line of input from a terminal without local echo. This // is commonly used for inputting passwords and other sensitive data. The slice // returned does not include the \n. @@ -107,27 +115,5 @@ func ReadPassword(fd int) ([]byte, error) { syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) }() - var buf [16]byte - var ret []byte - for { - n, err := syscall.Read(fd, buf[:]) - if err != nil { - return nil, err - } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF - } - break - } - if buf[n-1] == '\n' { - n-- - } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break - } - } - - return ret, nil + return readPasswordLine(passwordReader(fd)) } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go index ae9fa9ec1..e0a1f36ce 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go @@ -17,7 +17,6 @@ package terminal import ( - "io" "syscall" "unsafe" ) @@ -123,6 +122,13 @@ func GetSize(fd int) (width, height int, err error) { return int(info.size.x), int(info.size.y), nil } +// passwordReader is an io.Reader that reads from a specific Windows HANDLE. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return syscall.Read(syscall.Handle(r), buf) +} + // ReadPassword reads a line of input from a terminal without local echo. This // is commonly used for inputting passwords and other sensitive data. The slice // returned does not include the \n. @@ -145,30 +151,5 @@ func ReadPassword(fd int) ([]byte, error) { syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) }() - var buf [16]byte - var ret []byte - for { - n, err := syscall.Read(syscall.Handle(fd), buf[:]) - if err != nil { - return nil, err - } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF - } - break - } - if buf[n-1] == '\n' { - n-- - } - if n > 0 && buf[n-1] == '\r' { - n-- - } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break - } - } - - return ret, nil + return readPasswordLine(passwordReader(fd)) } diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go index 62fba629e..fd199324d 100644 --- a/vendor/golang.org/x/crypto/ssh/transport.go +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -22,7 +22,9 @@ type packetConn interface { // Encrypt and send a packet of data to the remote peer. writePacket(packet []byte) error - // Read a packet from the connection + // Read a packet from the connection. The read is blocking, + // i.e. if error is nil, then the returned byte slice is + // always non-empty. readPacket() ([]byte, error) // Close closes the write-side of the connection. @@ -85,8 +87,18 @@ func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) err } // Read and decrypt next packet. -func (t *transport) readPacket() ([]byte, error) { - return t.reader.readPacket(t.bufReader) +func (t *transport) readPacket() (p []byte, err error) { + for { + p, err = t.reader.readPacket(t.bufReader) + if err != nil { + break + } + if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { + break + } + } + + return p, err } func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { |