summaryrefslogtreecommitdiff
path: root/src/pkg/exp/ssh/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/exp/ssh/server.go')
-rw-r--r--src/pkg/exp/ssh/server.go714
1 files changed, 0 insertions, 714 deletions
diff --git a/src/pkg/exp/ssh/server.go b/src/pkg/exp/ssh/server.go
deleted file mode 100644
index bc0af13e8..000000000
--- a/src/pkg/exp/ssh/server.go
+++ /dev/null
@@ -1,714 +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 (
- "big"
- "bufio"
- "bytes"
- "crypto"
- "crypto/rand"
- "crypto/rsa"
- _ "crypto/sha1"
- "crypto/x509"
- "encoding/pem"
- "net"
- "os"
- "sync"
-)
-
-var supportedKexAlgos = []string{kexAlgoDH14SHA1}
-var supportedHostKeyAlgos = []string{hostAlgoRSA}
-var supportedCiphers = []string{cipherAES128CTR}
-var supportedMACs = []string{macSHA196}
-var supportedCompressions = []string{compressionNone}
-
-// Server represents an SSH server. A Server may have several ServerConnections.
-type Server struct {
- rsa *rsa.PrivateKey
- rsaSerialized []byte
-
- // 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
-
- // PubKeyCallback, 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.
- PubKeyCallback func(user, algo string, pubkey []byte) bool
-}
-
-// 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 *Server) SetRSAPrivateKey(pemBytes []byte) os.Error {
- block, _ := pem.Decode(pemBytes)
- if block == nil {
- return os.NewError("ssh: no key found")
- }
- var err os.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 ServerConnection.
-type cachedPubKey struct {
- user, algo string
- pubKey []byte
- result bool
-}
-
-const maxCachedPubKeys = 16
-
-// ServerConnection represents an incomming connection to a Server.
-type ServerConnection struct {
- Server *Server
-
- *transport
-
- channels map[uint32]*channel
- nextChanId uint32
-
- // lock protects err and also allows Channels to serialise their writes
- // to out.
- lock sync.RWMutex
- err os.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
-}
-
-// 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,
- }
-}
-
-type handshakeMagics struct {
- clientVersion, serverVersion []byte
- clientKexInit, serverKexInit []byte
-}
-
-// 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 *ServerConnection) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) (H, K []byte, err os.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, os.NewError("client DH parameter out of bounds")
- }
-
- y, err := rand.Int(rand.Reader, 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.Server.rsaSerialized
- default:
- return nil, nil, os.NewError("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()
-
- h.Reset()
- h.Write(H)
- hh := h.Sum()
-
- var sig []byte
- switch hostKeyAlgo {
- case hostAlgoRSA:
- sig, err = rsa.SignPKCS1v15(rand.Reader, s.Server.rsa, hashFunc, hh)
- if err != nil {
- return
- }
- default:
- return nil, nil, os.NewError("internal error")
- }
-
- serializedSig := serializeRSASignature(sig)
-
- kexDHReply := kexDHReplyMsg{
- HostKey: serializedHostKey,
- Y: Y,
- Signature: serializedSig,
- }
- packet = marshal(msgKexDHReply, kexDHReply)
-
- err = s.writePacket(packet)
- return
-}
-
-func serializeRSASignature(sig []byte) []byte {
- length := stringLength([]byte(hostAlgoRSA))
- length += stringLength(sig)
-
- ret := make([]byte, length)
- r := marshalString(ret, []byte(hostAlgoRSA))
- r = marshalString(r, sig)
-
- return ret
-}
-
-// serverVersion is the fixed identification string that Server will use.
-var serverVersion = []byte("SSH-2.0-Go\r\n")
-
-// 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
-}
-
-// Handshake performs an SSH transport and client authentication on the given ServerConnection.
-func (s *ServerConnection) Handshake(conn net.Conn) os.Error {
- var magics handshakeMagics
- s.transport = &transport{
- reader: reader{
- Reader: bufio.NewReader(conn),
- },
- writer: writer{
- Writer: bufio.NewWriter(conn),
- rand: rand.Reader,
- },
- Close: func() os.Error {
- return conn.Close()
- },
- }
-
- if _, err := conn.Write(serverVersion); err != nil {
- return err
- }
- magics.serverVersion = serverVersion[:len(serverVersion)-2]
-
- version, ok := readVersion(s.transport)
- if !ok {
- return os.NewError("failed to read version string from client")
- }
- magics.clientVersion = version
-
- serverKexInit := kexInitMsg{
- KexAlgos: supportedKexAlgos,
- ServerHostKeyAlgos: supportedHostKeyAlgos,
- CiphersClientServer: supportedCiphers,
- CiphersServerClient: supportedCiphers,
- 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, s.transport, &clientKexInit, &serverKexInit)
- if !ok {
- return os.NewError("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.
- _, err := s.readPacket()
- if 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 = os.NewError("ssh: internal error")
- }
-
- if err != nil {
- return err
- }
-
- packet = []byte{msgNewKeys}
- if err = s.writePacket(packet); 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]}
- }
-
- s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc)
-
- packet, err = s.readPacket()
- if err != nil {
- return err
- }
-
- var serviceRequest serviceRequestMsg
- if err = unmarshal(&serviceRequest, packet, msgServiceRequest); err != nil {
- return err
- }
- if serviceRequest.Service != serviceUserAuth {
- return os.NewError("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
- }
-
- serviceAccept := serviceAcceptMsg{
- Service: serviceUserAuth,
- }
- packet = marshal(msgServiceAccept, serviceAccept)
- if err = s.writePacket(packet); err != nil {
- return err
- }
-
- if err = s.authenticate(H); err != nil {
- return err
- }
-
- s.channels = make(map[uint32]*channel)
- 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 *ServerConnection) testPubKey(user, algo string, pubKey []byte) bool {
- if s.Server.PubKeyCallback == 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.Server.PubKeyCallback(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 *ServerConnection) authenticate(H []byte) os.Error {
- var userAuthReq userAuthRequestMsg
- var err os.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 os.NewError("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
- }
-
- switch userAuthReq.Method {
- case "none":
- if s.Server.NoClientAuth {
- break userAuthLoop
- }
- case "password":
- if s.Server.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.Server.PasswordCallback(userAuthReq.User, string(password)) {
- break userAuthLoop
- }
- case "publickey":
- if s.Server.PubKeyCallback == 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()
- rsaKey, ok := parseRSA(pubKey)
- if !ok {
- return ParseError{msgUserAuthRequest}
- }
- if rsa.VerifyPKCS1v15(rsaKey, hashFunc, digest, rsaSig) != nil {
- return ParseError{msgUserAuthRequest}
- }
- default:
- return os.NewError("ssh: isAcceptableAlgo incorrect")
- }
- if s.testPubKey(userAuthReq.User, algo, pubKey) {
- break userAuthLoop
- }
- }
- }
-
- var failureMsg userAuthFailureMsg
- if s.Server.PasswordCallback != nil {
- failureMsg.Methods = append(failureMsg.Methods, "password")
- }
- if s.Server.PubKeyCallback != nil {
- failureMsg.Methods = append(failureMsg.Methods, "publickey")
- }
-
- if len(failureMsg.Methods) == 0 {
- return os.NewError("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 ServerConnection. It must be called
-// in order to demultiplex messages to any resulting Channels.
-func (s *ServerConnection) Accept() (Channel, os.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 msgChannelOpen:
- var chanOpen channelOpenMsg
- if err := unmarshal(&chanOpen, packet, msgChannelOpen); err != nil {
- return nil, err
- }
-
- c := new(channel)
- c.chanType = chanOpen.ChanType
- c.theirId = chanOpen.PeersId
- c.theirWindow = chanOpen.PeersWindow
- c.maxPacketSize = chanOpen.MaxPacketSize
- c.extraData = chanOpen.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 msgChannelRequest:
- var chanRequest channelRequestMsg
- if err := unmarshal(&chanRequest, packet, msgChannelRequest); err != nil {
- return nil, err
- }
-
- s.lock.Lock()
- c, ok := s.channels[chanRequest.PeersId]
- if !ok {
- continue
- }
- c.handlePacket(&chanRequest)
- s.lock.Unlock()
-
- case msgChannelData:
- if len(packet) < 5 {
- return nil, ParseError{msgChannelData}
- }
- chanId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4])
-
- s.lock.Lock()
- c, ok := s.channels[chanId]
- if !ok {
- continue
- }
- c.handleData(packet[9:])
- s.lock.Unlock()
-
- case msgChannelEOF:
- var eofMsg channelEOFMsg
- if err := unmarshal(&eofMsg, packet, msgChannelEOF); err != nil {
- return nil, err
- }
-
- s.lock.Lock()
- c, ok := s.channels[eofMsg.PeersId]
- if !ok {
- continue
- }
- c.handlePacket(&eofMsg)
- s.lock.Unlock()
-
- case msgChannelClose:
- var closeMsg channelCloseMsg
- if err := unmarshal(&closeMsg, packet, msgChannelClose); err != nil {
- return nil, err
- }
-
- s.lock.Lock()
- c, ok := s.channels[closeMsg.PeersId]
- if !ok {
- continue
- }
- c.handlePacket(&closeMsg)
- s.lock.Unlock()
-
- case msgGlobalRequest:
- var request globalRequestMsg
- if err := unmarshal(&request, packet, msgGlobalRequest); err != nil {
- return nil, err
- }
-
- if request.WantReply {
- if err := s.writePacket([]byte{msgRequestFailure}); err != nil {
- return nil, err
- }
- }
-
- default:
- // Unknown message. Ignore.
- }
- }
-
- panic("unreachable")
-}