1
0
mirror of https://github.com/golang/go synced 2024-11-25 08:57:58 -07:00

exp/ssh: fix length header leaking into channel data streams.

The payload of a data message is defined as an SSH string type,
which uses the first four bytes to encode its length. When channelData
and channelExtendedData were added I defined Payload as []byte to
be able to use it directly without a string to []byte conversion. This
resulted in the length data leaking into the payload data.

This CL fixes the bug, and restores agl's original fast path code.

Additionally, a bug whereby s.lock was not released if a packet arrived
for an invalid channel has been fixed.

Finally, as they were no longer used, I have removed
the channelData and channelExtedendData structs.

R=agl, rsc
CC=golang-dev
https://golang.org/cl/5330053
This commit is contained in:
Dave Cheney 2011-10-29 14:22:30 -04:00 committed by Adam Langley
parent 604e10c34d
commit 0f6b80c694
3 changed files with 131 additions and 114 deletions

View File

@ -258,17 +258,45 @@ func (c *ClientConn) openChan(typ string) (*clientChan, os.Error) {
// mainloop reads incoming messages and routes channel messages // mainloop reads incoming messages and routes channel messages
// to their respective ClientChans. // to their respective ClientChans.
func (c *ClientConn) mainLoop() { func (c *ClientConn) mainLoop() {
// TODO(dfc) signal the underlying close to all channels
defer c.Close()
for { for {
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
// TODO(dfc) signal the underlying close to all channels break
c.Close()
return
} }
// TODO(dfc) A note on blocking channel use. // TODO(dfc) A note on blocking channel use.
// The msg, win, data and dataExt channels of a clientChan can // The msg, win, data and dataExt channels of a clientChan can
// cause this loop to block indefinately if the consumer does // cause this loop to block indefinately if the consumer does
// not service them. // not service them.
switch packet[0] {
case msgChannelData:
if len(packet) < 9 {
// malformed data packet
break
}
peersId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4])
if length := int(packet[5])<<24 | int(packet[6])<<16 | int(packet[7])<<8 | int(packet[8]); length > 0 {
packet = packet[9:]
c.getChan(peersId).data <- packet[:length]
}
case msgChannelExtendedData:
if len(packet) < 13 {
// malformed data packet
break
}
peersId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4])
datatype := uint32(packet[5])<<24 | uint32(packet[6])<<16 | uint32(packet[7])<<8 | uint32(packet[8])
if length := int(packet[9])<<24 | int(packet[10])<<16 | int(packet[11])<<8 | int(packet[12]); length > 0 {
packet = packet[13:]
// RFC 4254 5.2 defines data_type_code 1 to be data destined
// for stderr on interactive sessions. Other data types are
// silently discarded.
if datatype == 1 {
c.getChan(peersId).dataExt <- packet[:length]
}
}
default:
switch msg := decode(packet).(type) { switch msg := decode(packet).(type) {
case *channelOpenMsg: case *channelOpenMsg:
c.getChan(msg.PeersId).msg <- msg c.getChan(msg.PeersId).msg <- msg
@ -292,20 +320,12 @@ func (c *ClientConn) mainLoop() {
c.getChan(msg.PeersId).msg <- msg c.getChan(msg.PeersId).msg <- msg
case *windowAdjustMsg: case *windowAdjustMsg:
c.getChan(msg.PeersId).win <- int(msg.AdditionalBytes) c.getChan(msg.PeersId).win <- int(msg.AdditionalBytes)
case *channelData:
c.getChan(msg.PeersId).data <- msg.Payload
case *channelExtendedData:
// RFC 4254 5.2 defines data_type_code 1 to be data destined
// for stderr on interactive sessions. Other data types are
// silently discarded.
if msg.Datatype == 1 {
c.getChan(msg.PeersId).dataExt <- msg.Payload
}
default: default:
fmt.Printf("mainLoop: unhandled %#v\n", msg) fmt.Printf("mainLoop: unhandled %#v\n", msg)
} }
} }
} }
}
// Dial connects to the given network address using net.Dial and // Dial connects to the given network address using net.Dial and
// then initiates a SSH handshake, returning the resulting client connection. // then initiates a SSH handshake, returning the resulting client connection.

View File

@ -144,19 +144,6 @@ type channelOpenFailureMsg struct {
Language string Language string
} }
// See RFC 4254, section 5.2.
type channelData struct {
PeersId uint32
Payload []byte `ssh:"rest"`
}
// See RFC 4254, section 5.2.
type channelExtendedData struct {
PeersId uint32
Datatype uint32
Payload []byte `ssh:"rest"`
}
type channelRequestMsg struct { type channelRequestMsg struct {
PeersId uint32 PeersId uint32
Request string Request string
@ -612,10 +599,6 @@ func decode(packet []byte) interface{} {
msg = new(channelOpenFailureMsg) msg = new(channelOpenFailureMsg)
case msgChannelWindowAdjust: case msgChannelWindowAdjust:
msg = new(windowAdjustMsg) msg = new(windowAdjustMsg)
case msgChannelData:
msg = new(channelData)
case msgChannelExtendedData:
msg = new(channelExtendedData)
case msgChannelEOF: case msgChannelEOF:
msg = new(channelEOFMsg) msg = new(channelEOFMsg)
case msgChannelClose: case msgChannelClose:

View File

@ -581,6 +581,25 @@ func (s *ServerConn) Accept() (Channel, os.Error) {
return nil, err return nil, err
} }
switch packet[0] {
case msgChannelData:
if len(packet) < 9 {
// malformed data packet
return nil, ParseError{msgChannelData}
}
peersId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4])
s.lock.Lock()
c, ok := s.channels[peersId]
if !ok {
s.lock.Unlock()
continue
}
if length := int(packet[5])<<24 | int(packet[6])<<16 | int(packet[7])<<8 | int(packet[8]); length > 0 {
packet = packet[9:]
c.handleData(packet[:length])
}
s.lock.Unlock()
default:
switch msg := decode(packet).(type) { switch msg := decode(packet).(type) {
case *channelOpenMsg: case *channelOpenMsg:
c := new(channel) c := new(channel)
@ -605,24 +624,17 @@ func (s *ServerConn) Accept() (Channel, os.Error) {
s.lock.Lock() s.lock.Lock()
c, ok := s.channels[msg.PeersId] c, ok := s.channels[msg.PeersId]
if !ok { if !ok {
s.lock.Unlock()
continue continue
} }
c.handlePacket(msg) c.handlePacket(msg)
s.lock.Unlock() s.lock.Unlock()
case *channelData:
s.lock.Lock()
c, ok := s.channels[msg.PeersId]
if !ok {
continue
}
c.handleData(msg.Payload)
s.lock.Unlock()
case *channelEOFMsg: case *channelEOFMsg:
s.lock.Lock() s.lock.Lock()
c, ok := s.channels[msg.PeersId] c, ok := s.channels[msg.PeersId]
if !ok { if !ok {
s.lock.Unlock()
continue continue
} }
c.handlePacket(msg) c.handlePacket(msg)
@ -632,6 +644,7 @@ func (s *ServerConn) Accept() (Channel, os.Error) {
s.lock.Lock() s.lock.Lock()
c, ok := s.channels[msg.PeersId] c, ok := s.channels[msg.PeersId]
if !ok { if !ok {
s.lock.Unlock()
continue continue
} }
c.handlePacket(msg) c.handlePacket(msg)
@ -652,6 +665,7 @@ func (s *ServerConn) Accept() (Channel, os.Error) {
// Unknown message. Ignore. // Unknown message. Ignore.
} }
} }
}
panic("unreachable") panic("unreachable")
} }