vendor: update golang.org/x/crypto/ssh

Related to #4160
master
Unknwon 2017-03-01 21:33:58 -05:00
parent 038b107c3d
commit 600f748cb0
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
14 changed files with 462 additions and 185 deletions

View File

@ -16,7 +16,7 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const APP_VER = "0.10.3.0228" const APP_VER = "0.10.3.0301"
func init() { func init() {
setting.AppVer = APP_VER setting.AppVer = APP_VER

View File

@ -1 +1 @@
0.10.3.0228 0.10.3.0301

View File

@ -461,8 +461,8 @@ func (m *mux) newChannel(chanType string, direction channelDirection, extraData
pending: newBuffer(), pending: newBuffer(),
extPending: newBuffer(), extPending: newBuffer(),
direction: direction, direction: direction,
incomingRequests: make(chan *Request, 16), incomingRequests: make(chan *Request, chanSize),
msg: make(chan interface{}, 16), msg: make(chan interface{}, chanSize),
chanType: chanType, chanType: chanType,
extraData: extraData, extraData: extraData,
mux: m, mux: m,

View File

@ -135,6 +135,7 @@ const prefixLen = 5
type streamPacketCipher struct { type streamPacketCipher struct {
mac hash.Hash mac hash.Hash
cipher cipher.Stream cipher cipher.Stream
etm bool
// The following members are to avoid per-packet allocations. // The following members are to avoid per-packet allocations.
prefix [prefixLen]byte prefix [prefixLen]byte
@ -150,7 +151,14 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
return nil, err return nil, err
} }
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) var encryptedPaddingLength [1]byte
if s.mac != nil && s.etm {
copy(encryptedPaddingLength[:], s.prefix[4:5])
s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
} else {
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
}
length := binary.BigEndian.Uint32(s.prefix[0:4]) length := binary.BigEndian.Uint32(s.prefix[0:4])
paddingLength := uint32(s.prefix[4]) paddingLength := uint32(s.prefix[4])
@ -159,7 +167,12 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
s.mac.Reset() s.mac.Reset()
binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
s.mac.Write(s.seqNumBytes[:]) s.mac.Write(s.seqNumBytes[:])
s.mac.Write(s.prefix[:]) if s.etm {
s.mac.Write(s.prefix[:4])
s.mac.Write(encryptedPaddingLength[:])
} else {
s.mac.Write(s.prefix[:])
}
macSize = uint32(s.mac.Size()) macSize = uint32(s.mac.Size())
} }
@ -184,10 +197,17 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
} }
mac := s.packetData[length-1:] mac := s.packetData[length-1:]
data := s.packetData[:length-1] data := s.packetData[:length-1]
if s.mac != nil && s.etm {
s.mac.Write(data)
}
s.cipher.XORKeyStream(data, data) s.cipher.XORKeyStream(data, data)
if s.mac != nil { if s.mac != nil {
s.mac.Write(data) if !s.etm {
s.mac.Write(data)
}
s.macResult = s.mac.Sum(s.macResult[:0]) s.macResult = s.mac.Sum(s.macResult[:0])
if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { if subtle.ConstantTimeCompare(s.macResult, mac) != 1 {
return nil, errors.New("ssh: MAC failure") return nil, errors.New("ssh: MAC failure")
@ -203,7 +223,13 @@ func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Rea
return errors.New("ssh: packet too large") return errors.New("ssh: packet too large")
} }
paddingLength := packetSizeMultiple - (prefixLen+len(packet))%packetSizeMultiple aadlen := 0
if s.mac != nil && s.etm {
// packet length is not encrypted for EtM modes
aadlen = 4
}
paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple
if paddingLength < 4 { if paddingLength < 4 {
paddingLength += packetSizeMultiple paddingLength += packetSizeMultiple
} }
@ -220,15 +246,37 @@ func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Rea
s.mac.Reset() s.mac.Reset()
binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
s.mac.Write(s.seqNumBytes[:]) s.mac.Write(s.seqNumBytes[:])
if s.etm {
// For EtM algorithms, the packet length must stay unencrypted,
// but the following data (padding length) must be encrypted
s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
}
s.mac.Write(s.prefix[:]) s.mac.Write(s.prefix[:])
if !s.etm {
// For non-EtM algorithms, the algorithm is applied on unencrypted data
s.mac.Write(packet)
s.mac.Write(padding)
}
}
if !(s.mac != nil && s.etm) {
// For EtM algorithms, the padding length has already been encrypted
// and the packet length must remain unencrypted
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
}
s.cipher.XORKeyStream(packet, packet)
s.cipher.XORKeyStream(padding, padding)
if s.mac != nil && s.etm {
// For EtM algorithms, packet and padding must be encrypted
s.mac.Write(packet) s.mac.Write(packet)
s.mac.Write(padding) s.mac.Write(padding)
} }
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
s.cipher.XORKeyStream(packet, packet)
s.cipher.XORKeyStream(padding, padding)
if _, err := w.Write(s.prefix[:]); err != nil { if _, err := w.Write(s.prefix[:]); err != nil {
return err return err
} }

View File

@ -40,7 +40,7 @@ func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel {
return nil return nil
} }
ch = make(chan NewChannel, 16) ch = make(chan NewChannel, chanSize)
c.channelHandlers[channelType] = ch c.channelHandlers[channelType] = ch
return ch return ch
} }
@ -97,13 +97,11 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
c.transport = newClientTransport( c.transport = newClientTransport(
newTransport(c.sshConn.conn, config.Rand, true /* is client */), newTransport(c.sshConn.conn, config.Rand, true /* is client */),
c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) 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 return err
} }
// We just did the key change, so the session ID is established.
c.sessionID = c.transport.getSessionID() c.sessionID = c.transport.getSessionID()
return c.clientAuthenticate(config) return c.clientAuthenticate(config)
} }

View File

@ -30,8 +30,10 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
// then any untried methods suggested by the server. // then any untried methods suggested by the server.
tried := make(map[string]bool) tried := make(map[string]bool)
var lastMethods []string var lastMethods []string
sessionID := c.transport.getSessionID()
for auth := AuthMethod(new(noneAuth)); auth != nil; { 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 { if err != nil {
return err return err
} }

View File

@ -56,7 +56,7 @@ var supportedHostKeyAlgos = []string{
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
// because they have reached the end of their useful life. // because they have reached the end of their useful life.
var supportedMACs = []string{ var supportedMACs = []string{
"hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96",
} }
var supportedCompressions = []string{compressionNone} var supportedCompressions = []string{compressionNone}
@ -104,6 +104,21 @@ type directionAlgorithms struct {
Compression string 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 { type algorithms struct {
kex string kex string
hostKey string hostKey string

View File

@ -19,6 +19,11 @@ import (
// messages are wrong when using ECDH. // messages are wrong when using ECDH.
const debugHandshake = false 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 // keyingTransport is a packet based transport that supports key
// changes. It need not be thread-safe. It should pass through // changes. It need not be thread-safe. It should pass through
// msgNewKeys in both directions. // msgNewKeys in both directions.
@ -53,34 +58,58 @@ type handshakeTransport struct {
incoming chan []byte incoming chan []byte
readError error 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.
requestKex chan struct{}
// 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 // data for host key checking
hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
dialAddress string dialAddress string
remoteAddr net.Addr remoteAddr net.Addr
readSinceKex uint64 // Algorithms agreed in the last key exchange.
algorithms *algorithms
// Protects the writing side of the connection readPacketsLeft uint32
mu sync.Mutex readBytesLeft int64
cond *sync.Cond
sentInitPacket []byte writePacketsLeft uint32
sentInitMsg *kexInitMsg writeBytesLeft int64
writtenSinceKex uint64
writeError error
// The session ID or nil if first kex did not complete yet. // The session ID or nil if first kex did not complete yet.
sessionID []byte sessionID []byte
} }
type pendingKex struct {
otherInit []byte
done chan error
}
func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport {
t := &handshakeTransport{ t := &handshakeTransport{
conn: conn, conn: conn,
serverVersion: serverVersion, serverVersion: serverVersion,
clientVersion: clientVersion, clientVersion: clientVersion,
incoming: make(chan []byte, 16), incoming: make(chan []byte, chanSize),
config: config, requestKex: make(chan struct{}, 1),
startKex: make(chan *pendingKex, 1),
config: config,
} }
t.cond = sync.NewCond(&t.mu)
// We always start with a mandatory key exchange.
t.requestKex <- struct{}{}
return t return t
} }
@ -95,6 +124,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
t.hostKeyAlgorithms = supportedHostKeyAlgos t.hostKeyAlgorithms = supportedHostKeyAlgos
} }
go t.readLoop() go t.readLoop()
go t.kexLoop()
return t return t
} }
@ -102,6 +132,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt
t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
t.hostKeys = config.hostKeys t.hostKeys = config.hostKeys
go t.readLoop() go t.readLoop()
go t.kexLoop()
return t return t
} }
@ -109,6 +140,20 @@ func (t *handshakeTransport) getSessionID() []byte {
return t.sessionID 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 { func (t *handshakeTransport) id() string {
if len(t.hostKeys) > 0 { if len(t.hostKeys) > 0 {
return "server" return "server"
@ -116,6 +161,20 @@ func (t *handshakeTransport) id() string {
return "client" 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) { func (t *handshakeTransport) readPacket() ([]byte, error) {
p, ok := <-t.incoming p, ok := <-t.incoming
if !ok { if !ok {
@ -125,8 +184,10 @@ func (t *handshakeTransport) readPacket() ([]byte, error) {
} }
func (t *handshakeTransport) readLoop() { func (t *handshakeTransport) readLoop() {
first := true
for { for {
p, err := t.readOnePacket() p, err := t.readOnePacket(first)
first = false
if err != nil { if err != nil {
t.readError = err t.readError = err
close(t.incoming) close(t.incoming)
@ -138,67 +199,204 @@ func (t *handshakeTransport) readLoop() {
t.incoming <- p t.incoming <- p
} }
// If we can't read, declare the writing part dead too. // Stop writers too.
t.mu.Lock() t.recordWriteError(t.readError)
defer t.mu.Unlock()
if t.writeError == nil { // Unblock the writer should it wait for this.
t.writeError = t.readError close(t.startKex)
}
t.cond.Broadcast() // Don't close t.requestKex; it's also written to from writePacket.
} }
func (t *handshakeTransport) readOnePacket() ([]byte, error) { func (t *handshakeTransport) pushPacket(p []byte) error {
if t.readSinceKex > t.config.RekeyThreshold { if debugHandshake {
if err := t.requestKeyChange(); err != nil { t.printPacket(p, true)
return nil, err }
return t.conn.writePacket(p)
}
func (t *handshakeTransport) getWriteError() error {
t.mu.Lock()
defer t.mu.Unlock()
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
}
}
func (t *handshakeTransport) requestKeyExchange() {
select {
case t.requestKex <- struct{}{}:
default:
// something already requested a kex, so do nothing.
}
}
func (t *handshakeTransport) kexLoop() {
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 <-t.requestKex:
break
}
if !sent {
if err := t.sendKexInit(); err != nil {
t.recordWriteError(err)
break
}
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, until we close the done
// channel on the pendingKex 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()
}
// we have completed the key exchange. Since the
// reader is still blocked, it is safe to clear out
// the requestKex channel. This avoids the situation
// where: 1) we consumed our own request for the
// initial kex, and 2) the kex from the remote side
// caused another send on the requestKex channel,
clear:
for {
select {
case <-t.requestKex:
//
default:
break clear
}
}
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() p, err := t.conn.readPacket()
if err != nil { if err != nil {
return nil, err return nil, err
} }
t.readSinceKex += uint64(len(p)) if t.readPacketsLeft > 0 {
if debugHandshake { t.readPacketsLeft--
if p[0] == msgChannelData || p[0] == msgChannelExtendedData { } else {
log.Printf("%s got data (packet %d bytes)", t.id(), len(p)) t.requestKeyExchange()
} else {
msg, err := decode(p)
log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
}
} }
if t.readBytesLeft > 0 {
t.readBytesLeft -= int64(len(p))
} else {
t.requestKeyExchange()
}
if debugHandshake {
t.printPacket(p, false)
}
if first && p[0] != msgKexInit {
return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
}
if p[0] != msgKexInit { if p[0] != msgKexInit {
return p, nil return p, nil
} }
t.mu.Lock()
firstKex := t.sessionID == nil firstKex := t.sessionID == nil
err = t.enterKeyExchangeLocked(p) kex := pendingKex{
if err != nil { done: make(chan error, 1),
// drop connection otherInit: p,
t.conn.Close()
t.writeError = err
} }
t.startKex <- &kex
err = <-kex.done
if debugHandshake { if debugHandshake {
log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err) 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 { if err != nil {
return nil, err 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 // By default, a key exchange is hidden from higher layers by
// translating it into msgIgnore. // translating it into msgIgnore.
@ -213,61 +411,16 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
return successPacket, nil return successPacket, nil
} }
// keyChangeCategory describes whether a key exchange is the first on a // sendKexInit sends a key change message.
// connection, or a subsequent one. func (t *handshakeTransport) sendKexInit() error {
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
t.mu.Lock() t.mu.Lock()
// If this is the initial key change, but we already have a sessionID, defer t.mu.Unlock()
// 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.
if t.sentInitMsg != nil { 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{ msg := &kexInitMsg{
@ -295,53 +448,65 @@ func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexI
packetCopy := make([]byte, len(packet)) packetCopy := make([]byte, len(packet))
copy(packetCopy, packet) copy(packetCopy, packet)
if err := t.conn.writePacket(packetCopy); err != nil { if err := t.pushPacket(packetCopy); err != nil {
return nil, nil, err return err
} }
t.sentInitMsg = msg t.sentInitMsg = msg
t.sentInitPacket = packet t.sentInitPacket = packet
return msg, packet, nil
return nil
} }
func (t *handshakeTransport) writePacket(p []byte) error { func (t *handshakeTransport) writePacket(p []byte) error {
t.mu.Lock()
defer t.mu.Unlock()
if t.writtenSinceKex > t.config.RekeyThreshold {
t.sendKexInitLocked(subsequentKeyExchange)
}
for t.sentInitMsg != nil && t.writeError == nil {
t.cond.Wait()
}
if t.writeError != nil {
return t.writeError
}
t.writtenSinceKex += uint64(len(p))
switch p[0] { switch p[0] {
case msgKexInit: case msgKexInit:
return errors.New("ssh: only handshakeTransport can send kexInit") return errors.New("ssh: only handshakeTransport can send kexInit")
case msgNewKeys: case msgNewKeys:
return errors.New("ssh: only handshakeTransport can send newKeys") return errors.New("ssh: only handshakeTransport can send newKeys")
default:
return t.conn.writePacket(p)
} }
t.mu.Lock()
defer t.mu.Unlock()
if t.writeError != nil {
return t.writeError
}
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
}
if t.writeBytesLeft > 0 {
t.writeBytesLeft -= int64(len(p))
} else {
t.requestKeyExchange()
}
if t.writePacketsLeft > 0 {
t.writePacketsLeft--
} else {
t.requestKeyExchange()
}
if err := t.pushPacket(p); err != nil {
t.writeError = err
}
return nil
} }
func (t *handshakeTransport) Close() error { func (t *handshakeTransport) Close() error {
return t.conn.Close() return t.conn.Close()
} }
// enterKeyExchange runs the key exchange. t.mu must be held while running this. func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error {
if debugHandshake { if debugHandshake {
log.Printf("%s entered key exchange", t.id()) log.Printf("%s entered key exchange", t.id())
} }
myInit, myInitPacket, err := t.sendKexInitLocked(subsequentKeyExchange)
if err != nil {
return err
}
otherInit := &kexInitMsg{} otherInit := &kexInitMsg{}
if err := Unmarshal(otherInitPacket, otherInit); err != nil { if err := Unmarshal(otherInitPacket, otherInit); err != nil {
@ -352,20 +517,20 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
clientVersion: t.clientVersion, clientVersion: t.clientVersion,
serverVersion: t.serverVersion, serverVersion: t.serverVersion,
clientKexInit: otherInitPacket, clientKexInit: otherInitPacket,
serverKexInit: myInitPacket, serverKexInit: t.sentInitPacket,
} }
clientInit := otherInit clientInit := otherInit
serverInit := myInit serverInit := t.sentInitMsg
if len(t.hostKeys) == 0 { if len(t.hostKeys) == 0 {
clientInit = myInit clientInit, serverInit = serverInit, clientInit
serverInit = otherInit
magics.clientKexInit = myInitPacket magics.clientKexInit = t.sentInitPacket
magics.serverKexInit = otherInitPacket magics.serverKexInit = otherInitPacket
} }
algs, err := findAgreedAlgorithms(clientInit, serverInit) var err error
t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit)
if err != nil { if err != nil {
return err return err
} }
@ -388,16 +553,16 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
} }
} }
kex, ok := kexAlgoMap[algs.kex] kex, ok := kexAlgoMap[t.algorithms.kex]
if !ok { 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 var result *kexResult
if len(t.hostKeys) > 0 { if len(t.hostKeys) > 0 {
result, err = t.server(kex, algs, &magics) result, err = t.server(kex, t.algorithms, &magics)
} else { } else {
result, err = t.client(kex, algs, &magics) result, err = t.client(kex, t.algorithms, &magics)
} }
if err != nil { if err != nil {
@ -409,7 +574,7 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
} }
result.SessionID = t.sessionID result.SessionID = t.sessionID
t.conn.prepareKeyChange(algs, result) t.conn.prepareKeyChange(t.algorithms, result)
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
return err return err
} }

View File

@ -798,8 +798,8 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
P *big.Int P *big.Int
Q *big.Int Q *big.Int
G *big.Int G *big.Int
Priv *big.Int
Pub *big.Int Pub *big.Int
Priv *big.Int
} }
rest, err := asn1.Unmarshal(der, &k) rest, err := asn1.Unmarshal(der, &k)
if err != nil { if err != nil {
@ -816,9 +816,9 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
Q: k.Q, Q: k.Q,
G: k.G, G: k.G,
}, },
Y: k.Priv, Y: k.Pub,
}, },
X: k.Pub, X: k.Priv,
}, nil }, nil
} }

View File

@ -15,6 +15,7 @@ import (
type macMode struct { type macMode struct {
keySize int keySize int
etm bool
new func(key []byte) hash.Hash new func(key []byte) hash.Hash
} }
@ -45,13 +46,16 @@ func (t truncatingMAC) Size() int {
func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
var macModes = map[string]*macMode{ var macModes = map[string]*macMode{
"hmac-sha2-256": {32, func(key []byte) hash.Hash { "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key) return hmac.New(sha256.New, key)
}}, }},
"hmac-sha1": {20, func(key []byte) hash.Hash { "hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}},
"hmac-sha1": {20, false, func(key []byte) hash.Hash {
return hmac.New(sha1.New, key) return hmac.New(sha1.New, key)
}}, }},
"hmac-sha1-96": {20, func(key []byte) hash.Hash { "hmac-sha1-96": {20, false, func(key []byte) hash.Hash {
return truncatingMAC{12, hmac.New(sha1.New, key)} return truncatingMAC{12, hmac.New(sha1.New, key)}
}}, }},
} }

View File

@ -116,9 +116,9 @@ func (m *mux) Wait() error {
func newMux(p packetConn) *mux { func newMux(p packetConn) *mux {
m := &mux{ m := &mux{
conn: p, conn: p,
incomingChannels: make(chan NewChannel, 16), incomingChannels: make(chan NewChannel, chanSize),
globalResponses: make(chan interface{}, 1), globalResponses: make(chan interface{}, 1),
incomingRequests: make(chan *Request, 16), incomingRequests: make(chan *Request, chanSize),
errCond: newCond(), errCond: newCond(),
} }
if debugMux { if debugMux {

View File

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"strings"
) )
// The Permissions type holds fine-grained permissions that are // The Permissions type holds fine-grained permissions that are
@ -188,7 +189,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) 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 return nil, err
} }
@ -231,7 +232,7 @@ func isAcceptableAlgo(algo string) bool {
return false return false
} }
func checkSourceAddress(addr net.Addr, sourceAddr string) error { func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
if addr == nil { if addr == nil {
return errors.New("ssh: no address known for client, but source-address match required") return errors.New("ssh: no address known for client, but source-address match required")
} }
@ -241,18 +242,20 @@ func checkSourceAddress(addr net.Addr, sourceAddr string) error {
return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr) return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
} }
if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
if allowedIP.Equal(tcpAddr.IP) { if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
return nil if allowedIP.Equal(tcpAddr.IP) {
} return nil
} else { }
_, ipNet, err := net.ParseCIDR(sourceAddr) } else {
if err != nil { _, ipNet, err := net.ParseCIDR(sourceAddr)
return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err) if err != nil {
} return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
}
if ipNet.Contains(tcpAddr.IP) { if ipNet.Contains(tcpAddr.IP) {
return nil return nil
}
} }
} }
@ -260,7 +263,7 @@ func checkSourceAddress(addr net.Addr, sourceAddr string) error {
} }
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
var err error sessionID := s.transport.getSessionID()
var cache pubKeyCache var cache pubKeyCache
var perms *Permissions var perms *Permissions
@ -385,7 +388,7 @@ userAuthLoop:
if !isAcceptableAlgo(sig.Format) { if !isAcceptableAlgo(sig.Format) {
break break
} }
signedData := buildDataSignedForAuth(s.transport.getSessionID(), userAuthReq, algoBytes, pubKeyData) signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData)
if err := pubKey.Verify(signedData, sig); err != nil { if err := pubKey.Verify(signedData, sig); err != nil {
return nil, err return nil, err
@ -421,12 +424,12 @@ userAuthLoop:
return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") 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 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 nil, err
} }
return perms, nil return perms, nil

View File

@ -8,8 +8,13 @@ import (
"bufio" "bufio"
"errors" "errors"
"io" "io"
"log"
) )
// debugTransport if set, will print packet types as they go over the
// wire. No message decoding is done, to minimize the impact on timing.
const debugTransport = false
const ( const (
gcmCipherID = "aes128-gcm@openssh.com" gcmCipherID = "aes128-gcm@openssh.com"
aes128cbcID = "aes128-cbc" aes128cbcID = "aes128-cbc"
@ -22,7 +27,9 @@ type packetConn interface {
// Encrypt and send a packet of data to the remote peer. // Encrypt and send a packet of data to the remote peer.
writePacket(packet []byte) error 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) readPacket() ([]byte, error)
// Close closes the write-side of the connection. // Close closes the write-side of the connection.
@ -38,7 +45,7 @@ type transport struct {
bufReader *bufio.Reader bufReader *bufio.Reader
bufWriter *bufio.Writer bufWriter *bufio.Writer
rand io.Reader rand io.Reader
isClient bool
io.Closer io.Closer
} }
@ -84,9 +91,38 @@ func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) err
return nil return nil
} }
func (t *transport) printPacket(p []byte, write bool) {
if len(p) == 0 {
return
}
who := "server"
if t.isClient {
who = "client"
}
what := "read"
if write {
what = "write"
}
log.Println(what, who, p[0])
}
// Read and decrypt next packet. // Read and decrypt next packet.
func (t *transport) readPacket() ([]byte, error) { func (t *transport) readPacket() (p []byte, err error) {
return t.reader.readPacket(t.bufReader) for {
p, err = t.reader.readPacket(t.bufReader)
if err != nil {
break
}
if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
break
}
}
if debugTransport {
t.printPacket(p, false)
}
return p, err
} }
func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
@ -129,6 +165,9 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
} }
func (t *transport) writePacket(packet []byte) error { func (t *transport) writePacket(packet []byte) error {
if debugTransport {
t.printPacket(packet, true)
}
return t.writer.writePacket(t.bufWriter, t.rand, packet) return t.writer.writePacket(t.bufWriter, t.rand, packet)
} }
@ -169,6 +208,8 @@ func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transp
}, },
Closer: rwc, Closer: rwc,
} }
t.isClient = isClient
if isClient { if isClient {
t.reader.dir = serverKeys t.reader.dir = serverKeys
t.writer.dir = clientKeys t.writer.dir = clientKeys
@ -226,6 +267,7 @@ func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (pac
c := &streamPacketCipher{ c := &streamPacketCipher{
mac: macModes[algs.MAC].new(macKey), mac: macModes[algs.MAC].new(macKey),
etm: macModes[algs.MAC].etm,
} }
c.macResult = make([]byte, c.mac.Size()) c.macResult = make([]byte, c.mac.Size())

6
vendor/vendor.json vendored
View File

@ -375,10 +375,10 @@
"revisionTime": "2016-12-13T22:25:08Z" "revisionTime": "2016-12-13T22:25:08Z"
}, },
{ {
"checksumSHA1": "RnJfaFwKpC0h06J2MZ8UZX2eohc=", "checksumSHA1": "fsrFs762jlaILyqqQImS1GfvIvw=",
"path": "golang.org/x/crypto/ssh", "path": "golang.org/x/crypto/ssh",
"revision": "d8e61c69ab46ca38328da2f4995abaf93b252290", "revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
"revisionTime": "2016-12-13T22:25:08Z" "revisionTime": "2017-02-08T20:51:15Z"
}, },
{ {
"checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=", "checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",