diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-02-01 19:26:59 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-02-01 19:26:59 +0000 |
commit | 86babf4bfda7c2c2e9be4abc20e4d8073e05e5be (patch) | |
tree | 7e7e6083ebe59999943a211a17f8ef6f07f17c2f /libgo/go/exp/ssh | |
parent | df9ff8bf53f716508a120d15cc144e628fd2e9b5 (diff) | |
download | ppe42-gcc-86babf4bfda7c2c2e9be4abc20e4d8073e05e5be.tar.gz ppe42-gcc-86babf4bfda7c2c2e9be4abc20e4d8073e05e5be.zip |
libgo: Update to weekly.2012-01-27.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@183810 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/exp/ssh')
-rw-r--r-- | libgo/go/exp/ssh/channel.go | 318 | ||||
-rw-r--r-- | libgo/go/exp/ssh/cipher.go | 88 | ||||
-rw-r--r-- | libgo/go/exp/ssh/cipher_test.go | 62 | ||||
-rw-r--r-- | libgo/go/exp/ssh/client.go | 505 | ||||
-rw-r--r-- | libgo/go/exp/ssh/client_auth.go | 316 | ||||
-rw-r--r-- | libgo/go/exp/ssh/client_auth_test.go | 257 | ||||
-rw-r--r-- | libgo/go/exp/ssh/client_func_test.go | 61 | ||||
-rw-r--r-- | libgo/go/exp/ssh/common.go | 239 | ||||
-rw-r--r-- | libgo/go/exp/ssh/common_test.go | 26 | ||||
-rw-r--r-- | libgo/go/exp/ssh/doc.go | 127 | ||||
-rw-r--r-- | libgo/go/exp/ssh/messages.go | 640 | ||||
-rw-r--r-- | libgo/go/exp/ssh/messages_test.go | 125 | ||||
-rw-r--r-- | libgo/go/exp/ssh/server.go | 676 | ||||
-rw-r--r-- | libgo/go/exp/ssh/server_terminal.go | 81 | ||||
-rw-r--r-- | libgo/go/exp/ssh/session.go | 494 | ||||
-rw-r--r-- | libgo/go/exp/ssh/session_test.go | 374 | ||||
-rw-r--r-- | libgo/go/exp/ssh/tcpip.go | 132 | ||||
-rw-r--r-- | libgo/go/exp/ssh/tcpip_func_test.go | 59 | ||||
-rw-r--r-- | libgo/go/exp/ssh/transport.go | 369 | ||||
-rw-r--r-- | libgo/go/exp/ssh/transport_test.go | 51 |
20 files changed, 0 insertions, 5000 deletions
diff --git a/libgo/go/exp/ssh/channel.go b/libgo/go/exp/ssh/channel.go deleted file mode 100644 index 9d75f37de74..00000000000 --- a/libgo/go/exp/ssh/channel.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "errors" - "io" - "sync" -) - -// A Channel is an ordered, reliable, duplex stream that is multiplexed over an -// SSH connection. -type Channel interface { - // Accept accepts the channel creation request. - Accept() error - // Reject rejects the channel creation request. After calling this, no - // other methods on the Channel may be called. If they are then the - // peer is likely to signal a protocol error and drop the connection. - Reject(reason RejectionReason, message string) error - - // Read may return a ChannelRequest as an error. - Read(data []byte) (int, error) - Write(data []byte) (int, error) - Close() error - - // AckRequest either sends an ack or nack to the channel request. - AckRequest(ok bool) error - - // ChannelType returns the type of the channel, as supplied by the - // client. - ChannelType() string - // ExtraData returns the arbitary payload for this channel, as supplied - // by the client. This data is specific to the channel type. - ExtraData() []byte -} - -// ChannelRequest represents a request sent on a channel, outside of the normal -// stream of bytes. It may result from calling Read on a Channel. -type ChannelRequest struct { - Request string - WantReply bool - Payload []byte -} - -func (c ChannelRequest) Error() string { - return "channel request received" -} - -// RejectionReason is an enumeration used when rejecting channel creation -// requests. See RFC 4254, section 5.1. -type RejectionReason int - -const ( - Prohibited RejectionReason = iota + 1 - ConnectionFailed - UnknownChannelType - ResourceShortage -) - -type channel struct { - // immutable once created - chanType string - extraData []byte - - theyClosed bool - theySentEOF bool - weClosed bool - dead bool - - serverConn *ServerConn - myId, theirId uint32 - myWindow, theirWindow uint32 - maxPacketSize uint32 - err error - - pendingRequests []ChannelRequest - pendingData []byte - head, length int - - // This lock is inferior to serverConn.lock - lock sync.Mutex - cond *sync.Cond -} - -func (c *channel) Accept() error { - c.serverConn.lock.Lock() - defer c.serverConn.lock.Unlock() - - if c.serverConn.err != nil { - return c.serverConn.err - } - - confirm := channelOpenConfirmMsg{ - PeersId: c.theirId, - MyId: c.myId, - MyWindow: c.myWindow, - MaxPacketSize: c.maxPacketSize, - } - return c.serverConn.writePacket(marshal(msgChannelOpenConfirm, confirm)) -} - -func (c *channel) Reject(reason RejectionReason, message string) error { - c.serverConn.lock.Lock() - defer c.serverConn.lock.Unlock() - - if c.serverConn.err != nil { - return c.serverConn.err - } - - reject := channelOpenFailureMsg{ - PeersId: c.theirId, - Reason: uint32(reason), - Message: message, - Language: "en", - } - return c.serverConn.writePacket(marshal(msgChannelOpenFailure, reject)) -} - -func (c *channel) handlePacket(packet interface{}) { - c.lock.Lock() - defer c.lock.Unlock() - - switch packet := packet.(type) { - case *channelRequestMsg: - req := ChannelRequest{ - Request: packet.Request, - WantReply: packet.WantReply, - Payload: packet.RequestSpecificData, - } - - c.pendingRequests = append(c.pendingRequests, req) - c.cond.Signal() - case *channelCloseMsg: - c.theyClosed = true - c.cond.Signal() - case *channelEOFMsg: - c.theySentEOF = true - c.cond.Signal() - default: - panic("unknown packet type") - } -} - -func (c *channel) handleData(data []byte) { - c.lock.Lock() - defer c.lock.Unlock() - - // The other side should never send us more than our window. - if len(data)+c.length > len(c.pendingData) { - // TODO(agl): we should tear down the channel with a protocol - // error. - return - } - - c.myWindow -= uint32(len(data)) - for i := 0; i < 2; i++ { - tail := c.head + c.length - if tail > len(c.pendingData) { - tail -= len(c.pendingData) - } - n := copy(c.pendingData[tail:], data) - data = data[n:] - c.length += n - } - - c.cond.Signal() -} - -func (c *channel) Read(data []byte) (n int, err error) { - c.lock.Lock() - defer c.lock.Unlock() - - if c.err != nil { - return 0, c.err - } - - if c.myWindow <= uint32(len(c.pendingData))/2 { - packet := marshal(msgChannelWindowAdjust, windowAdjustMsg{ - PeersId: c.theirId, - AdditionalBytes: uint32(len(c.pendingData)) - c.myWindow, - }) - if err := c.serverConn.writePacket(packet); err != nil { - return 0, err - } - } - - for { - if c.theySentEOF || c.theyClosed || c.dead { - return 0, io.EOF - } - - if len(c.pendingRequests) > 0 { - req := c.pendingRequests[0] - if len(c.pendingRequests) == 1 { - c.pendingRequests = nil - } else { - oldPendingRequests := c.pendingRequests - c.pendingRequests = make([]ChannelRequest, len(oldPendingRequests)-1) - copy(c.pendingRequests, oldPendingRequests[1:]) - } - - return 0, req - } - - if c.length > 0 { - tail := c.head + c.length - if tail > len(c.pendingData) { - tail -= len(c.pendingData) - } - n = copy(data, c.pendingData[c.head:tail]) - c.head += n - c.length -= n - if c.head == len(c.pendingData) { - c.head = 0 - } - return - } - - c.cond.Wait() - } - - panic("unreachable") -} - -func (c *channel) Write(data []byte) (n int, err error) { - for len(data) > 0 { - c.lock.Lock() - if c.dead || c.weClosed { - return 0, io.EOF - } - - if c.theirWindow == 0 { - c.cond.Wait() - continue - } - c.lock.Unlock() - - todo := data - if uint32(len(todo)) > c.theirWindow { - todo = todo[:c.theirWindow] - } - - packet := make([]byte, 1+4+4+len(todo)) - packet[0] = msgChannelData - packet[1] = byte(c.theirId >> 24) - packet[2] = byte(c.theirId >> 16) - packet[3] = byte(c.theirId >> 8) - packet[4] = byte(c.theirId) - packet[5] = byte(len(todo) >> 24) - packet[6] = byte(len(todo) >> 16) - packet[7] = byte(len(todo) >> 8) - packet[8] = byte(len(todo)) - copy(packet[9:], todo) - - c.serverConn.lock.Lock() - if err = c.serverConn.writePacket(packet); err != nil { - c.serverConn.lock.Unlock() - return - } - c.serverConn.lock.Unlock() - - n += len(todo) - data = data[len(todo):] - } - - return -} - -func (c *channel) Close() error { - c.serverConn.lock.Lock() - defer c.serverConn.lock.Unlock() - - if c.serverConn.err != nil { - return c.serverConn.err - } - - if c.weClosed { - return errors.New("ssh: channel already closed") - } - c.weClosed = true - - closeMsg := channelCloseMsg{ - PeersId: c.theirId, - } - return c.serverConn.writePacket(marshal(msgChannelClose, closeMsg)) -} - -func (c *channel) AckRequest(ok bool) error { - c.serverConn.lock.Lock() - defer c.serverConn.lock.Unlock() - - if c.serverConn.err != nil { - return c.serverConn.err - } - - if ok { - ack := channelRequestSuccessMsg{ - PeersId: c.theirId, - } - return c.serverConn.writePacket(marshal(msgChannelSuccess, ack)) - } else { - ack := channelRequestFailureMsg{ - PeersId: c.theirId, - } - return c.serverConn.writePacket(marshal(msgChannelFailure, ack)) - } - panic("unreachable") -} - -func (c *channel) ChannelType() string { - return c.chanType -} - -func (c *channel) ExtraData() []byte { - return c.extraData -} diff --git a/libgo/go/exp/ssh/cipher.go b/libgo/go/exp/ssh/cipher.go deleted file mode 100644 index d91929aa99a..00000000000 --- a/libgo/go/exp/ssh/cipher.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rc4" -) - -// streamDump is used to dump the initial keystream for stream ciphers. It is a -// a write-only buffer, and not intended for reading so do not require a mutex. -var streamDump [512]byte - -// noneCipher implements cipher.Stream and provides no encryption. It is used -// by the transport before the first key-exchange. -type noneCipher struct{} - -func (c noneCipher) XORKeyStream(dst, src []byte) { - copy(dst, src) -} - -func newAESCTR(key, iv []byte) (cipher.Stream, error) { - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - return cipher.NewCTR(c, iv), nil -} - -func newRC4(key, iv []byte) (cipher.Stream, error) { - return rc4.NewCipher(key) -} - -type cipherMode struct { - keySize int - ivSize int - skip int - createFn func(key, iv []byte) (cipher.Stream, error) -} - -func (c *cipherMode) createCipher(key, iv []byte) (cipher.Stream, error) { - if len(key) < c.keySize { - panic("ssh: key length too small for cipher") - } - if len(iv) < c.ivSize { - panic("ssh: iv too small for cipher") - } - - stream, err := c.createFn(key[:c.keySize], iv[:c.ivSize]) - if err != nil { - return nil, err - } - - for remainingToDump := c.skip; remainingToDump > 0; { - dumpThisTime := remainingToDump - if dumpThisTime > len(streamDump) { - dumpThisTime = len(streamDump) - } - stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) - remainingToDump -= dumpThisTime - } - - return stream, nil -} - -// Specifies a default set of ciphers and a preference order. This is based on -// OpenSSH's default client preference order, minus algorithms that are not -// implemented. -var DefaultCipherOrder = []string{ - "aes128-ctr", "aes192-ctr", "aes256-ctr", - "arcfour256", "arcfour128", -} - -var cipherModes = map[string]*cipherMode{ - // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms - // are defined in the order specified in the RFC. - "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, - "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, - "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, - - // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. - // They are defined in the order specified in the RFC. - "arcfour128": {16, 0, 1536, newRC4}, - "arcfour256": {32, 0, 1536, newRC4}, -} diff --git a/libgo/go/exp/ssh/cipher_test.go b/libgo/go/exp/ssh/cipher_test.go deleted file mode 100644 index ea27bd8a803..00000000000 --- a/libgo/go/exp/ssh/cipher_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "testing" -) - -// TestCipherReversal tests that each cipher factory produces ciphers that can -// encrypt and decrypt some data successfully. -func TestCipherReversal(t *testing.T) { - testData := []byte("abcdefghijklmnopqrstuvwxyz012345") - testKey := []byte("AbCdEfGhIjKlMnOpQrStUvWxYz012345") - testIv := []byte("sdflkjhsadflkjhasdflkjhsadfklhsa") - - cryptBuffer := make([]byte, 32) - - for name, cipherMode := range cipherModes { - encrypter, err := cipherMode.createCipher(testKey, testIv) - if err != nil { - t.Errorf("failed to create encrypter for %q: %s", name, err) - continue - } - decrypter, err := cipherMode.createCipher(testKey, testIv) - if err != nil { - t.Errorf("failed to create decrypter for %q: %s", name, err) - continue - } - - copy(cryptBuffer, testData) - - encrypter.XORKeyStream(cryptBuffer, cryptBuffer) - if name == "none" { - if !bytes.Equal(cryptBuffer, testData) { - t.Errorf("encryption made change with 'none' cipher") - continue - } - } else { - if bytes.Equal(cryptBuffer, testData) { - t.Errorf("encryption made no change with %q", name) - continue - } - } - - decrypter.XORKeyStream(cryptBuffer, cryptBuffer) - if !bytes.Equal(cryptBuffer, testData) { - t.Errorf("decrypted bytes not equal to input with %q", name) - continue - } - } -} - -func TestDefaultCiphersExist(t *testing.T) { - for _, cipherAlgo := range DefaultCipherOrder { - if _, ok := cipherModes[cipherAlgo]; !ok { - t.Errorf("default cipher %q is unknown", cipherAlgo) - } - } -} diff --git a/libgo/go/exp/ssh/client.go b/libgo/go/exp/ssh/client.go deleted file mode 100644 index eb6c0352210..00000000000 --- a/libgo/go/exp/ssh/client.go +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "crypto" - "crypto/rand" - "errors" - "fmt" - "io" - "math/big" - "net" - "sync" -) - -// clientVersion is the fixed identification string that the client will use. -var clientVersion = []byte("SSH-2.0-Go\r\n") - -// ClientConn represents the client side of an SSH connection. -type ClientConn struct { - *transport - config *ClientConfig - chanlist -} - -// Client returns a new SSH client connection using c as the underlying transport. -func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) { - conn := &ClientConn{ - transport: newTransport(c, config.rand()), - config: config, - } - if err := conn.handshake(); err != nil { - conn.Close() - return nil, err - } - go conn.mainLoop() - return conn, nil -} - -// handshake performs the client side key exchange. See RFC 4253 Section 7. -func (c *ClientConn) handshake() error { - var magics handshakeMagics - - if _, err := c.Write(clientVersion); err != nil { - return err - } - if err := c.Flush(); err != nil { - return err - } - magics.clientVersion = clientVersion[:len(clientVersion)-2] - - // read remote server version - version, err := readVersion(c) - if err != nil { - return err - } - magics.serverVersion = version - clientKexInit := kexInitMsg{ - KexAlgos: supportedKexAlgos, - ServerHostKeyAlgos: supportedHostKeyAlgos, - CiphersClientServer: c.config.Crypto.ciphers(), - CiphersServerClient: c.config.Crypto.ciphers(), - MACsClientServer: supportedMACs, - MACsServerClient: supportedMACs, - CompressionClientServer: supportedCompressions, - CompressionServerClient: supportedCompressions, - } - kexInitPacket := marshal(msgKexInit, clientKexInit) - magics.clientKexInit = kexInitPacket - - if err := c.writePacket(kexInitPacket); err != nil { - return err - } - packet, err := c.readPacket() - if err != nil { - return err - } - - magics.serverKexInit = packet - - var serverKexInit kexInitMsg - if err = unmarshal(&serverKexInit, packet, msgKexInit); err != nil { - return err - } - - kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(c.transport, &clientKexInit, &serverKexInit) - if !ok { - return errors.New("ssh: no common algorithms") - } - - if serverKexInit.FirstKexFollows && kexAlgo != serverKexInit.KexAlgos[0] { - // The server sent a Kex message for the wrong algorithm, - // which we have to ignore. - if _, err := c.readPacket(); err != nil { - return err - } - } - - var H, K []byte - var hashFunc crypto.Hash - switch kexAlgo { - case kexAlgoDH14SHA1: - hashFunc = crypto.SHA1 - dhGroup14Once.Do(initDHGroup14) - H, K, err = c.kexDH(dhGroup14, hashFunc, &magics, hostKeyAlgo) - default: - err = fmt.Errorf("ssh: unexpected key exchange algorithm %v", kexAlgo) - } - if err != nil { - return err - } - - if err = c.writePacket([]byte{msgNewKeys}); err != nil { - return err - } - if err = c.transport.writer.setupKeys(clientKeys, K, H, H, hashFunc); err != nil { - return err - } - if packet, err = c.readPacket(); err != nil { - return err - } - if packet[0] != msgNewKeys { - return UnexpectedMessageError{msgNewKeys, packet[0]} - } - if err := c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc); err != nil { - return err - } - return c.authenticate(H) -} - -// kexDH performs Diffie-Hellman key agreement on a ClientConn. The -// returned values are given the same names as in RFC 4253, section 8. -func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) ([]byte, []byte, error) { - x, err := rand.Int(c.config.rand(), group.p) - if err != nil { - return nil, nil, err - } - X := new(big.Int).Exp(group.g, x, group.p) - kexDHInit := kexDHInitMsg{ - X: X, - } - if err := c.writePacket(marshal(msgKexDHInit, kexDHInit)); err != nil { - return nil, nil, err - } - - packet, err := c.readPacket() - if err != nil { - return nil, nil, err - } - - var kexDHReply = new(kexDHReplyMsg) - if err = unmarshal(kexDHReply, packet, msgKexDHReply); err != nil { - return nil, nil, err - } - - if kexDHReply.Y.Sign() == 0 || kexDHReply.Y.Cmp(group.p) >= 0 { - return nil, nil, errors.New("server DH parameter out of bounds") - } - - kInt := new(big.Int).Exp(kexDHReply.Y, x, group.p) - h := hashFunc.New() - writeString(h, magics.clientVersion) - writeString(h, magics.serverVersion) - writeString(h, magics.clientKexInit) - writeString(h, magics.serverKexInit) - writeString(h, kexDHReply.HostKey) - writeInt(h, X) - writeInt(h, kexDHReply.Y) - K := make([]byte, intLength(kInt)) - marshalInt(K, kInt) - h.Write(K) - - H := h.Sum(nil) - - return H, K, nil -} - -// mainLoop reads incoming messages and routes channel messages -// to their respective ClientChans. -func (c *ClientConn) mainLoop() { - // TODO(dfc) signal the underlying close to all channels - defer c.Close() - for { - packet, err := c.readPacket() - if err != nil { - break - } - // TODO(dfc) A note on blocking channel use. - // The msg, win, data and dataExt channels of a clientChan can - // cause this loop to block indefinately if the consumer does - // 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).stdout.handleData(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).stderr.handleData(packet[:length]) - } - } - default: - switch msg := decode(packet).(type) { - case *channelOpenMsg: - c.getChan(msg.PeersId).msg <- msg - case *channelOpenConfirmMsg: - c.getChan(msg.PeersId).msg <- msg - case *channelOpenFailureMsg: - c.getChan(msg.PeersId).msg <- msg - case *channelCloseMsg: - ch := c.getChan(msg.PeersId) - ch.theyClosed = true - close(ch.stdin.win) - ch.stdout.eof() - ch.stderr.eof() - close(ch.msg) - if !ch.weClosed { - ch.weClosed = true - ch.sendClose() - } - c.chanlist.remove(msg.PeersId) - case *channelEOFMsg: - ch := c.getChan(msg.PeersId) - ch.stdout.eof() - // RFC 4254 is mute on how EOF affects dataExt messages but - // it is logical to signal EOF at the same time. - ch.stderr.eof() - case *channelRequestSuccessMsg: - c.getChan(msg.PeersId).msg <- msg - case *channelRequestFailureMsg: - c.getChan(msg.PeersId).msg <- msg - case *channelRequestMsg: - c.getChan(msg.PeersId).msg <- msg - case *windowAdjustMsg: - c.getChan(msg.PeersId).stdin.win <- int(msg.AdditionalBytes) - case *disconnectMsg: - break - default: - fmt.Printf("mainLoop: unhandled message %T: %v\n", msg, msg) - } - } - } -} - -// Dial connects to the given network address using net.Dial and -// then initiates a SSH handshake, returning the resulting client connection. -func Dial(network, addr string, config *ClientConfig) (*ClientConn, error) { - conn, err := net.Dial(network, addr) - if err != nil { - return nil, err - } - return Client(conn, config) -} - -// A ClientConfig structure is used to configure a ClientConn. After one has -// been passed to an SSH function it must not be modified. -type ClientConfig struct { - // Rand provides the source of entropy for key exchange. If Rand is - // nil, the cryptographic random reader in package crypto/rand will - // be used. - Rand io.Reader - - // The username to authenticate. - User string - - // A slice of ClientAuth methods. Only the first instance - // of a particular RFC 4252 method will be used during authentication. - Auth []ClientAuth - - // Cryptographic-related configuration. - Crypto CryptoConfig -} - -func (c *ClientConfig) rand() io.Reader { - if c.Rand == nil { - return rand.Reader - } - return c.Rand -} - -// A clientChan represents a single RFC 4254 channel that is multiplexed -// over a single SSH connection. -type clientChan struct { - packetWriter - id, peersId uint32 - stdin *chanWriter // receives window adjustments - stdout *chanReader // receives the payload of channelData messages - stderr *chanReader // receives the payload of channelExtendedData messages - msg chan interface{} // incoming messages - theyClosed bool // indicates the close msg has been received from the remote side - weClosed bool // incidates the close msg has been sent from our side -} - -// newClientChan returns a partially constructed *clientChan -// using the local id provided. To be usable clientChan.peersId -// needs to be assigned once known. -func newClientChan(t *transport, id uint32) *clientChan { - c := &clientChan{ - packetWriter: t, - id: id, - msg: make(chan interface{}, 16), - } - c.stdin = &chanWriter{ - win: make(chan int, 16), - clientChan: c, - } - c.stdout = &chanReader{ - data: make(chan []byte, 16), - clientChan: c, - } - c.stderr = &chanReader{ - data: make(chan []byte, 16), - clientChan: c, - } - return c -} - -// waitForChannelOpenResponse, if successful, fills out -// the peerId and records any initial window advertisement. -func (c *clientChan) waitForChannelOpenResponse() error { - switch msg := (<-c.msg).(type) { - case *channelOpenConfirmMsg: - // fixup peersId field - c.peersId = msg.MyId - c.stdin.win <- int(msg.MyWindow) - return nil - case *channelOpenFailureMsg: - return errors.New(safeString(msg.Message)) - } - return errors.New("unexpected packet") -} - -// sendEOF sends EOF to the server. RFC 4254 Section 5.3 -func (c *clientChan) sendEOF() error { - return c.writePacket(marshal(msgChannelEOF, channelEOFMsg{ - PeersId: c.peersId, - })) -} - -// sendClose signals the intent to close the channel. -func (c *clientChan) sendClose() error { - return c.writePacket(marshal(msgChannelClose, channelCloseMsg{ - PeersId: c.peersId, - })) -} - -// Close closes the channel. This does not close the underlying connection. -func (c *clientChan) Close() error { - if !c.weClosed { - c.weClosed = true - return c.sendClose() - } - return nil -} - -// Thread safe channel list. -type chanlist struct { - // protects concurrent access to chans - sync.Mutex - // chans are indexed by the local id of the channel, clientChan.id. - // The PeersId value of messages received by ClientConn.mainLoop is - // used to locate the right local clientChan in this slice. - chans []*clientChan -} - -// Allocate a new ClientChan with the next avail local id. -func (c *chanlist) newChan(t *transport) *clientChan { - c.Lock() - defer c.Unlock() - for i := range c.chans { - if c.chans[i] == nil { - ch := newClientChan(t, uint32(i)) - c.chans[i] = ch - return ch - } - } - i := len(c.chans) - ch := newClientChan(t, uint32(i)) - c.chans = append(c.chans, ch) - return ch -} - -func (c *chanlist) getChan(id uint32) *clientChan { - c.Lock() - defer c.Unlock() - return c.chans[int(id)] -} - -func (c *chanlist) remove(id uint32) { - c.Lock() - defer c.Unlock() - c.chans[int(id)] = nil -} - -// A chanWriter represents the stdin of a remote process. -type chanWriter struct { - win chan int // receives window adjustments - rwin int // current rwin size - clientChan *clientChan // the channel backing this writer -} - -// Write writes data to the remote process's standard input. -func (w *chanWriter) Write(data []byte) (written int, err error) { - for len(data) > 0 { - for w.rwin < 1 { - win, ok := <-w.win - if !ok { - return 0, io.EOF - } - w.rwin += win - } - n := min(len(data), w.rwin) - peersId := w.clientChan.peersId - packet := []byte{ - msgChannelData, - byte(peersId >> 24), byte(peersId >> 16), byte(peersId >> 8), byte(peersId), - byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n), - } - if err = w.clientChan.writePacket(append(packet, data[:n]...)); err != nil { - break - } - data = data[n:] - w.rwin -= n - written += n - } - return -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func (w *chanWriter) Close() error { - return w.clientChan.sendEOF() -} - -// A chanReader represents stdout or stderr of a remote process. -type chanReader struct { - // TODO(dfc) a fixed size channel may not be the right data structure. - // If writes to this channel block, they will block mainLoop, making - // it unable to receive new messages from the remote side. - data chan []byte // receives data from remote - dataClosed bool // protects data from being closed twice - clientChan *clientChan // the channel backing this reader - buf []byte -} - -// eof signals to the consumer that there is no more data to be received. -func (r *chanReader) eof() { - if !r.dataClosed { - r.dataClosed = true - close(r.data) - } -} - -// handleData sends buf to the reader's consumer. If r.data is closed -// the data will be silently discarded -func (r *chanReader) handleData(buf []byte) { - if !r.dataClosed { - r.data <- buf - } -} - -// Read reads data from the remote process's stdout or stderr. -func (r *chanReader) Read(data []byte) (int, error) { - var ok bool - for { - if len(r.buf) > 0 { - n := copy(data, r.buf) - r.buf = r.buf[n:] - msg := windowAdjustMsg{ - PeersId: r.clientChan.peersId, - AdditionalBytes: uint32(n), - } - return n, r.clientChan.writePacket(marshal(msgChannelWindowAdjust, msg)) - } - r.buf, ok = <-r.data - if !ok { - return 0, io.EOF - } - } - panic("unreachable") -} diff --git a/libgo/go/exp/ssh/client_auth.go b/libgo/go/exp/ssh/client_auth.go deleted file mode 100644 index 3a7e9fb9801..00000000000 --- a/libgo/go/exp/ssh/client_auth.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "errors" - "io" -) - -// authenticate authenticates with the remote server. See RFC 4252. -func (c *ClientConn) authenticate(session []byte) error { - // initiate user auth session - if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil { - return err - } - packet, err := c.readPacket() - if err != nil { - return err - } - var serviceAccept serviceAcceptMsg - if err := unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil { - return err - } - // during the authentication phase the client first attempts the "none" method - // then any untried methods suggested by the server. - tried, remain := make(map[string]bool), make(map[string]bool) - for auth := ClientAuth(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(session, c.config.User, c.transport, c.config.rand()) - if err != nil { - return err - } - if ok { - // success - return nil - } - tried[auth.method()] = true - delete(remain, auth.method()) - for _, meth := range methods { - if tried[meth] { - // if we've tried meth already, skip it. - continue - } - remain[meth] = true - } - auth = nil - for _, a := range c.config.Auth { - if remain[a.method()] { - auth = a - break - } - } - } - return errors.New("ssh: unable to authenticate, no supported methods remain") -} - -// A ClientAuth represents an instance of an RFC 4252 authentication method. -type ClientAuth interface { - // auth authenticates user over transport t. - // Returns true if authentication is successful. - // If authentication is not successful, a []string of alternative - // method names is returned. - auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) - - // method returns the RFC 4252 method name. - method() string -} - -// "none" authentication, RFC 4252 section 5.2. -type noneAuth int - -func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { - if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{ - User: user, - Service: serviceSSH, - Method: "none", - })); err != nil { - return false, nil, err - } - - return handleAuthResponse(t) -} - -func (n *noneAuth) method() string { - return "none" -} - -// "password" authentication, RFC 4252 Section 8. -type passwordAuth struct { - ClientPassword -} - -func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { - type passwordAuthMsg struct { - User string - Service string - Method string - Reply bool - Password string - } - - pw, err := p.Password(user) - if err != nil { - return false, nil, err - } - - if err := t.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{ - User: user, - Service: serviceSSH, - Method: "password", - Reply: false, - Password: pw, - })); err != nil { - return false, nil, err - } - - return handleAuthResponse(t) -} - -func (p *passwordAuth) method() string { - return "password" -} - -// A ClientPassword implements access to a client's passwords. -type ClientPassword interface { - // Password returns the password to use for user. - Password(user string) (password string, err error) -} - -// ClientAuthPassword returns a ClientAuth using password authentication. -func ClientAuthPassword(impl ClientPassword) ClientAuth { - return &passwordAuth{impl} -} - -// ClientKeyring implements access to a client key ring. -type ClientKeyring interface { - // Key returns the i'th rsa.Publickey or dsa.Publickey, or nil if - // no key exists at i. - Key(i int) (key interface{}, err error) - - // Sign returns a signature of the given data using the i'th key - // and the supplied random source. - Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) -} - -// "publickey" authentication, RFC 4252 Section 7. -type publickeyAuth struct { - ClientKeyring -} - -type publickeyAuthMsg struct { - User string - Service string - Method string - // HasSig indicates to the reciver packet that the auth request is signed and - // should be used for authentication of the request. - HasSig bool - Algoname string - Pubkey string - // Sig is defined as []byte so marshal will exclude it during validateKey - Sig []byte `ssh:"rest"` -} - -func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { - - // Authentication is performed in two stages. The first stage sends an - // enquiry to test if each key is acceptable to the remote. The second - // stage attempts to authenticate with the valid keys obtained in the - // first stage. - - var index int - // a map of public keys to their index in the keyring - validKeys := make(map[int]interface{}) - for { - key, err := p.Key(index) - if err != nil { - return false, nil, err - } - if key == nil { - // no more keys in the keyring - break - } - - if ok, err := p.validateKey(key, user, t); ok { - validKeys[index] = key - } else { - if err != nil { - return false, nil, err - } - } - index++ - } - - // methods that may continue if this auth is not successful. - var methods []string - for i, key := range validKeys { - pubkey := serializePublickey(key) - algoname := algoName(key) - sign, err := p.Sign(i, rand, buildDataSignedForAuth(session, userAuthRequestMsg{ - User: user, - Service: serviceSSH, - Method: p.method(), - }, []byte(algoname), pubkey)) - if err != nil { - return false, nil, err - } - // manually wrap the serialized signature in a string - s := serializeSignature(algoname, sign) - sig := make([]byte, stringLength(s)) - marshalString(sig, s) - msg := publickeyAuthMsg{ - User: user, - Service: serviceSSH, - Method: p.method(), - HasSig: true, - Algoname: algoname, - Pubkey: string(pubkey), - Sig: sig, - } - p := marshal(msgUserAuthRequest, msg) - if err := t.writePacket(p); err != nil { - return false, nil, err - } - success, methods, err := handleAuthResponse(t) - if err != nil { - return false, nil, err - } - if success { - return success, methods, err - } - } - return false, methods, nil -} - -// validateKey validates the key provided it is acceptable to the server. -func (p *publickeyAuth) validateKey(key interface{}, user string, t *transport) (bool, error) { - pubkey := serializePublickey(key) - algoname := algoName(key) - msg := publickeyAuthMsg{ - User: user, - Service: serviceSSH, - Method: p.method(), - HasSig: false, - Algoname: algoname, - Pubkey: string(pubkey), - } - if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil { - return false, err - } - - return p.confirmKeyAck(key, t) -} - -func (p *publickeyAuth) confirmKeyAck(key interface{}, t *transport) (bool, error) { - pubkey := serializePublickey(key) - algoname := algoName(key) - - for { - packet, err := t.readPacket() - if err != nil { - return false, err - } - switch packet[0] { - case msgUserAuthBanner: - // TODO(gpaul): add callback to present the banner to the user - case msgUserAuthPubKeyOk: - msg := decode(packet).(*userAuthPubKeyOkMsg) - if msg.Algo != algoname || msg.PubKey != string(pubkey) { - return false, nil - } - return true, nil - case msgUserAuthFailure: - return false, nil - default: - return false, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} - } - } - panic("unreachable") -} - -func (p *publickeyAuth) method() string { - return "publickey" -} - -// ClientAuthKeyring returns a ClientAuth using public key authentication. -func ClientAuthKeyring(impl ClientKeyring) ClientAuth { - return &publickeyAuth{impl} -} - -// handleAuthResponse returns whether the preceding authentication request succeeded -// along with a list of remaining authentication methods to try next and -// an error if an unexpected response was received. -func handleAuthResponse(t *transport) (bool, []string, error) { - for { - packet, err := t.readPacket() - if err != nil { - return false, nil, err - } - - switch packet[0] { - case msgUserAuthBanner: - // TODO: add callback to present the banner to the user - case msgUserAuthFailure: - msg := decode(packet).(*userAuthFailureMsg) - return false, msg.Methods, nil - case msgUserAuthSuccess: - return true, nil, nil - case msgDisconnect: - return false, nil, io.EOF - default: - return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} - } - } - panic("unreachable") -} diff --git a/libgo/go/exp/ssh/client_auth_test.go b/libgo/go/exp/ssh/client_auth_test.go deleted file mode 100644 index c41a93b5c7d..00000000000 --- a/libgo/go/exp/ssh/client_auth_test.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto" - "crypto/dsa" - "crypto/rsa" - _ "crypto/sha1" - "crypto/x509" - "encoding/pem" - "errors" - "io" - "io/ioutil" - "math/big" - "testing" -) - -// private key for mock server -const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU -70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx -9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF -tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z -s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc -qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT -+IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea -riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH -D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh -atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT -b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN -ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M -MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4 -KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8 -e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1 -D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+ -3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj -orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw -64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc -XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc -QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g -/SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ -I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk -gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl -NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw== ------END RSA PRIVATE KEY-----` - -const testClientPrivateKey = `-----BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBALdGZxkXDAjsYk10ihwU6Id2KeILz1TAJuoq4tOgDWxEEGeTrcld -r/ZwVaFzjWzxaf6zQIJbfaSEAhqD5yo72+sCAwEAAQJBAK8PEVU23Wj8mV0QjwcJ -tZ4GcTUYQL7cF4+ezTCE9a1NrGnCP2RuQkHEKxuTVrxXt+6OF15/1/fuXnxKjmJC -nxkCIQDaXvPPBi0c7vAxGwNY9726x01/dNbHCE0CBtcotobxpwIhANbbQbh3JHVW -2haQh4fAG5mhesZKAGcxTyv4mQ7uMSQdAiAj+4dzMpJWdSzQ+qGHlHMIBvVHLkqB -y2VdEyF7DPCZewIhAI7GOI/6LDIFOvtPo6Bj2nNmyQ1HU6k/LRtNIXi4c9NJAiAr -rrxx26itVhJmcvoUhOjwuzSlP2bE5VHAvkGB352YBg== ------END RSA PRIVATE KEY-----` - -// keychain implements the ClientPublickey interface -type keychain struct { - keys []interface{} -} - -func (k *keychain) Key(i int) (interface{}, error) { - if i < 0 || i >= len(k.keys) { - return nil, nil - } - switch key := k.keys[i].(type) { - case *rsa.PrivateKey: - return key.PublicKey, nil - case *dsa.PrivateKey: - return key.PublicKey, nil - } - panic("unknown key type") -} - -func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) { - hashFunc := crypto.SHA1 - h := hashFunc.New() - h.Write(data) - digest := h.Sum(nil) - switch key := k.keys[i].(type) { - case *rsa.PrivateKey: - return rsa.SignPKCS1v15(rand, key, hashFunc, digest) - } - return nil, errors.New("unknown key type") -} - -func (k *keychain) loadPEM(file string) error { - buf, err := ioutil.ReadFile(file) - if err != nil { - return err - } - block, _ := pem.Decode(buf) - if block == nil { - return errors.New("ssh: no key found") - } - r, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return err - } - k.keys = append(k.keys, r) - return nil -} - -// password implements the ClientPassword interface -type password string - -func (p password) Password(user string) (string, error) { - return string(p), nil -} - -// reused internally by tests -var ( - rsakey *rsa.PrivateKey - dsakey *dsa.PrivateKey - clientKeychain = new(keychain) - clientPassword = password("tiger") - serverConfig = &ServerConfig{ - PasswordCallback: func(user, pass string) bool { - return user == "testuser" && pass == string(clientPassword) - }, - PublicKeyCallback: func(user, algo string, pubkey []byte) bool { - key := clientKeychain.keys[0].(*rsa.PrivateKey).PublicKey - expected := []byte(serializePublickey(key)) - algoname := algoName(key) - return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected) - }, - } -) - -func init() { - if err := serverConfig.SetRSAPrivateKey([]byte(testServerPrivateKey)); err != nil { - panic("unable to set private key: " + err.Error()) - } - - block, _ := pem.Decode([]byte(testClientPrivateKey)) - rsakey, _ = x509.ParsePKCS1PrivateKey(block.Bytes) - - clientKeychain.keys = append(clientKeychain.keys, rsakey) - dsakey = new(dsa.PrivateKey) - // taken from crypto/dsa/dsa_test.go - dsakey.P, _ = new(big.Int).SetString("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF", 16) - dsakey.Q, _ = new(big.Int).SetString("E1D3391245933D68A0714ED34BBCB7A1F422B9C1", 16) - dsakey.G, _ = new(big.Int).SetString("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA", 16) - dsakey.Y, _ = new(big.Int).SetString("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2", 16) - dsakey.X, _ = new(big.Int).SetString("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A", 16) -} - -// newMockAuthServer creates a new Server bound to -// the loopback interface. The server exits after -// processing one handshake. -func newMockAuthServer(t *testing.T) string { - l, err := Listen("tcp", "127.0.0.1:0", serverConfig) - if err != nil { - t.Fatalf("unable to newMockAuthServer: %s", err) - } - go func() { - defer l.Close() - c, err := l.Accept() - defer c.Close() - if err != nil { - t.Errorf("Unable to accept incoming connection: %v", err) - return - } - if err := c.Handshake(); err != nil { - // not Errorf because this is expected to - // fail for some tests. - t.Logf("Handshaking error: %v", err) - return - } - }() - return l.Addr().String() -} - -func TestClientAuthPublickey(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []ClientAuth{ - ClientAuthKeyring(clientKeychain), - }, - } - c, err := Dial("tcp", newMockAuthServer(t), config) - if err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } - c.Close() -} - -func TestClientAuthPassword(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []ClientAuth{ - ClientAuthPassword(clientPassword), - }, - } - - c, err := Dial("tcp", newMockAuthServer(t), config) - if err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } - c.Close() -} - -func TestClientAuthWrongPassword(t *testing.T) { - wrongPw := password("wrong") - config := &ClientConfig{ - User: "testuser", - Auth: []ClientAuth{ - ClientAuthPassword(wrongPw), - ClientAuthKeyring(clientKeychain), - }, - } - - c, err := Dial("tcp", newMockAuthServer(t), config) - if err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } - c.Close() -} - -// the mock server will only authenticate ssh-rsa keys -func TestClientAuthInvalidPublickey(t *testing.T) { - kc := new(keychain) - kc.keys = append(kc.keys, dsakey) - config := &ClientConfig{ - User: "testuser", - Auth: []ClientAuth{ - ClientAuthKeyring(kc), - }, - } - - c, err := Dial("tcp", newMockAuthServer(t), config) - if err == nil { - c.Close() - t.Fatalf("dsa private key should not have authenticated with rsa public key") - } -} - -// the client should authenticate with the second key -func TestClientAuthRSAandDSA(t *testing.T) { - kc := new(keychain) - kc.keys = append(kc.keys, dsakey, rsakey) - config := &ClientConfig{ - User: "testuser", - Auth: []ClientAuth{ - ClientAuthKeyring(kc), - }, - } - c, err := Dial("tcp", newMockAuthServer(t), config) - if err != nil { - t.Fatalf("client could not authenticate with rsa key: %v", err) - } - c.Close() -} diff --git a/libgo/go/exp/ssh/client_func_test.go b/libgo/go/exp/ssh/client_func_test.go deleted file mode 100644 index b4bdba95396..00000000000 --- a/libgo/go/exp/ssh/client_func_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// ClientConn functional tests. -// These tests require a running ssh server listening on port 22 -// on the local host. Functional tests will be skipped unless -// -ssh.user and -ssh.pass must be passed to gotest. - -import ( - "flag" - "testing" -) - -var ( - sshuser = flag.String("ssh.user", "", "ssh username") - sshpass = flag.String("ssh.pass", "", "ssh password") - sshprivkey = flag.String("ssh.privkey", "", "ssh privkey file") -) - -func TestFuncPasswordAuth(t *testing.T) { - if *sshuser == "" { - t.Log("ssh.user not defined, skipping test") - return - } - config := &ClientConfig{ - User: *sshuser, - Auth: []ClientAuth{ - ClientAuthPassword(password(*sshpass)), - }, - } - conn, err := Dial("tcp", "localhost:22", config) - if err != nil { - t.Fatalf("Unable to connect: %s", err) - } - defer conn.Close() -} - -func TestFuncPublickeyAuth(t *testing.T) { - if *sshuser == "" { - t.Log("ssh.user not defined, skipping test") - return - } - kc := new(keychain) - if err := kc.loadPEM(*sshprivkey); err != nil { - t.Fatalf("unable to load private key: %s", err) - } - config := &ClientConfig{ - User: *sshuser, - Auth: []ClientAuth{ - ClientAuthKeyring(kc), - }, - } - conn, err := Dial("tcp", "localhost:22", config) - if err != nil { - t.Fatalf("unable to connect: %s", err) - } - defer conn.Close() -} diff --git a/libgo/go/exp/ssh/common.go b/libgo/go/exp/ssh/common.go deleted file mode 100644 index 6844fb89b79..00000000000 --- a/libgo/go/exp/ssh/common.go +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "crypto/dsa" - "crypto/rsa" - "math/big" - "strconv" - "sync" -) - -// These are string constants in the SSH protocol. -const ( - kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" - hostAlgoRSA = "ssh-rsa" - macSHA196 = "hmac-sha1-96" - compressionNone = "none" - serviceUserAuth = "ssh-userauth" - serviceSSH = "ssh-connection" -) - -var supportedKexAlgos = []string{kexAlgoDH14SHA1} -var supportedHostKeyAlgos = []string{hostAlgoRSA} -var supportedMACs = []string{macSHA196} -var supportedCompressions = []string{compressionNone} - -// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. -type dhGroup struct { - g, p *big.Int -} - -// dhGroup14 is the group called diffie-hellman-group14-sha1 in RFC 4253 and -// Oakley Group 14 in RFC 3526. -var dhGroup14 *dhGroup - -var dhGroup14Once sync.Once - -func initDHGroup14() { - p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) - - dhGroup14 = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, - } -} - -// UnexpectedMessageError results when the SSH message that we received didn't -// match what we wanted. -type UnexpectedMessageError struct { - expected, got uint8 -} - -func (u UnexpectedMessageError) Error() string { - return "ssh: unexpected message type " + strconv.Itoa(int(u.got)) + " (expected " + strconv.Itoa(int(u.expected)) + ")" -} - -// ParseError results from a malformed SSH message. -type ParseError struct { - msgType uint8 -} - -func (p ParseError) Error() string { - return "ssh: parse error in message type " + strconv.Itoa(int(p.msgType)) -} - -type handshakeMagics struct { - clientVersion, serverVersion []byte - clientKexInit, serverKexInit []byte -} - -func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { - for _, clientAlgo := range clientAlgos { - for _, serverAlgo := range serverAlgos { - if clientAlgo == serverAlgo { - return clientAlgo, true - } - } - } - - return -} - -func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) { - kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) - if !ok { - return - } - - hostKeyAlgo, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) - if !ok { - return - } - - transport.writer.cipherAlgo, ok = findCommonAlgorithm(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) - if !ok { - return - } - - transport.reader.cipherAlgo, ok = findCommonAlgorithm(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) - if !ok { - return - } - - transport.writer.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) - if !ok { - return - } - - transport.reader.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) - if !ok { - return - } - - transport.writer.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) - if !ok { - return - } - - transport.reader.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) - if !ok { - return - } - - ok = true - return -} - -// Cryptographic configuration common to both ServerConfig and ClientConfig. -type CryptoConfig struct { - // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is - // used. - Ciphers []string -} - -func (c *CryptoConfig) ciphers() []string { - if c.Ciphers == nil { - return DefaultCipherOrder - } - return c.Ciphers -} - -// serialize a signed slice according to RFC 4254 6.6. -func serializeSignature(algoname string, sig []byte) []byte { - length := stringLength([]byte(algoname)) - length += stringLength(sig) - - ret := make([]byte, length) - r := marshalString(ret, []byte(algoname)) - r = marshalString(r, sig) - - return ret -} - -// serialize an rsa.PublicKey or dsa.PublicKey according to RFC 4253 6.6. -func serializePublickey(key interface{}) []byte { - algoname := algoName(key) - switch key := key.(type) { - case rsa.PublicKey: - e := new(big.Int).SetInt64(int64(key.E)) - length := stringLength([]byte(algoname)) - length += intLength(e) - length += intLength(key.N) - ret := make([]byte, length) - r := marshalString(ret, []byte(algoname)) - r = marshalInt(r, e) - marshalInt(r, key.N) - return ret - case dsa.PublicKey: - length := stringLength([]byte(algoname)) - length += intLength(key.P) - length += intLength(key.Q) - length += intLength(key.G) - length += intLength(key.Y) - ret := make([]byte, length) - r := marshalString(ret, []byte(algoname)) - r = marshalInt(r, key.P) - r = marshalInt(r, key.Q) - r = marshalInt(r, key.G) - marshalInt(r, key.Y) - return ret - } - panic("unexpected key type") -} - -func algoName(key interface{}) string { - switch key.(type) { - case rsa.PublicKey: - return "ssh-rsa" - case dsa.PublicKey: - return "ssh-dss" - } - panic("unexpected key type") -} - -// buildDataSignedForAuth returns the data that is signed in order to prove -// posession of a private key. See RFC 4252, section 7. -func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { - user := []byte(req.User) - service := []byte(req.Service) - method := []byte(req.Method) - - length := stringLength(sessionId) - length += 1 - length += stringLength(user) - length += stringLength(service) - length += stringLength(method) - length += 1 - length += stringLength(algo) - length += stringLength(pubKey) - - ret := make([]byte, length) - r := marshalString(ret, sessionId) - r[0] = msgUserAuthRequest - r = r[1:] - r = marshalString(r, user) - r = marshalString(r, service) - r = marshalString(r, method) - r[0] = 1 - r = r[1:] - r = marshalString(r, algo) - r = marshalString(r, pubKey) - return ret -} - -// safeString sanitises s according to RFC 4251, section 9.2. -// All control characters except tab, carriage return and newline are -// replaced by 0x20. -func safeString(s string) string { - out := []byte(s) - for i, c := range out { - if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 { - out[i] = 0x20 - } - } - return string(out) -} diff --git a/libgo/go/exp/ssh/common_test.go b/libgo/go/exp/ssh/common_test.go deleted file mode 100644 index 058fb04fe1b..00000000000 --- a/libgo/go/exp/ssh/common_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "testing" -) - -func TestSafeString(t *testing.T) { - strings := map[string]string{ - "\x20\x0d\x0a": "\x20\x0d\x0a", - "flibble": "flibble", - "new\x20line": "new\x20line", - "123456\x07789": "123456 789", - "\t\t\x10\r\n": "\t\t \r\n", - } - - for s, expected := range strings { - actual := safeString(s) - if expected != actual { - t.Errorf("expected: %v, actual: %v", []byte(expected), []byte(actual)) - } - } -} diff --git a/libgo/go/exp/ssh/doc.go b/libgo/go/exp/ssh/doc.go deleted file mode 100644 index e7deb5ec168..00000000000 --- a/libgo/go/exp/ssh/doc.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package ssh implements an SSH client and server. - -SSH is a transport security protocol, an authentication protocol and a -family of application protocols. The most typical application level -protocol is a remote shell and this is specifically implemented. However, -the multiplexed nature of SSH is exposed to users that wish to support -others. - -An SSH server is represented by a ServerConfig, which holds certificate -details and handles authentication of ServerConns. - - config := new(ssh.ServerConfig) - config.PubKeyCallback = pubKeyAuth - config.PasswordCallback = passwordAuth - - pemBytes, err := ioutil.ReadFile("id_rsa") - if err != nil { - panic("Failed to load private key") - } - err = config.SetRSAPrivateKey(pemBytes) - if err != nil { - panic("Failed to parse private key") - } - -Once a ServerConfig has been configured, connections can be accepted. - - listener := Listen("tcp", "0.0.0.0:2022", config) - sConn, err := listener.Accept() - if err != nil { - panic("failed to accept incoming connection") - } - if err := sConn.Handshake(conn); err != nil { - panic("failed to handshake") - } - -An SSH connection multiplexes several channels, which must be accepted themselves: - - for { - channel, err := sConn.Accept() - if err != nil { - panic("error from Accept") - } - - ... - } - -Accept reads from the connection, demultiplexes packets to their corresponding -channels and returns when a new channel request is seen. Some goroutine must -always be calling Accept; otherwise no messages will be forwarded to the -channels. - -Channels have a type, depending on the application level protocol intended. In -the case of a shell, the type is "session" and ServerShell may be used to -present a simple terminal interface. - - if channel.ChannelType() != "session" { - channel.Reject(UnknownChannelType, "unknown channel type") - return - } - channel.Accept() - - term := terminal.NewTerminal(channel, "> ") - serverTerm := &ssh.ServerTerminal{ - Term: term, - Channel: channel, - } - go func() { - defer channel.Close() - for { - line, err := serverTerm.ReadLine() - if err != nil { - break - } - println(line) - } - return - }() - -To authenticate with the remote server you must pass at least one implementation of -ClientAuth via the Auth field in ClientConfig. - - // password implements the ClientPassword interface - type password string - - func (p password) Password(user string) (string, error) { - return string(p), nil - } - - config := &ssh.ClientConfig { - User: "username", - Auth: []ClientAuth { - // ClientAuthPassword wraps a ClientPassword implementation - // in a type that implements ClientAuth. - ClientAuthPassword(password("yourpassword")), - } - } - -An SSH client is represented with a ClientConn. Currently only the "password" -authentication method is supported. - - config := &ClientConfig{ - User: "username", - Auth: []ClientAuth{ ... }, - } - client, err := Dial("yourserver.com:22", config) - -Each ClientConn can support multiple interactive sessions, represented by a Session. - - session, err := client.NewSession() - -Once a Session is created, you can execute a single command on the remote side -using the Exec method. - - b := bytes.NewBuffer() - session.Stdin = b - if err := session.Run("/usr/bin/whoami"); err != nil { - panic("Failed to exec: " + err.String()) - } - fmt.Println(bytes.String()) - session.Close() -*/ -package ssh diff --git a/libgo/go/exp/ssh/messages.go b/libgo/go/exp/ssh/messages.go deleted file mode 100644 index 34ad131ff64..00000000000 --- a/libgo/go/exp/ssh/messages.go +++ /dev/null @@ -1,640 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "io" - "math/big" - "reflect" -) - -// These are SSH message type numbers. They are scattered around several -// documents but many were taken from -// http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 -const ( - msgDisconnect = 1 - msgIgnore = 2 - msgUnimplemented = 3 - msgDebug = 4 - msgServiceRequest = 5 - msgServiceAccept = 6 - - msgKexInit = 20 - msgNewKeys = 21 - - msgKexDHInit = 30 - msgKexDHReply = 31 - - msgUserAuthRequest = 50 - msgUserAuthFailure = 51 - msgUserAuthSuccess = 52 - msgUserAuthBanner = 53 - msgUserAuthPubKeyOk = 60 - - msgGlobalRequest = 80 - msgRequestSuccess = 81 - msgRequestFailure = 82 - - msgChannelOpen = 90 - msgChannelOpenConfirm = 91 - msgChannelOpenFailure = 92 - msgChannelWindowAdjust = 93 - msgChannelData = 94 - msgChannelExtendedData = 95 - msgChannelEOF = 96 - msgChannelClose = 97 - msgChannelRequest = 98 - msgChannelSuccess = 99 - msgChannelFailure = 100 -) - -// SSH messages: -// -// These structures mirror the wire format of the corresponding SSH messages. -// They are marshaled using reflection with the marshal and unmarshal functions -// in this file. The only wrinkle is that a final member of type []byte with a -// ssh tag of "rest" receives the remainder of a packet when unmarshaling. - -// See RFC 4253, section 11.1. -type disconnectMsg struct { - Reason uint32 - Message string - Language string -} - -// See RFC 4253, section 7.1. -type kexInitMsg struct { - Cookie [16]byte - KexAlgos []string - ServerHostKeyAlgos []string - CiphersClientServer []string - CiphersServerClient []string - MACsClientServer []string - MACsServerClient []string - CompressionClientServer []string - CompressionServerClient []string - LanguagesClientServer []string - LanguagesServerClient []string - FirstKexFollows bool - Reserved uint32 -} - -// See RFC 4253, section 8. -type kexDHInitMsg struct { - X *big.Int -} - -type kexDHReplyMsg struct { - HostKey []byte - Y *big.Int - Signature []byte -} - -// See RFC 4253, section 10. -type serviceRequestMsg struct { - Service string -} - -// See RFC 4253, section 10. -type serviceAcceptMsg struct { - Service string -} - -// See RFC 4252, section 5. -type userAuthRequestMsg struct { - User string - Service string - Method string - Payload []byte `ssh:"rest"` -} - -// See RFC 4252, section 5.1 -type userAuthFailureMsg struct { - Methods []string - PartialSuccess bool -} - -// See RFC 4254, section 5.1. -type channelOpenMsg struct { - ChanType string - PeersId uint32 - PeersWindow uint32 - MaxPacketSize uint32 - TypeSpecificData []byte `ssh:"rest"` -} - -// See RFC 4254, section 5.1. -type channelOpenConfirmMsg struct { - PeersId uint32 - MyId uint32 - MyWindow uint32 - MaxPacketSize uint32 - TypeSpecificData []byte `ssh:"rest"` -} - -// See RFC 4254, section 5.1. -type channelOpenFailureMsg struct { - PeersId uint32 - Reason uint32 - Message string - Language string -} - -type channelRequestMsg struct { - PeersId uint32 - Request string - WantReply bool - RequestSpecificData []byte `ssh:"rest"` -} - -// See RFC 4254, section 5.4. -type channelRequestSuccessMsg struct { - PeersId uint32 -} - -// See RFC 4254, section 5.4. -type channelRequestFailureMsg struct { - PeersId uint32 -} - -// See RFC 4254, section 5.3 -type channelCloseMsg struct { - PeersId uint32 -} - -// See RFC 4254, section 5.3 -type channelEOFMsg struct { - PeersId uint32 -} - -// See RFC 4254, section 4 -type globalRequestMsg struct { - Type string - WantReply bool -} - -// See RFC 4254, section 5.2 -type windowAdjustMsg struct { - PeersId uint32 - AdditionalBytes uint32 -} - -// See RFC 4252, section 7 -type userAuthPubKeyOkMsg struct { - Algo string - PubKey string -} - -// unmarshal parses the SSH wire data in packet into out using reflection. -// expectedType is the expected SSH message type. It either returns nil on -// success, or a ParseError or UnexpectedMessageError on error. -func unmarshal(out interface{}, packet []byte, expectedType uint8) error { - if len(packet) == 0 { - return ParseError{expectedType} - } - if packet[0] != expectedType { - return UnexpectedMessageError{expectedType, packet[0]} - } - packet = packet[1:] - - v := reflect.ValueOf(out).Elem() - structType := v.Type() - var ok bool - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - t := field.Type() - switch t.Kind() { - case reflect.Bool: - if len(packet) < 1 { - return ParseError{expectedType} - } - field.SetBool(packet[0] != 0) - packet = packet[1:] - case reflect.Array: - if t.Elem().Kind() != reflect.Uint8 { - panic("array of non-uint8") - } - if len(packet) < t.Len() { - return ParseError{expectedType} - } - for j := 0; j < t.Len(); j++ { - field.Index(j).Set(reflect.ValueOf(packet[j])) - } - packet = packet[t.Len():] - case reflect.Uint32: - var u32 uint32 - if u32, packet, ok = parseUint32(packet); !ok { - return ParseError{expectedType} - } - field.SetUint(uint64(u32)) - case reflect.String: - var s []byte - if s, packet, ok = parseString(packet); !ok { - return ParseError{expectedType} - } - field.SetString(string(s)) - case reflect.Slice: - switch t.Elem().Kind() { - case reflect.Uint8: - if structType.Field(i).Tag.Get("ssh") == "rest" { - field.Set(reflect.ValueOf(packet)) - packet = nil - } else { - var s []byte - if s, packet, ok = parseString(packet); !ok { - return ParseError{expectedType} - } - field.Set(reflect.ValueOf(s)) - } - case reflect.String: - var nl []string - if nl, packet, ok = parseNameList(packet); !ok { - return ParseError{expectedType} - } - field.Set(reflect.ValueOf(nl)) - default: - panic("slice of unknown type") - } - case reflect.Ptr: - if t == bigIntType { - var n *big.Int - if n, packet, ok = parseInt(packet); !ok { - return ParseError{expectedType} - } - field.Set(reflect.ValueOf(n)) - } else { - panic("pointer to unknown type") - } - default: - panic("unknown type") - } - } - - if len(packet) != 0 { - return ParseError{expectedType} - } - - return nil -} - -// marshal serializes the message in msg, using the given message type. -func marshal(msgType uint8, msg interface{}) []byte { - var out []byte - out = append(out, msgType) - - v := reflect.ValueOf(msg) - structType := v.Type() - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - t := field.Type() - switch t.Kind() { - case reflect.Bool: - var v uint8 - if field.Bool() { - v = 1 - } - out = append(out, v) - case reflect.Array: - if t.Elem().Kind() != reflect.Uint8 { - panic("array of non-uint8") - } - for j := 0; j < t.Len(); j++ { - out = append(out, byte(field.Index(j).Uint())) - } - case reflect.Uint32: - u32 := uint32(field.Uint()) - out = append(out, byte(u32>>24)) - out = append(out, byte(u32>>16)) - out = append(out, byte(u32>>8)) - out = append(out, byte(u32)) - case reflect.String: - s := field.String() - out = append(out, byte(len(s)>>24)) - out = append(out, byte(len(s)>>16)) - out = append(out, byte(len(s)>>8)) - out = append(out, byte(len(s))) - out = append(out, s...) - case reflect.Slice: - switch t.Elem().Kind() { - case reflect.Uint8: - length := field.Len() - if structType.Field(i).Tag.Get("ssh") != "rest" { - out = append(out, byte(length>>24)) - out = append(out, byte(length>>16)) - out = append(out, byte(length>>8)) - out = append(out, byte(length)) - } - for j := 0; j < length; j++ { - out = append(out, byte(field.Index(j).Uint())) - } - case reflect.String: - var length int - for j := 0; j < field.Len(); j++ { - if j != 0 { - length++ /* comma */ - } - length += len(field.Index(j).String()) - } - - out = append(out, byte(length>>24)) - out = append(out, byte(length>>16)) - out = append(out, byte(length>>8)) - out = append(out, byte(length)) - for j := 0; j < field.Len(); j++ { - if j != 0 { - out = append(out, ',') - } - out = append(out, field.Index(j).String()...) - } - default: - panic("slice of unknown type") - } - case reflect.Ptr: - if t == bigIntType { - var n *big.Int - nValue := reflect.ValueOf(&n) - nValue.Elem().Set(field) - needed := intLength(n) - oldLength := len(out) - - if cap(out)-len(out) < needed { - newOut := make([]byte, len(out), 2*(len(out)+needed)) - copy(newOut, out) - out = newOut - } - out = out[:oldLength+needed] - marshalInt(out[oldLength:], n) - } else { - panic("pointer to unknown type") - } - } - } - - return out -} - -var bigOne = big.NewInt(1) - -func parseString(in []byte) (out, rest []byte, ok bool) { - if len(in) < 4 { - return - } - length := uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) - if uint32(len(in)) < 4+length { - return - } - out = in[4 : 4+length] - rest = in[4+length:] - ok = true - return -} - -var ( - comma = []byte{','} - emptyNameList = []string{} -) - -func parseNameList(in []byte) (out []string, rest []byte, ok bool) { - contents, rest, ok := parseString(in) - if !ok { - return - } - if len(contents) == 0 { - out = emptyNameList - return - } - parts := bytes.Split(contents, comma) - out = make([]string, len(parts)) - for i, part := range parts { - out[i] = string(part) - } - return -} - -func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { - contents, rest, ok := parseString(in) - if !ok { - return - } - out = new(big.Int) - - if len(contents) > 0 && contents[0]&0x80 == 0x80 { - // This is a negative number - notBytes := make([]byte, len(contents)) - for i := range notBytes { - notBytes[i] = ^contents[i] - } - out.SetBytes(notBytes) - out.Add(out, bigOne) - out.Neg(out) - } else { - // Positive number - out.SetBytes(contents) - } - ok = true - return -} - -func parseUint32(in []byte) (out uint32, rest []byte, ok bool) { - if len(in) < 4 { - return - } - out = uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) - rest = in[4:] - ok = true - return -} - -func nameListLength(namelist []string) int { - length := 4 /* uint32 length prefix */ - for i, name := range namelist { - if i != 0 { - length++ /* comma */ - } - length += len(name) - } - return length -} - -func intLength(n *big.Int) int { - length := 4 /* length bytes */ - if n.Sign() < 0 { - nMinus1 := new(big.Int).Neg(n) - nMinus1.Sub(nMinus1, bigOne) - bitLen := nMinus1.BitLen() - if bitLen%8 == 0 { - // The number will need 0xff padding - length++ - } - length += (bitLen + 7) / 8 - } else if n.Sign() == 0 { - // A zero is the zero length string - } else { - bitLen := n.BitLen() - if bitLen%8 == 0 { - // The number will need 0x00 padding - length++ - } - length += (bitLen + 7) / 8 - } - - return length -} - -func marshalUint32(to []byte, n uint32) []byte { - to[0] = byte(n >> 24) - to[1] = byte(n >> 16) - to[2] = byte(n >> 8) - to[3] = byte(n) - return to[4:] -} - -func marshalUint64(to []byte, n uint64) []byte { - to[0] = byte(n >> 56) - to[1] = byte(n >> 48) - to[2] = byte(n >> 40) - to[3] = byte(n >> 32) - to[4] = byte(n >> 24) - to[5] = byte(n >> 16) - to[6] = byte(n >> 8) - to[7] = byte(n) - return to[8:] -} - -func marshalInt(to []byte, n *big.Int) []byte { - lengthBytes := to - to = to[4:] - length := 0 - - if n.Sign() < 0 { - // A negative number has to be converted to two's-complement - // form. So we'll subtract 1 and invert. If the - // most-significant-bit isn't set then we'll need to pad the - // beginning with 0xff in order to keep the number negative. - nMinus1 := new(big.Int).Neg(n) - nMinus1.Sub(nMinus1, bigOne) - bytes := nMinus1.Bytes() - for i := range bytes { - bytes[i] ^= 0xff - } - if len(bytes) == 0 || bytes[0]&0x80 == 0 { - to[0] = 0xff - to = to[1:] - length++ - } - nBytes := copy(to, bytes) - to = to[nBytes:] - length += nBytes - } else if n.Sign() == 0 { - // A zero is the zero length string - } else { - bytes := n.Bytes() - if len(bytes) > 0 && bytes[0]&0x80 != 0 { - // We'll have to pad this with a 0x00 in order to - // stop it looking like a negative number. - to[0] = 0 - to = to[1:] - length++ - } - nBytes := copy(to, bytes) - to = to[nBytes:] - length += nBytes - } - - lengthBytes[0] = byte(length >> 24) - lengthBytes[1] = byte(length >> 16) - lengthBytes[2] = byte(length >> 8) - lengthBytes[3] = byte(length) - return to -} - -func writeInt(w io.Writer, n *big.Int) { - length := intLength(n) - buf := make([]byte, length) - marshalInt(buf, n) - w.Write(buf) -} - -func writeString(w io.Writer, s []byte) { - var lengthBytes [4]byte - lengthBytes[0] = byte(len(s) >> 24) - lengthBytes[1] = byte(len(s) >> 16) - lengthBytes[2] = byte(len(s) >> 8) - lengthBytes[3] = byte(len(s)) - w.Write(lengthBytes[:]) - w.Write(s) -} - -func stringLength(s []byte) int { - return 4 + len(s) -} - -func marshalString(to []byte, s []byte) []byte { - to[0] = byte(len(s) >> 24) - to[1] = byte(len(s) >> 16) - to[2] = byte(len(s) >> 8) - to[3] = byte(len(s)) - to = to[4:] - copy(to, s) - return to[len(s):] -} - -var bigIntType = reflect.TypeOf((*big.Int)(nil)) - -// Decode a packet into it's corresponding message. -func decode(packet []byte) interface{} { - var msg interface{} - switch packet[0] { - case msgDisconnect: - msg = new(disconnectMsg) - case msgServiceRequest: - msg = new(serviceRequestMsg) - case msgServiceAccept: - msg = new(serviceAcceptMsg) - case msgKexInit: - msg = new(kexInitMsg) - case msgKexDHInit: - msg = new(kexDHInitMsg) - case msgKexDHReply: - msg = new(kexDHReplyMsg) - case msgUserAuthRequest: - msg = new(userAuthRequestMsg) - case msgUserAuthFailure: - msg = new(userAuthFailureMsg) - case msgUserAuthPubKeyOk: - msg = new(userAuthPubKeyOkMsg) - case msgGlobalRequest: - msg = new(globalRequestMsg) - case msgRequestSuccess: - msg = new(channelRequestSuccessMsg) - case msgRequestFailure: - msg = new(channelRequestFailureMsg) - case msgChannelOpen: - msg = new(channelOpenMsg) - case msgChannelOpenConfirm: - msg = new(channelOpenConfirmMsg) - case msgChannelOpenFailure: - msg = new(channelOpenFailureMsg) - case msgChannelWindowAdjust: - msg = new(windowAdjustMsg) - case msgChannelEOF: - msg = new(channelEOFMsg) - case msgChannelClose: - msg = new(channelCloseMsg) - case msgChannelRequest: - msg = new(channelRequestMsg) - case msgChannelSuccess: - msg = new(channelRequestSuccessMsg) - case msgChannelFailure: - msg = new(channelRequestFailureMsg) - default: - return UnexpectedMessageError{0, packet[0]} - } - if err := unmarshal(msg, packet, packet[0]); err != nil { - return err - } - return msg -} diff --git a/libgo/go/exp/ssh/messages_test.go b/libgo/go/exp/ssh/messages_test.go deleted file mode 100644 index fe4c397dc3a..00000000000 --- a/libgo/go/exp/ssh/messages_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "math/big" - "math/rand" - "reflect" - "testing" - "testing/quick" -) - -var intLengthTests = []struct { - val, length int -}{ - {0, 4 + 0}, - {1, 4 + 1}, - {127, 4 + 1}, - {128, 4 + 2}, - {-1, 4 + 1}, -} - -func TestIntLength(t *testing.T) { - for _, test := range intLengthTests { - v := new(big.Int).SetInt64(int64(test.val)) - length := intLength(v) - if length != test.length { - t.Errorf("For %d, got length %d but expected %d", test.val, length, test.length) - } - } -} - -var messageTypes = []interface{}{ - &kexInitMsg{}, - &kexDHInitMsg{}, - &serviceRequestMsg{}, - &serviceAcceptMsg{}, - &userAuthRequestMsg{}, - &channelOpenMsg{}, - &channelOpenConfirmMsg{}, - &channelRequestMsg{}, - &channelRequestSuccessMsg{}, -} - -func TestMarshalUnmarshal(t *testing.T) { - rand := rand.New(rand.NewSource(0)) - for i, iface := range messageTypes { - ty := reflect.ValueOf(iface).Type() - - n := 100 - if testing.Short() { - n = 5 - } - for j := 0; j < n; j++ { - v, ok := quick.Value(ty, rand) - if !ok { - t.Errorf("#%d: failed to create value", i) - break - } - - m1 := v.Elem().Interface() - m2 := iface - - marshaled := marshal(msgIgnore, m1) - if err := unmarshal(m2, marshaled, msgIgnore); err != nil { - t.Errorf("#%d failed to unmarshal %#v: %s", i, m1, err) - break - } - - if !reflect.DeepEqual(v.Interface(), m2) { - t.Errorf("#%d\ngot: %#v\nwant:%#v\n%x", i, m2, m1, marshaled) - break - } - } - } -} - -func randomBytes(out []byte, rand *rand.Rand) { - for i := 0; i < len(out); i++ { - out[i] = byte(rand.Int31()) - } -} - -func randomNameList(rand *rand.Rand) []string { - ret := make([]string, rand.Int31()&15) - for i := range ret { - s := make([]byte, 1+(rand.Int31()&15)) - for j := range s { - s[j] = 'a' + uint8(rand.Int31()&15) - } - ret[i] = string(s) - } - return ret -} - -func randomInt(rand *rand.Rand) *big.Int { - return new(big.Int).SetInt64(int64(int32(rand.Uint32()))) -} - -func (*kexInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { - ki := &kexInitMsg{} - randomBytes(ki.Cookie[:], rand) - ki.KexAlgos = randomNameList(rand) - ki.ServerHostKeyAlgos = randomNameList(rand) - ki.CiphersClientServer = randomNameList(rand) - ki.CiphersServerClient = randomNameList(rand) - ki.MACsClientServer = randomNameList(rand) - ki.MACsServerClient = randomNameList(rand) - ki.CompressionClientServer = randomNameList(rand) - ki.CompressionServerClient = randomNameList(rand) - ki.LanguagesClientServer = randomNameList(rand) - ki.LanguagesServerClient = randomNameList(rand) - if rand.Int31()&1 == 1 { - ki.FirstKexFollows = true - } - return reflect.ValueOf(ki) -} - -func (*kexDHInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { - dhi := &kexDHInitMsg{} - dhi.X = randomInt(rand) - return reflect.ValueOf(dhi) -} diff --git a/libgo/go/exp/ssh/server.go b/libgo/go/exp/ssh/server.go deleted file mode 100644 index 31011c66176..00000000000 --- a/libgo/go/exp/ssh/server.go +++ /dev/null @@ -1,676 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "errors" - "io" - "math/big" - "net" - "sync" -) - -type ServerConfig struct { - rsa *rsa.PrivateKey - rsaSerialized []byte - - // Rand provides the source of entropy for key exchange. If Rand is - // nil, the cryptographic random reader in package crypto/rand will - // be used. - Rand io.Reader - - // NoClientAuth is true if clients are allowed to connect without - // authenticating. - NoClientAuth bool - - // PasswordCallback, if non-nil, is called when a user attempts to - // authenticate using a password. It may be called concurrently from - // several goroutines. - PasswordCallback func(user, password string) bool - - // PublicKeyCallback, if non-nil, is called when a client attempts public - // key authentication. It must return true iff the given public key is - // valid for the given user. - PublicKeyCallback func(user, algo string, pubkey []byte) bool - - // Cryptographic-related configuration. - Crypto CryptoConfig -} - -func (c *ServerConfig) rand() io.Reader { - if c.Rand == nil { - return rand.Reader - } - return c.Rand -} - -// SetRSAPrivateKey sets the private key for a Server. A Server must have a -// private key configured in order to accept connections. The private key must -// be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa" -// typically contains such a key. -func (s *ServerConfig) SetRSAPrivateKey(pemBytes []byte) error { - block, _ := pem.Decode(pemBytes) - if block == nil { - return errors.New("ssh: no key found") - } - var err error - s.rsa, err = x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return err - } - - s.rsaSerialized = marshalRSA(s.rsa) - return nil -} - -// marshalRSA serializes an RSA private key according to RFC 4256, section 6.6. -func marshalRSA(priv *rsa.PrivateKey) []byte { - e := new(big.Int).SetInt64(int64(priv.E)) - length := stringLength([]byte(hostAlgoRSA)) - length += intLength(e) - length += intLength(priv.N) - - ret := make([]byte, length) - r := marshalString(ret, []byte(hostAlgoRSA)) - r = marshalInt(r, e) - r = marshalInt(r, priv.N) - - return ret -} - -// parseRSA parses an RSA key according to RFC 4256, section 6.6. -func parseRSA(in []byte) (pubKey *rsa.PublicKey, ok bool) { - algo, in, ok := parseString(in) - if !ok || string(algo) != hostAlgoRSA { - return nil, false - } - bigE, in, ok := parseInt(in) - if !ok || bigE.BitLen() > 24 { - return nil, false - } - e := bigE.Int64() - if e < 3 || e&1 == 0 { - return nil, false - } - N, in, ok := parseInt(in) - if !ok || len(in) > 0 { - return nil, false - } - return &rsa.PublicKey{ - N: N, - E: int(e), - }, true -} - -func parseRSASig(in []byte) (sig []byte, ok bool) { - algo, in, ok := parseString(in) - if !ok || string(algo) != hostAlgoRSA { - return nil, false - } - sig, in, ok = parseString(in) - if len(in) > 0 { - ok = false - } - return -} - -// cachedPubKey contains the results of querying whether a public key is -// acceptable for a user. The cache only applies to a single ServerConn. -type cachedPubKey struct { - user, algo string - pubKey []byte - result bool -} - -const maxCachedPubKeys = 16 - -// A ServerConn represents an incomming connection. -type ServerConn struct { - *transport - config *ServerConfig - - channels map[uint32]*channel - nextChanId uint32 - - // lock protects err and also allows Channels to serialise their writes - // to out. - lock sync.RWMutex - err error - - // cachedPubKeys contains the cache results of tests for public keys. - // Since SSH clients will query whether a public key is acceptable - // before attempting to authenticate with it, we end up with duplicate - // queries for public key validity. - cachedPubKeys []cachedPubKey -} - -// Server returns a new SSH server connection -// using c as the underlying transport. -func Server(c net.Conn, config *ServerConfig) *ServerConn { - conn := &ServerConn{ - transport: newTransport(c, config.rand()), - channels: make(map[uint32]*channel), - config: config, - } - return conn -} - -// kexDH performs Diffie-Hellman key agreement on a ServerConnection. The -// returned values are given the same names as in RFC 4253, section 8. -func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) (H, K []byte, err error) { - packet, err := s.readPacket() - if err != nil { - return - } - var kexDHInit kexDHInitMsg - if err = unmarshal(&kexDHInit, packet, msgKexDHInit); err != nil { - return - } - - if kexDHInit.X.Sign() == 0 || kexDHInit.X.Cmp(group.p) >= 0 { - return nil, nil, errors.New("client DH parameter out of bounds") - } - - y, err := rand.Int(s.config.rand(), group.p) - if err != nil { - return - } - - Y := new(big.Int).Exp(group.g, y, group.p) - kInt := new(big.Int).Exp(kexDHInit.X, y, group.p) - - var serializedHostKey []byte - switch hostKeyAlgo { - case hostAlgoRSA: - serializedHostKey = s.config.rsaSerialized - default: - return nil, nil, errors.New("internal error") - } - - h := hashFunc.New() - writeString(h, magics.clientVersion) - writeString(h, magics.serverVersion) - writeString(h, magics.clientKexInit) - writeString(h, magics.serverKexInit) - writeString(h, serializedHostKey) - writeInt(h, kexDHInit.X) - writeInt(h, Y) - K = make([]byte, intLength(kInt)) - marshalInt(K, kInt) - h.Write(K) - - H = h.Sum(nil) - - h.Reset() - h.Write(H) - hh := h.Sum(nil) - - var sig []byte - switch hostKeyAlgo { - case hostAlgoRSA: - sig, err = rsa.SignPKCS1v15(s.config.rand(), s.config.rsa, hashFunc, hh) - if err != nil { - return - } - default: - return nil, nil, errors.New("internal error") - } - - serializedSig := serializeSignature(hostAlgoRSA, sig) - - kexDHReply := kexDHReplyMsg{ - HostKey: serializedHostKey, - Y: Y, - Signature: serializedSig, - } - packet = marshal(msgKexDHReply, kexDHReply) - - err = s.writePacket(packet) - return -} - -// serverVersion is the fixed identification string that Server will use. -var serverVersion = []byte("SSH-2.0-Go\r\n") - -// Handshake performs an SSH transport and client authentication on the given ServerConn. -func (s *ServerConn) Handshake() error { - var magics handshakeMagics - if _, err := s.Write(serverVersion); err != nil { - return err - } - if err := s.Flush(); err != nil { - return err - } - magics.serverVersion = serverVersion[:len(serverVersion)-2] - - version, err := readVersion(s) - if err != nil { - return err - } - magics.clientVersion = version - - serverKexInit := kexInitMsg{ - KexAlgos: supportedKexAlgos, - ServerHostKeyAlgos: supportedHostKeyAlgos, - CiphersClientServer: s.config.Crypto.ciphers(), - CiphersServerClient: s.config.Crypto.ciphers(), - MACsClientServer: supportedMACs, - MACsServerClient: supportedMACs, - CompressionClientServer: supportedCompressions, - CompressionServerClient: supportedCompressions, - } - kexInitPacket := marshal(msgKexInit, serverKexInit) - magics.serverKexInit = kexInitPacket - - if err := s.writePacket(kexInitPacket); err != nil { - return err - } - - packet, err := s.readPacket() - if err != nil { - return err - } - - magics.clientKexInit = packet - - var clientKexInit kexInitMsg - if err = unmarshal(&clientKexInit, packet, msgKexInit); err != nil { - return err - } - - kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(s.transport, &clientKexInit, &serverKexInit) - if !ok { - return errors.New("ssh: no common algorithms") - } - - if clientKexInit.FirstKexFollows && kexAlgo != clientKexInit.KexAlgos[0] { - // The client sent a Kex message for the wrong algorithm, - // which we have to ignore. - if _, err := s.readPacket(); err != nil { - return err - } - } - - var H, K []byte - var hashFunc crypto.Hash - switch kexAlgo { - case kexAlgoDH14SHA1: - hashFunc = crypto.SHA1 - dhGroup14Once.Do(initDHGroup14) - H, K, err = s.kexDH(dhGroup14, hashFunc, &magics, hostKeyAlgo) - default: - err = errors.New("ssh: unexpected key exchange algorithm " + kexAlgo) - } - if err != nil { - return err - } - - if err = s.writePacket([]byte{msgNewKeys}); err != nil { - return err - } - if err = s.transport.writer.setupKeys(serverKeys, K, H, H, hashFunc); err != nil { - return err - } - if packet, err = s.readPacket(); err != nil { - return err - } - - if packet[0] != msgNewKeys { - return UnexpectedMessageError{msgNewKeys, packet[0]} - } - if err = s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc); err != nil { - return err - } - if packet, err = s.readPacket(); err != nil { - return err - } - - var serviceRequest serviceRequestMsg - if err = unmarshal(&serviceRequest, packet, msgServiceRequest); err != nil { - return err - } - if serviceRequest.Service != serviceUserAuth { - return errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating") - } - serviceAccept := serviceAcceptMsg{ - Service: serviceUserAuth, - } - if err = s.writePacket(marshal(msgServiceAccept, serviceAccept)); err != nil { - return err - } - - if err = s.authenticate(H); err != nil { - return err - } - return nil -} - -func isAcceptableAlgo(algo string) bool { - return algo == hostAlgoRSA -} - -// testPubKey returns true if the given public key is acceptable for the user. -func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool { - if s.config.PublicKeyCallback == nil || !isAcceptableAlgo(algo) { - return false - } - - for _, c := range s.cachedPubKeys { - if c.user == user && c.algo == algo && bytes.Equal(c.pubKey, pubKey) { - return c.result - } - } - - result := s.config.PublicKeyCallback(user, algo, pubKey) - if len(s.cachedPubKeys) < maxCachedPubKeys { - c := cachedPubKey{ - user: user, - algo: algo, - pubKey: make([]byte, len(pubKey)), - result: result, - } - copy(c.pubKey, pubKey) - s.cachedPubKeys = append(s.cachedPubKeys, c) - } - - return result -} - -func (s *ServerConn) authenticate(H []byte) error { - var userAuthReq userAuthRequestMsg - var err error - var packet []byte - -userAuthLoop: - for { - if packet, err = s.readPacket(); err != nil { - return err - } - if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); err != nil { - return err - } - - if userAuthReq.Service != serviceSSH { - return errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) - } - - switch userAuthReq.Method { - case "none": - if s.config.NoClientAuth { - break userAuthLoop - } - case "password": - if s.config.PasswordCallback == nil { - break - } - payload := userAuthReq.Payload - if len(payload) < 1 || payload[0] != 0 { - return ParseError{msgUserAuthRequest} - } - payload = payload[1:] - password, payload, ok := parseString(payload) - if !ok || len(payload) > 0 { - return ParseError{msgUserAuthRequest} - } - - if s.config.PasswordCallback(userAuthReq.User, string(password)) { - break userAuthLoop - } - case "publickey": - if s.config.PublicKeyCallback == nil { - break - } - payload := userAuthReq.Payload - if len(payload) < 1 { - return ParseError{msgUserAuthRequest} - } - isQuery := payload[0] == 0 - payload = payload[1:] - algoBytes, payload, ok := parseString(payload) - if !ok { - return ParseError{msgUserAuthRequest} - } - algo := string(algoBytes) - - pubKey, payload, ok := parseString(payload) - if !ok { - return ParseError{msgUserAuthRequest} - } - if isQuery { - // The client can query if the given public key - // would be ok. - if len(payload) > 0 { - return ParseError{msgUserAuthRequest} - } - if s.testPubKey(userAuthReq.User, algo, pubKey) { - okMsg := userAuthPubKeyOkMsg{ - Algo: algo, - PubKey: string(pubKey), - } - if err = s.writePacket(marshal(msgUserAuthPubKeyOk, okMsg)); err != nil { - return err - } - continue userAuthLoop - } - } else { - sig, payload, ok := parseString(payload) - if !ok || len(payload) > 0 { - return ParseError{msgUserAuthRequest} - } - if !isAcceptableAlgo(algo) { - break - } - rsaSig, ok := parseRSASig(sig) - if !ok { - return ParseError{msgUserAuthRequest} - } - signedData := buildDataSignedForAuth(H, userAuthReq, algoBytes, pubKey) - switch algo { - case hostAlgoRSA: - hashFunc := crypto.SHA1 - h := hashFunc.New() - h.Write(signedData) - digest := h.Sum(nil) - rsaKey, ok := parseRSA(pubKey) - if !ok { - return ParseError{msgUserAuthRequest} - } - if rsa.VerifyPKCS1v15(rsaKey, hashFunc, digest, rsaSig) != nil { - return ParseError{msgUserAuthRequest} - } - default: - return errors.New("ssh: isAcceptableAlgo incorrect") - } - if s.testPubKey(userAuthReq.User, algo, pubKey) { - break userAuthLoop - } - } - } - - var failureMsg userAuthFailureMsg - if s.config.PasswordCallback != nil { - failureMsg.Methods = append(failureMsg.Methods, "password") - } - if s.config.PublicKeyCallback != nil { - failureMsg.Methods = append(failureMsg.Methods, "publickey") - } - - if len(failureMsg.Methods) == 0 { - return errors.New("ssh: no authentication methods configured but NoClientAuth is also false") - } - - if err = s.writePacket(marshal(msgUserAuthFailure, failureMsg)); err != nil { - return err - } - } - - packet = []byte{msgUserAuthSuccess} - if err = s.writePacket(packet); err != nil { - return err - } - - return nil -} - -const defaultWindowSize = 32768 - -// Accept reads and processes messages on a ServerConn. It must be called -// in order to demultiplex messages to any resulting Channels. -func (s *ServerConn) Accept() (Channel, error) { - if s.err != nil { - return nil, s.err - } - - for { - packet, err := s.readPacket() - if err != nil { - - s.lock.Lock() - s.err = err - s.lock.Unlock() - - for _, c := range s.channels { - c.dead = true - c.handleData(nil) - } - - 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) { - case *channelOpenMsg: - c := new(channel) - c.chanType = msg.ChanType - c.theirId = msg.PeersId - c.theirWindow = msg.PeersWindow - c.maxPacketSize = msg.MaxPacketSize - c.extraData = msg.TypeSpecificData - c.myWindow = defaultWindowSize - c.serverConn = s - c.cond = sync.NewCond(&c.lock) - c.pendingData = make([]byte, c.myWindow) - - s.lock.Lock() - c.myId = s.nextChanId - s.nextChanId++ - s.channels[c.myId] = c - s.lock.Unlock() - return c, nil - - case *channelRequestMsg: - s.lock.Lock() - c, ok := s.channels[msg.PeersId] - if !ok { - s.lock.Unlock() - continue - } - c.handlePacket(msg) - s.lock.Unlock() - - case *channelEOFMsg: - s.lock.Lock() - c, ok := s.channels[msg.PeersId] - if !ok { - s.lock.Unlock() - continue - } - c.handlePacket(msg) - s.lock.Unlock() - - case *channelCloseMsg: - s.lock.Lock() - c, ok := s.channels[msg.PeersId] - if !ok { - s.lock.Unlock() - continue - } - c.handlePacket(msg) - s.lock.Unlock() - - case *globalRequestMsg: - if msg.WantReply { - if err := s.writePacket([]byte{msgRequestFailure}); err != nil { - return nil, err - } - } - - case UnexpectedMessageError: - return nil, msg - case *disconnectMsg: - return nil, io.EOF - default: - // Unknown message. Ignore. - } - } - } - - panic("unreachable") -} - -// A Listener implements a network listener (net.Listener) for SSH connections. -type Listener struct { - listener net.Listener - config *ServerConfig -} - -// Accept waits for and returns the next incoming SSH connection. -// The receiver should call Handshake() in another goroutine -// to avoid blocking the accepter. -func (l *Listener) Accept() (*ServerConn, error) { - c, err := l.listener.Accept() - if err != nil { - return nil, err - } - conn := Server(c, l.config) - return conn, nil -} - -// Addr returns the listener's network address. -func (l *Listener) Addr() net.Addr { - return l.listener.Addr() -} - -// Close closes the listener. -func (l *Listener) Close() error { - return l.listener.Close() -} - -// Listen creates an SSH listener accepting connections on -// the given network address using net.Listen. -func Listen(network, addr string, config *ServerConfig) (*Listener, error) { - l, err := net.Listen(network, addr) - if err != nil { - return nil, err - } - return &Listener{ - l, - config, - }, nil -} diff --git a/libgo/go/exp/ssh/server_terminal.go b/libgo/go/exp/ssh/server_terminal.go deleted file mode 100644 index 708a9159ec8..00000000000 --- a/libgo/go/exp/ssh/server_terminal.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// A Terminal is capable of parsing and generating virtual terminal -// data from an SSH client. -type Terminal interface { - ReadLine() (line string, err error) - SetSize(x, y int) - Write([]byte) (int, error) -} - -// ServerTerminal contains the state for running a terminal that is capable of -// reading lines of input. -type ServerTerminal struct { - Term Terminal - Channel Channel -} - -// parsePtyRequest parses the payload of the pty-req message and extracts the -// dimensions of the terminal. See RFC 4254, section 6.2. -func parsePtyRequest(s []byte) (width, height int, ok bool) { - _, s, ok = parseString(s) - if !ok { - return - } - width32, s, ok := parseUint32(s) - if !ok { - return - } - height32, _, ok := parseUint32(s) - width = int(width32) - height = int(height32) - if width < 1 { - ok = false - } - if height < 1 { - ok = false - } - return -} - -func (ss *ServerTerminal) Write(buf []byte) (n int, err error) { - return ss.Term.Write(buf) -} - -// ReadLine returns a line of input from the terminal. -func (ss *ServerTerminal) ReadLine() (line string, err error) { - for { - if line, err = ss.Term.ReadLine(); err == nil { - return - } - - req, ok := err.(ChannelRequest) - if !ok { - return - } - - ok = false - switch req.Request { - case "pty-req": - var width, height int - width, height, ok = parsePtyRequest(req.Payload) - ss.Term.SetSize(width, height) - case "shell": - ok = true - if len(req.Payload) > 0 { - // We don't accept any commands, only the default shell. - ok = false - } - case "env": - ok = true - } - if req.WantReply { - ss.Channel.AckRequest(ok) - } - } - panic("unreachable") -} diff --git a/libgo/go/exp/ssh/session.go b/libgo/go/exp/ssh/session.go deleted file mode 100644 index ea4addbd50b..00000000000 --- a/libgo/go/exp/ssh/session.go +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// Session implements an interactive session described in -// "RFC 4254, section 6". - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" -) - -type Signal string - -// POSIX signals as listed in RFC 4254 Section 6.10. -const ( - SIGABRT Signal = "ABRT" - SIGALRM Signal = "ALRM" - SIGFPE Signal = "FPE" - SIGHUP Signal = "HUP" - SIGILL Signal = "ILL" - SIGINT Signal = "INT" - SIGKILL Signal = "KILL" - SIGPIPE Signal = "PIPE" - SIGQUIT Signal = "QUIT" - SIGSEGV Signal = "SEGV" - SIGTERM Signal = "TERM" - SIGUSR1 Signal = "USR1" - SIGUSR2 Signal = "USR2" -) - -var signals = map[Signal]int{ - SIGABRT: 6, - SIGALRM: 14, - SIGFPE: 8, - SIGHUP: 1, - SIGILL: 4, - SIGINT: 2, - SIGKILL: 9, - SIGPIPE: 13, - SIGQUIT: 3, - SIGSEGV: 11, - SIGTERM: 15, -} - -// A Session represents a connection to a remote command or shell. -type Session struct { - // Stdin specifies the remote process's standard input. - // If Stdin is nil, the remote process reads from an empty - // bytes.Buffer. - Stdin io.Reader - - // Stdout and Stderr specify the remote process's standard - // output and error. - // - // If either is nil, Run connects the corresponding file - // descriptor to an instance of ioutil.Discard. There is a - // fixed amount of buffering that is shared for the two streams. - // If either blocks it may eventually cause the remote - // command to block. - Stdout io.Writer - Stderr io.Writer - - *clientChan // the channel backing this session - - started bool // true once Start, Run or Shell is invoked. - copyFuncs []func() error - errors chan error // one send per copyFunc - - // true if pipe method is active - stdinpipe, stdoutpipe, stderrpipe bool -} - -// RFC 4254 Section 6.4. -type setenvRequest struct { - PeersId uint32 - Request string - WantReply bool - Name string - Value string -} - -// Setenv sets an environment variable that will be applied to any -// command executed by Shell or Run. -func (s *Session) Setenv(name, value string) error { - req := setenvRequest{ - PeersId: s.peersId, - Request: "env", - WantReply: true, - Name: name, - Value: value, - } - if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { - return err - } - return s.waitForResponse() -} - -// An empty mode list, see RFC 4254 Section 8. -var emptyModelist = "\x00" - -// RFC 4254 Section 6.2. -type ptyRequestMsg struct { - PeersId uint32 - Request string - WantReply bool - Term string - Columns uint32 - Rows uint32 - Width uint32 - Height uint32 - Modelist string -} - -// RequestPty requests the association of a pty with the session on the remote host. -func (s *Session) RequestPty(term string, h, w int) error { - req := ptyRequestMsg{ - PeersId: s.peersId, - Request: "pty-req", - WantReply: true, - Term: term, - Columns: uint32(w), - Rows: uint32(h), - Width: uint32(w * 8), - Height: uint32(h * 8), - Modelist: emptyModelist, - } - if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { - return err - } - return s.waitForResponse() -} - -// RFC 4254 Section 6.9. -type signalMsg struct { - PeersId uint32 - Request string - WantReply bool - Signal string -} - -// Signal sends the given signal to the remote process. -// sig is one of the SIG* constants. -func (s *Session) Signal(sig Signal) error { - req := signalMsg{ - PeersId: s.peersId, - Request: "signal", - WantReply: false, - Signal: string(sig), - } - return s.writePacket(marshal(msgChannelRequest, req)) -} - -// RFC 4254 Section 6.5. -type execMsg struct { - PeersId uint32 - Request string - WantReply bool - Command string -} - -// Start runs cmd on the remote host. Typically, the remote -// server passes cmd to the shell for interpretation. -// A Session only accepts one call to Run, Start or Shell. -func (s *Session) Start(cmd string) error { - if s.started { - return errors.New("ssh: session already started") - } - req := execMsg{ - PeersId: s.peersId, - Request: "exec", - WantReply: true, - Command: cmd, - } - if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { - return err - } - if err := s.waitForResponse(); err != nil { - return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err) - } - return s.start() -} - -// Run runs cmd on the remote host. Typically, the remote -// server passes cmd to the shell for interpretation. -// A Session only accepts one call to Run, Start or Shell. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -func (s *Session) Run(cmd string) error { - err := s.Start(cmd) - if err != nil { - return err - } - return s.Wait() -} - -// Shell starts a login shell on the remote host. A Session only -// accepts one call to Run, Start or Shell. -func (s *Session) Shell() error { - if s.started { - return errors.New("ssh: session already started") - } - req := channelRequestMsg{ - PeersId: s.peersId, - Request: "shell", - WantReply: true, - } - if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { - return err - } - if err := s.waitForResponse(); err != nil { - return fmt.Errorf("ssh: cound not execute shell: %v", err) - } - return s.start() -} - -func (s *Session) waitForResponse() error { - msg := <-s.msg - switch msg.(type) { - case *channelRequestSuccessMsg: - return nil - case *channelRequestFailureMsg: - return errors.New("request failed") - } - return fmt.Errorf("unknown packet %T received: %v", msg, msg) -} - -func (s *Session) start() error { - s.started = true - - type F func(*Session) - for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} { - setupFd(s) - } - - s.errors = make(chan error, len(s.copyFuncs)) - for _, fn := range s.copyFuncs { - go func(fn func() error) { - s.errors <- fn() - }(fn) - } - return nil -} - -// Wait waits for the remote command to exit. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -func (s *Session) Wait() error { - if !s.started { - return errors.New("ssh: session not started") - } - waitErr := s.wait() - - var copyError error - for _ = range s.copyFuncs { - if err := <-s.errors; err != nil && copyError == nil { - copyError = err - } - } - if waitErr != nil { - return waitErr - } - return copyError -} - -func (s *Session) wait() error { - wm := Waitmsg{status: -1} - - // Wait for msg channel to be closed before returning. - for msg := range s.msg { - switch msg := msg.(type) { - case *channelRequestMsg: - switch msg.Request { - case "exit-status": - d := msg.RequestSpecificData - wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3]) - case "exit-signal": - signal, rest, ok := parseString(msg.RequestSpecificData) - if !ok { - return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) - } - wm.signal = safeString(string(signal)) - - // skip coreDumped bool - if len(rest) == 0 { - return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) - } - rest = rest[1:] - - errmsg, rest, ok := parseString(rest) - if !ok { - return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) - } - wm.msg = safeString(string(errmsg)) - - lang, _, ok := parseString(rest) - if !ok { - return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) - } - wm.lang = safeString(string(lang)) - default: - return fmt.Errorf("wait: unexpected channel request: %v", msg) - } - default: - return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg) - } - } - if wm.status == 0 { - return nil - } - if wm.status == -1 { - // exit-status was never sent from server - if wm.signal == "" { - return errors.New("wait: remote command exited without exit status or exit signal") - } - wm.status = 128 - if _, ok := signals[Signal(wm.signal)]; ok { - wm.status += signals[Signal(wm.signal)] - } - } - return &ExitError{wm} -} - -func (s *Session) stdin() { - if s.stdinpipe { - return - } - if s.Stdin == nil { - s.Stdin = new(bytes.Buffer) - } - s.copyFuncs = append(s.copyFuncs, func() error { - _, err := io.Copy(s.clientChan.stdin, s.Stdin) - if err1 := s.clientChan.stdin.Close(); err == nil { - err = err1 - } - return err - }) -} - -func (s *Session) stdout() { - if s.stdoutpipe { - return - } - if s.Stdout == nil { - s.Stdout = ioutil.Discard - } - s.copyFuncs = append(s.copyFuncs, func() error { - _, err := io.Copy(s.Stdout, s.clientChan.stdout) - return err - }) -} - -func (s *Session) stderr() { - if s.stderrpipe { - return - } - if s.Stderr == nil { - s.Stderr = ioutil.Discard - } - s.copyFuncs = append(s.copyFuncs, func() error { - _, err := io.Copy(s.Stderr, s.clientChan.stderr) - return err - }) -} - -// StdinPipe returns a pipe that will be connected to the -// remote command's standard input when the command starts. -func (s *Session) StdinPipe() (io.WriteCloser, error) { - if s.Stdin != nil { - return nil, errors.New("ssh: Stdin already set") - } - if s.started { - return nil, errors.New("ssh: StdinPipe after process started") - } - s.stdinpipe = true - return s.clientChan.stdin, nil -} - -// StdoutPipe returns a pipe that will be connected to the -// remote command's standard output when the command starts. -// There is a fixed amount of buffering that is shared between -// stdout and stderr streams. If the StdoutPipe reader is -// not serviced fast enought it may eventually cause the -// remote command to block. -func (s *Session) StdoutPipe() (io.Reader, error) { - if s.Stdout != nil { - return nil, errors.New("ssh: Stdout already set") - } - if s.started { - return nil, errors.New("ssh: StdoutPipe after process started") - } - s.stdoutpipe = true - return s.clientChan.stdout, nil -} - -// StderrPipe returns a pipe that will be connected to the -// remote command's standard error when the command starts. -// There is a fixed amount of buffering that is shared between -// stdout and stderr streams. If the StderrPipe reader is -// not serviced fast enought it may eventually cause the -// remote command to block. -func (s *Session) StderrPipe() (io.Reader, error) { - if s.Stderr != nil { - return nil, errors.New("ssh: Stderr already set") - } - if s.started { - return nil, errors.New("ssh: StderrPipe after process started") - } - s.stderrpipe = true - return s.clientChan.stderr, nil -} - -// TODO(dfc) add Output and CombinedOutput helpers - -// NewSession returns a new interactive session on the remote host. -func (c *ClientConn) NewSession() (*Session, error) { - ch := c.newChan(c.transport) - if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{ - ChanType: "session", - PeersId: ch.id, - PeersWindow: 1 << 14, - MaxPacketSize: 1 << 15, // RFC 4253 6.1 - })); err != nil { - c.chanlist.remove(ch.id) - return nil, err - } - if err := ch.waitForChannelOpenResponse(); err != nil { - c.chanlist.remove(ch.id) - return nil, fmt.Errorf("ssh: unable to open session: %v", err) - } - return &Session{ - clientChan: ch, - }, nil -} - -// An ExitError reports unsuccessful completion of a remote command. -type ExitError struct { - Waitmsg -} - -func (e *ExitError) Error() string { - return e.Waitmsg.String() -} - -// Waitmsg stores the information about an exited remote command -// as reported by Wait. -type Waitmsg struct { - status int - signal string - msg string - lang string -} - -// ExitStatus returns the exit status of the remote command. -func (w Waitmsg) ExitStatus() int { - return w.status -} - -// Signal returns the exit signal of the remote command if -// it was terminated violently. -func (w Waitmsg) Signal() string { - return w.signal -} - -// Msg returns the exit message given by the remote command -func (w Waitmsg) Msg() string { - return w.msg -} - -// Lang returns the language tag. See RFC 3066 -func (w Waitmsg) Lang() string { - return w.lang -} - -func (w Waitmsg) String() string { - return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal) -} diff --git a/libgo/go/exp/ssh/session_test.go b/libgo/go/exp/ssh/session_test.go deleted file mode 100644 index 4a3d22bee04..00000000000 --- a/libgo/go/exp/ssh/session_test.go +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// Session tests. - -import ( - "bytes" - "exp/terminal" - "io" - "testing" -) - -type serverType func(*channel) - -// dial constructs a new test server and returns a *ClientConn. -func dial(handler serverType, t *testing.T) *ClientConn { - pw := password("tiger") - serverConfig.PasswordCallback = func(user, pass string) bool { - return user == "testuser" && pass == string(pw) - } - serverConfig.PublicKeyCallback = nil - - l, err := Listen("tcp", "127.0.0.1:0", serverConfig) - if err != nil { - t.Fatalf("unable to listen: %s", err) - } - go func() { - defer l.Close() - conn, err := l.Accept() - if err != nil { - t.Errorf("Unable to accept: %v", err) - return - } - defer conn.Close() - if err := conn.Handshake(); err != nil { - t.Errorf("Unable to handshake: %v", err) - return - } - for { - ch, err := conn.Accept() - if err == io.EOF { - return - } - if err != nil { - t.Errorf("Unable to accept incoming channel request: %v", err) - return - } - if ch.ChannelType() != "session" { - ch.Reject(UnknownChannelType, "unknown channel type") - continue - } - ch.Accept() - go handler(ch.(*channel)) - } - t.Log("done") - }() - - config := &ClientConfig{ - User: "testuser", - Auth: []ClientAuth{ - ClientAuthPassword(pw), - }, - } - - c, err := Dial("tcp", l.Addr().String(), config) - if err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } - return c -} - -// Test a simple string is returned to session.Stdout. -func TestSessionShell(t *testing.T) { - conn := dial(shellHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - stdout := new(bytes.Buffer) - session.Stdout = stdout - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - if err := session.Wait(); err != nil { - t.Fatalf("Remote command did not exit cleanly: %s", err) - } - actual := stdout.String() - if actual != "golang" { - t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) - } -} - -// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it. - -// Test a simple string is returned via StdoutPipe. -func TestSessionStdoutPipe(t *testing.T) { - conn := dial(shellHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - stdout, err := session.StdoutPipe() - if err != nil { - t.Fatalf("Unable to request StdoutPipe(): %v", err) - } - var buf bytes.Buffer - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - done := make(chan bool, 1) - go func() { - if _, err := io.Copy(&buf, stdout); err != nil { - t.Errorf("Copy of stdout failed: %v", err) - } - done <- true - }() - if err := session.Wait(); err != nil { - t.Fatalf("Remote command did not exit cleanly: %s", err) - } - <-done - actual := buf.String() - if actual != "golang" { - t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) - } -} - -// Test non-0 exit status is returned correctly. -func TestExitStatusNonZero(t *testing.T) { - conn := dial(exitStatusNonZeroHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.ExitStatus() != 15 { - t.Fatalf("expected command to exit with 15 but got %s", e.ExitStatus()) - } -} - -// Test 0 exit status is returned correctly. -func TestExitStatusZero(t *testing.T) { - conn := dial(exitStatusZeroHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - err = session.Wait() - if err != nil { - t.Fatalf("expected nil but got %s", err) - } -} - -// Test exit signal and status are both returned correctly. -func TestExitSignalAndStatus(t *testing.T) { - conn := dial(exitSignalAndStatusHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.Signal() != "TERM" || e.ExitStatus() != 15 { - t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus()) - } -} - -// Test exit signal and status are both returned correctly. -func TestKnownExitSignalOnly(t *testing.T) { - conn := dial(exitSignalHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.Signal() != "TERM" || e.ExitStatus() != 143 { - t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus()) - } -} - -// Test exit signal and status are both returned correctly. -func TestUnknownExitSignal(t *testing.T) { - conn := dial(exitSignalUnknownHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.Signal() != "SYS" || e.ExitStatus() != 128 { - t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus()) - } -} - -// Test WaitMsg is not returned if the channel closes abruptly. -func TestExitWithoutStatusOrSignal(t *testing.T) { - conn := dial(exitWithoutSignalOrStatus, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %s", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - _, ok := err.(*ExitError) - if ok { - // you can't actually test for errors.errorString - // because it's not exported. - t.Fatalf("expected *errorString but got %T", err) - } -} - -type exitStatusMsg struct { - PeersId uint32 - Request string - WantReply bool - Status uint32 -} - -type exitSignalMsg struct { - PeersId uint32 - Request string - WantReply bool - Signal string - CoreDumped bool - Errmsg string - Lang string -} - -func newServerShell(ch *channel, prompt string) *ServerTerminal { - term := terminal.NewTerminal(ch, prompt) - return &ServerTerminal{ - Term: term, - Channel: ch, - } -} - -func exitStatusZeroHandler(ch *channel) { - defer ch.Close() - // this string is returned to stdout - shell := newServerShell(ch, "> ") - shell.ReadLine() - sendStatus(0, ch) -} - -func exitStatusNonZeroHandler(ch *channel) { - defer ch.Close() - shell := newServerShell(ch, "> ") - shell.ReadLine() - sendStatus(15, ch) -} - -func exitSignalAndStatusHandler(ch *channel) { - defer ch.Close() - shell := newServerShell(ch, "> ") - shell.ReadLine() - sendStatus(15, ch) - sendSignal("TERM", ch) -} - -func exitSignalHandler(ch *channel) { - defer ch.Close() - shell := newServerShell(ch, "> ") - shell.ReadLine() - sendSignal("TERM", ch) -} - -func exitSignalUnknownHandler(ch *channel) { - defer ch.Close() - shell := newServerShell(ch, "> ") - shell.ReadLine() - sendSignal("SYS", ch) -} - -func exitWithoutSignalOrStatus(ch *channel) { - defer ch.Close() - shell := newServerShell(ch, "> ") - shell.ReadLine() -} - -func shellHandler(ch *channel) { - defer ch.Close() - // this string is returned to stdout - shell := newServerShell(ch, "golang") - shell.ReadLine() - sendStatus(0, ch) -} - -func sendStatus(status uint32, ch *channel) { - msg := exitStatusMsg{ - PeersId: ch.theirId, - Request: "exit-status", - WantReply: false, - Status: status, - } - ch.serverConn.writePacket(marshal(msgChannelRequest, msg)) -} - -func sendSignal(signal string, ch *channel) { - sig := exitSignalMsg{ - PeersId: ch.theirId, - Request: "exit-signal", - WantReply: false, - Signal: signal, - CoreDumped: false, - Errmsg: "Process terminated", - Lang: "en-GB-oed", - } - ch.serverConn.writePacket(marshal(msgChannelRequest, sig)) -} diff --git a/libgo/go/exp/ssh/tcpip.go b/libgo/go/exp/ssh/tcpip.go deleted file mode 100644 index e0c47bca1fc..00000000000 --- a/libgo/go/exp/ssh/tcpip.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "errors" - "fmt" - "io" - "net" - "time" -) - -// Dial initiates a connection to the addr from the remote host. -// addr is resolved using net.ResolveTCPAddr before connection. -// This could allow an observer to observe the DNS name of the -// remote host. Consider using ssh.DialTCP to avoid this. -func (c *ClientConn) Dial(n, addr string) (net.Conn, error) { - raddr, err := net.ResolveTCPAddr(n, addr) - if err != nil { - return nil, err - } - return c.DialTCP(n, nil, raddr) -} - -// DialTCP connects to the remote address raddr on the network net, -// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used -// as the local address for the connection. -func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { - if laddr == nil { - laddr = &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } - } - ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) - if err != nil { - return nil, err - } - return &tcpchanconn{ - tcpchan: ch, - laddr: laddr, - raddr: raddr, - }, nil -} - -// RFC 4254 7.2 -type channelOpenDirectMsg struct { - ChanType string - PeersId uint32 - PeersWindow uint32 - MaxPacketSize uint32 - raddr string - rport uint32 - laddr string - lport uint32 -} - -// dial opens a direct-tcpip connection to the remote server. laddr and raddr are passed as -// strings and are expected to be resolveable at the remote end. -func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tcpchan, error) { - ch := c.newChan(c.transport) - if err := c.writePacket(marshal(msgChannelOpen, channelOpenDirectMsg{ - ChanType: "direct-tcpip", - PeersId: ch.id, - PeersWindow: 1 << 14, - MaxPacketSize: 1 << 15, // RFC 4253 6.1 - raddr: raddr, - rport: uint32(rport), - laddr: laddr, - lport: uint32(lport), - })); err != nil { - c.chanlist.remove(ch.id) - return nil, err - } - if err := ch.waitForChannelOpenResponse(); err != nil { - c.chanlist.remove(ch.id) - return nil, fmt.Errorf("ssh: unable to open direct tcpip connection: %v", err) - } - return &tcpchan{ - clientChan: ch, - Reader: ch.stdout, - Writer: ch.stdin, - }, nil -} - -type tcpchan struct { - *clientChan // the backing channel - io.Reader - io.Writer -} - -// tcpchanconn fulfills the net.Conn interface without -// the tcpchan having to hold laddr or raddr directly. -type tcpchanconn struct { - *tcpchan - laddr, raddr net.Addr -} - -// LocalAddr returns the local network address. -func (t *tcpchanconn) LocalAddr() net.Addr { - return t.laddr -} - -// RemoteAddr returns the remote network address. -func (t *tcpchanconn) RemoteAddr() net.Addr { - return t.raddr -} - -// SetDeadline sets the read and write deadlines associated -// with the connection. -func (t *tcpchanconn) SetDeadline(deadline time.Time) error { - if err := t.SetReadDeadline(deadline); err != nil { - return err - } - return t.SetWriteDeadline(deadline) -} - -// SetReadDeadline sets the read deadline. -// A zero value for t means Read will not time out. -// After the deadline, the error from Read will implement net.Error -// with Timeout() == true. -func (t *tcpchanconn) SetReadDeadline(deadline time.Time) error { - return errors.New("ssh: tcpchan: deadline not supported") -} - -// SetWriteDeadline exists to satisfy the net.Conn interface -// but is not implemented by this type. It always returns an error. -func (t *tcpchanconn) SetWriteDeadline(deadline time.Time) error { - return errors.New("ssh: tcpchan: deadline not supported") -} diff --git a/libgo/go/exp/ssh/tcpip_func_test.go b/libgo/go/exp/ssh/tcpip_func_test.go deleted file mode 100644 index 261297241e9..00000000000 --- a/libgo/go/exp/ssh/tcpip_func_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// direct-tcpip functional tests - -import ( - "net" - "net/http" - "testing" -) - -func TestTCPIPHTTP(t *testing.T) { - if *sshuser == "" { - t.Log("ssh.user not defined, skipping test") - return - } - // google.com will generate at least one redirect, possibly three - // depending on your location. - doTest(t, "http://google.com") -} - -func TestTCPIPHTTPS(t *testing.T) { - if *sshuser == "" { - t.Log("ssh.user not defined, skipping test") - return - } - doTest(t, "https://encrypted.google.com/") -} - -func doTest(t *testing.T, url string) { - config := &ClientConfig{ - User: *sshuser, - Auth: []ClientAuth{ - ClientAuthPassword(password(*sshpass)), - }, - } - conn, err := Dial("tcp", "localhost:22", config) - if err != nil { - t.Fatalf("Unable to connect: %s", err) - } - defer conn.Close() - tr := &http.Transport{ - Dial: func(n, addr string) (net.Conn, error) { - return conn.Dial(n, addr) - }, - } - client := &http.Client{ - Transport: tr, - } - resp, err := client.Get(url) - if err != nil { - t.Fatalf("unable to proxy: %s", err) - } - // got a body without error - t.Log(resp) -} diff --git a/libgo/go/exp/ssh/transport.go b/libgo/go/exp/ssh/transport.go deleted file mode 100644 index e21bc4ba202..00000000000 --- a/libgo/go/exp/ssh/transport.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bufio" - "crypto" - "crypto/cipher" - "crypto/hmac" - "crypto/sha1" - "crypto/subtle" - "errors" - "hash" - "io" - "net" - "sync" -) - -const ( - packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. - minPacketSize = 16 - maxPacketSize = 36000 - minPaddingSize = 4 // TODO(huin) should this be configurable? -) - -// filteredConn reduces the set of methods exposed when embeddeding -// a net.Conn inside ssh.transport. -// TODO(dfc) suggestions for a better name will be warmly received. -type filteredConn interface { - // Close closes the connection. - Close() error - - // LocalAddr returns the local network address. - LocalAddr() net.Addr - - // RemoteAddr returns the remote network address. - RemoteAddr() net.Addr -} - -// Types implementing packetWriter provide the ability to send packets to -// an SSH peer. -type packetWriter interface { - // Encrypt and send a packet of data to the remote peer. - writePacket(packet []byte) error -} - -// transport represents the SSH connection to the remote peer. -type transport struct { - reader - writer - - filteredConn -} - -// reader represents the incoming connection state. -type reader struct { - io.Reader - common -} - -// writer represnts the outgoing connection state. -type writer struct { - *sync.Mutex // protects writer.Writer from concurrent writes - *bufio.Writer - rand io.Reader - common -} - -// common represents the cipher state needed to process messages in a single -// direction. -type common struct { - seqNum uint32 - mac hash.Hash - cipher cipher.Stream - - cipherAlgo string - macAlgo string - compressionAlgo string -} - -// Read and decrypt a single packet from the remote peer. -func (r *reader) readOnePacket() ([]byte, error) { - var lengthBytes = make([]byte, 5) - var macSize uint32 - if _, err := io.ReadFull(r, lengthBytes); err != nil { - return nil, err - } - - r.cipher.XORKeyStream(lengthBytes, lengthBytes) - - if r.mac != nil { - r.mac.Reset() - seqNumBytes := []byte{ - byte(r.seqNum >> 24), - byte(r.seqNum >> 16), - byte(r.seqNum >> 8), - byte(r.seqNum), - } - r.mac.Write(seqNumBytes) - r.mac.Write(lengthBytes) - macSize = uint32(r.mac.Size()) - } - - length := uint32(lengthBytes[0])<<24 | uint32(lengthBytes[1])<<16 | uint32(lengthBytes[2])<<8 | uint32(lengthBytes[3]) - paddingLength := uint32(lengthBytes[4]) - - if length <= paddingLength+1 { - return nil, errors.New("invalid packet length") - } - if length > maxPacketSize { - return nil, errors.New("packet too large") - } - - packet := make([]byte, length-1+macSize) - if _, err := io.ReadFull(r, packet); err != nil { - return nil, err - } - mac := packet[length-1:] - r.cipher.XORKeyStream(packet, packet[:length-1]) - - if r.mac != nil { - r.mac.Write(packet[:length-1]) - if subtle.ConstantTimeCompare(r.mac.Sum(nil), mac) != 1 { - return nil, errors.New("ssh: MAC failure") - } - } - - r.seqNum++ - return packet[:length-paddingLength-1], nil -} - -// Read and decrypt next packet discarding debug and noop messages. -func (t *transport) readPacket() ([]byte, error) { - for { - packet, err := t.readOnePacket() - if err != nil { - return nil, err - } - if packet[0] != msgIgnore && packet[0] != msgDebug { - return packet, nil - } - } - panic("unreachable") -} - -// Encrypt and send a packet of data to the remote peer. -func (w *writer) writePacket(packet []byte) error { - w.Mutex.Lock() - defer w.Mutex.Unlock() - - paddingLength := packetSizeMultiple - (5+len(packet))%packetSizeMultiple - if paddingLength < 4 { - paddingLength += packetSizeMultiple - } - - length := len(packet) + 1 + paddingLength - lengthBytes := []byte{ - byte(length >> 24), - byte(length >> 16), - byte(length >> 8), - byte(length), - byte(paddingLength), - } - padding := make([]byte, paddingLength) - _, err := io.ReadFull(w.rand, padding) - if err != nil { - return err - } - - if w.mac != nil { - w.mac.Reset() - seqNumBytes := []byte{ - byte(w.seqNum >> 24), - byte(w.seqNum >> 16), - byte(w.seqNum >> 8), - byte(w.seqNum), - } - w.mac.Write(seqNumBytes) - w.mac.Write(lengthBytes) - w.mac.Write(packet) - w.mac.Write(padding) - } - - // TODO(dfc) lengthBytes, packet and padding should be - // subslices of a single buffer - w.cipher.XORKeyStream(lengthBytes, lengthBytes) - w.cipher.XORKeyStream(packet, packet) - w.cipher.XORKeyStream(padding, padding) - - if _, err := w.Write(lengthBytes); err != nil { - return err - } - if _, err := w.Write(packet); err != nil { - return err - } - if _, err := w.Write(padding); err != nil { - return err - } - - if w.mac != nil { - if _, err := w.Write(w.mac.Sum(nil)); err != nil { - return err - } - } - - if err := w.Flush(); err != nil { - return err - } - w.seqNum++ - return err -} - -// Send a message to the remote peer -func (t *transport) sendMessage(typ uint8, msg interface{}) error { - packet := marshal(typ, msg) - return t.writePacket(packet) -} - -func newTransport(conn net.Conn, rand io.Reader) *transport { - return &transport{ - reader: reader{ - Reader: bufio.NewReader(conn), - common: common{ - cipher: noneCipher{}, - }, - }, - writer: writer{ - Writer: bufio.NewWriter(conn), - rand: rand, - Mutex: new(sync.Mutex), - common: common{ - cipher: noneCipher{}, - }, - }, - filteredConn: conn, - } -} - -type direction struct { - ivTag []byte - keyTag []byte - macKeyTag []byte -} - -// TODO(dfc) can this be made a constant ? -var ( - serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} - clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} -) - -// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as -// described in RFC 4253, section 6.4. direction should either be serverKeys -// (to setup server->client keys) or clientKeys (for client->server keys). -func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error { - cipherMode := cipherModes[c.cipherAlgo] - - macKeySize := 20 - - iv := make([]byte, cipherMode.ivSize) - key := make([]byte, cipherMode.keySize) - macKey := make([]byte, macKeySize) - - h := hashFunc.New() - generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h) - generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) - generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) - - c.mac = truncatingMAC{12, hmac.New(sha1.New, macKey)} - - cipher, err := cipherMode.createCipher(key, iv) - if err != nil { - return err - } - - c.cipher = cipher - - return nil -} - -// generateKeyMaterial fills out with key material generated from tag, K, H -// and sessionId, as specified in RFC 4253, section 7.2. -func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) { - var digestsSoFar []byte - - for len(out) > 0 { - h.Reset() - h.Write(K) - h.Write(H) - - if len(digestsSoFar) == 0 { - h.Write(tag) - h.Write(sessionId) - } else { - h.Write(digestsSoFar) - } - - digest := h.Sum(nil) - n := copy(out, digest) - out = out[n:] - if len(out) > 0 { - digestsSoFar = append(digestsSoFar, digest...) - } - } -} - -// truncatingMAC wraps around a hash.Hash and truncates the output digest to -// a given size. -type truncatingMAC struct { - length int - hmac hash.Hash -} - -func (t truncatingMAC) Write(data []byte) (int, error) { - return t.hmac.Write(data) -} - -func (t truncatingMAC) Sum(in []byte) []byte { - out := t.hmac.Sum(in) - return out[:len(in)+t.length] -} - -func (t truncatingMAC) Reset() { - t.hmac.Reset() -} - -func (t truncatingMAC) Size() int { - return t.length -} - -func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } - -// maxVersionStringBytes is the maximum number of bytes that we'll accept as a -// version string. In the event that the client is talking a different protocol -// we need to set a limit otherwise we will keep using more and more memory -// while searching for the end of the version handshake. -const maxVersionStringBytes = 1024 - -// Read version string as specified by RFC 4253, section 4.2. -func readVersion(r io.Reader) ([]byte, error) { - versionString := make([]byte, 0, 64) - var ok bool - var buf [1]byte -forEachByte: - for len(versionString) < maxVersionStringBytes { - _, err := io.ReadFull(r, buf[:]) - if err != nil { - return nil, err - } - // The RFC says that the version should be terminated with \r\n - // but several SSH servers actually only send a \n. - if buf[0] == '\n' { - ok = true - break forEachByte - } - versionString = append(versionString, buf[0]) - } - - if !ok { - return nil, errors.New("ssh: failed to read version string") - } - - // There might be a '\r' on the end which we should remove. - if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { - versionString = versionString[:len(versionString)-1] - } - return versionString, nil -} diff --git a/libgo/go/exp/ssh/transport_test.go b/libgo/go/exp/ssh/transport_test.go deleted file mode 100644 index ab9177f0d11..00000000000 --- a/libgo/go/exp/ssh/transport_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bufio" - "bytes" - "testing" -) - -func TestReadVersion(t *testing.T) { - buf := serverVersion - result, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))) - if err != nil { - t.Errorf("readVersion didn't read version correctly: %s", err) - } - if !bytes.Equal(buf[:len(buf)-2], result) { - t.Error("version read did not match expected") - } -} - -func TestReadVersionWithJustLF(t *testing.T) { - var buf []byte - buf = append(buf, serverVersion...) - buf = buf[:len(buf)-1] - buf[len(buf)-1] = '\n' - result, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))) - if err != nil { - t.Error("readVersion failed to handle just a \n") - } - if !bytes.Equal(buf[:len(buf)-1], result) { - t.Errorf("version read did not match expected: got %x, want %x", result, buf[:len(buf)-1]) - } -} - -func TestReadVersionTooLong(t *testing.T) { - buf := make([]byte, maxVersionStringBytes+1) - if _, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); err == nil { - t.Errorf("readVersion consumed %d bytes without error", len(buf)) - } -} - -func TestReadVersionWithoutCRLF(t *testing.T) { - buf := serverVersion - buf = buf[:len(buf)-1] - if _, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); err == nil { - t.Error("readVersion did not notice \\n was missing") - } -} |