github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/golang.org/x/crypto/ssh/server.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssh
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"net"
    13  )
    14  
    15  // The Permissions type holds fine-grained permissions that are
    16  // specific to a user or a specific authentication method for a
    17  // user. Permissions, except for "source-address", must be enforced in
    18  // the server application layer, after successful authentication. The
    19  // Permissions are passed on in ServerConn so a server implementation
    20  // can honor them.
    21  type Permissions struct {
    22  	// Critical options restrict default permissions. Common
    23  	// restrictions are "source-address" and "force-command". If
    24  	// the server cannot enforce the restriction, or does not
    25  	// recognize it, the user should not authenticate.
    26  	CriticalOptions map[string]string
    27  
    28  	// Extensions are extra functionality that the server may
    29  	// offer on authenticated connections. Common extensions are
    30  	// "permit-agent-forwarding", "permit-X11-forwarding". Lack of
    31  	// support for an extension does not preclude authenticating a
    32  	// user.
    33  	Extensions map[string]string
    34  }
    35  
    36  // ServerConfig holds server specific configuration data.
    37  type ServerConfig struct {
    38  	// Config contains configuration shared between client and server.
    39  	Config
    40  
    41  	hostKeys []Signer
    42  
    43  	// NoClientAuth is true if clients are allowed to connect without
    44  	// authenticating.
    45  	NoClientAuth bool
    46  
    47  	// PasswordCallback, if non-nil, is called when a user
    48  	// attempts to authenticate using a password.
    49  	PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
    50  
    51  	// PublicKeyCallback, if non-nil, is called when a client attempts public
    52  	// key authentication. It must return true if the given public key is
    53  	// valid for the given user. For example, see CertChecker.Authenticate.
    54  	PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
    55  
    56  	// KeyboardInteractiveCallback, if non-nil, is called when
    57  	// keyboard-interactive authentication is selected (RFC
    58  	// 4256). The client object's Challenge function should be
    59  	// used to query the user. The callback may offer multiple
    60  	// Challenge rounds. To avoid information leaks, the client
    61  	// should be presented a challenge even if the user is
    62  	// unknown.
    63  	KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
    64  
    65  	// AuthLogCallback, if non-nil, is called to log all authentication
    66  	// attempts.
    67  	AuthLogCallback func(conn ConnMetadata, method string, err error)
    68  
    69  	// ServerVersion is the version identification string to
    70  	// announce in the public handshake.
    71  	// If empty, a reasonable default is used.
    72  	ServerVersion string
    73  }
    74  
    75  // AddHostKey adds a private key as a host key. If an existing host
    76  // key exists with the same algorithm, it is overwritten. Each server
    77  // config must have at least one host key.
    78  func (s *ServerConfig) AddHostKey(key Signer) {
    79  	for i, k := range s.hostKeys {
    80  		if k.PublicKey().Type() == key.PublicKey().Type() {
    81  			s.hostKeys[i] = key
    82  			return
    83  		}
    84  	}
    85  
    86  	s.hostKeys = append(s.hostKeys, key)
    87  }
    88  
    89  // cachedPubKey contains the results of querying whether a public key is
    90  // acceptable for a user.
    91  type cachedPubKey struct {
    92  	user       string
    93  	pubKeyData []byte
    94  	result     error
    95  	perms      *Permissions
    96  }
    97  
    98  const maxCachedPubKeys = 16
    99  
   100  // pubKeyCache caches tests for public keys.  Since SSH clients
   101  // will query whether a public key is acceptable before attempting to
   102  // authenticate with it, we end up with duplicate queries for public
   103  // key validity.  The cache only applies to a single ServerConn.
   104  type pubKeyCache struct {
   105  	keys []cachedPubKey
   106  }
   107  
   108  // get returns the result for a given user/algo/key tuple.
   109  func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
   110  	for _, k := range c.keys {
   111  		if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
   112  			return k, true
   113  		}
   114  	}
   115  	return cachedPubKey{}, false
   116  }
   117  
   118  // add adds the given tuple to the cache.
   119  func (c *pubKeyCache) add(candidate cachedPubKey) {
   120  	if len(c.keys) < maxCachedPubKeys {
   121  		c.keys = append(c.keys, candidate)
   122  	}
   123  }
   124  
   125  // ServerConn is an authenticated SSH connection, as seen from the
   126  // server
   127  type ServerConn struct {
   128  	Conn
   129  
   130  	// If the succeeding authentication callback returned a
   131  	// non-nil Permissions pointer, it is stored here.
   132  	Permissions *Permissions
   133  }
   134  
   135  // NewServerConn starts a new SSH server with c as the underlying
   136  // transport.  It starts with a handshake and, if the handshake is
   137  // unsuccessful, it closes the connection and returns an error.  The
   138  // Request and NewChannel channels must be serviced, or the connection
   139  // will hang.
   140  func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
   141  	fullConf := *config
   142  	fullConf.SetDefaults()
   143  	s := &connection{
   144  		sshConn: sshConn{conn: c},
   145  	}
   146  	perms, err := s.serverHandshake(&fullConf)
   147  	if err != nil {
   148  		c.Close()
   149  		return nil, nil, nil, err
   150  	}
   151  	return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
   152  }
   153  
   154  // signAndMarshal signs the data with the appropriate algorithm,
   155  // and serializes the result in SSH wire format.
   156  func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
   157  	sig, err := k.Sign(rand, data)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	return Marshal(sig), nil
   163  }
   164  
   165  // handshake performs key exchange and user authentication.
   166  func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
   167  	if len(config.hostKeys) == 0 {
   168  		return nil, errors.New("ssh: server has no host keys")
   169  	}
   170  
   171  	if config.ServerVersion != "" {
   172  		s.serverVersion = []byte(config.ServerVersion)
   173  	} else {
   174  		s.serverVersion = []byte(packageVersion)
   175  	}
   176  	var err error
   177  	s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
   183  	s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
   184  
   185  	if err := s.transport.requestKeyChange(); err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	if packet, err := s.transport.readPacket(); err != nil {
   190  		return nil, err
   191  	} else if packet[0] != msgNewKeys {
   192  		return nil, unexpectedMessageError(msgNewKeys, packet[0])
   193  	}
   194  
   195  	// We just did the key change, so the session ID is established.
   196  	s.sessionID = s.transport.getSessionID()
   197  
   198  	var packet []byte
   199  	if packet, err = s.transport.readPacket(); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	var serviceRequest serviceRequestMsg
   204  	if err = Unmarshal(packet, &serviceRequest); err != nil {
   205  		return nil, err
   206  	}
   207  	if serviceRequest.Service != serviceUserAuth {
   208  		return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
   209  	}
   210  	serviceAccept := serviceAcceptMsg{
   211  		Service: serviceUserAuth,
   212  	}
   213  	if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	perms, err := s.serverAuthenticate(config)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	s.mux = newMux(s.transport)
   222  	return perms, err
   223  }
   224  
   225  func isAcceptableAlgo(algo string) bool {
   226  	switch algo {
   227  	case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
   228  		CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
   229  		return true
   230  	}
   231  	return false
   232  }
   233  
   234  func checkSourceAddress(addr net.Addr, sourceAddr string) error {
   235  	if addr == nil {
   236  		return errors.New("ssh: no address known for client, but source-address match required")
   237  	}
   238  
   239  	tcpAddr, ok := addr.(*net.TCPAddr)
   240  	if !ok {
   241  		return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
   242  	}
   243  
   244  	if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
   245  		if bytes.Equal(allowedIP, tcpAddr.IP) {
   246  			return nil
   247  		}
   248  	} else {
   249  		_, ipNet, err := net.ParseCIDR(sourceAddr)
   250  		if err != nil {
   251  			return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
   252  		}
   253  
   254  		if ipNet.Contains(tcpAddr.IP) {
   255  			return nil
   256  		}
   257  	}
   258  
   259  	return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
   260  }
   261  
   262  func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
   263  	var err error
   264  	var cache pubKeyCache
   265  	var perms *Permissions
   266  
   267  userAuthLoop:
   268  	for {
   269  		var userAuthReq userAuthRequestMsg
   270  		if packet, err := s.transport.readPacket(); err != nil {
   271  			return nil, err
   272  		} else if err = Unmarshal(packet, &userAuthReq); err != nil {
   273  			return nil, err
   274  		}
   275  
   276  		if userAuthReq.Service != serviceSSH {
   277  			return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
   278  		}
   279  
   280  		s.user = userAuthReq.User
   281  		perms = nil
   282  		authErr := errors.New("no auth passed yet")
   283  
   284  		switch userAuthReq.Method {
   285  		case "none":
   286  			if config.NoClientAuth {
   287  				s.user = ""
   288  				authErr = nil
   289  			}
   290  		case "password":
   291  			if config.PasswordCallback == nil {
   292  				authErr = errors.New("ssh: password auth not configured")
   293  				break
   294  			}
   295  			payload := userAuthReq.Payload
   296  			if len(payload) < 1 || payload[0] != 0 {
   297  				return nil, parseError(msgUserAuthRequest)
   298  			}
   299  			payload = payload[1:]
   300  			password, payload, ok := parseString(payload)
   301  			if !ok || len(payload) > 0 {
   302  				return nil, parseError(msgUserAuthRequest)
   303  			}
   304  
   305  			perms, authErr = config.PasswordCallback(s, password)
   306  		case "keyboard-interactive":
   307  			if config.KeyboardInteractiveCallback == nil {
   308  				authErr = errors.New("ssh: keyboard-interactive auth not configubred")
   309  				break
   310  			}
   311  
   312  			prompter := &sshClientKeyboardInteractive{s}
   313  			perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge)
   314  		case "publickey":
   315  			if config.PublicKeyCallback == nil {
   316  				authErr = errors.New("ssh: publickey auth not configured")
   317  				break
   318  			}
   319  			payload := userAuthReq.Payload
   320  			if len(payload) < 1 {
   321  				return nil, parseError(msgUserAuthRequest)
   322  			}
   323  			isQuery := payload[0] == 0
   324  			payload = payload[1:]
   325  			algoBytes, payload, ok := parseString(payload)
   326  			if !ok {
   327  				return nil, parseError(msgUserAuthRequest)
   328  			}
   329  			algo := string(algoBytes)
   330  			if !isAcceptableAlgo(algo) {
   331  				authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
   332  				break
   333  			}
   334  
   335  			pubKeyData, payload, ok := parseString(payload)
   336  			if !ok {
   337  				return nil, parseError(msgUserAuthRequest)
   338  			}
   339  
   340  			pubKey, err := ParsePublicKey(pubKeyData)
   341  			if err != nil {
   342  				return nil, err
   343  			}
   344  
   345  			candidate, ok := cache.get(s.user, pubKeyData)
   346  			if !ok {
   347  				candidate.user = s.user
   348  				candidate.pubKeyData = pubKeyData
   349  				candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey)
   350  				if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
   351  					candidate.result = checkSourceAddress(
   352  						s.RemoteAddr(),
   353  						candidate.perms.CriticalOptions[sourceAddressCriticalOption])
   354  				}
   355  				cache.add(candidate)
   356  			}
   357  
   358  			if isQuery {
   359  				// The client can query if the given public key
   360  				// would be okay.
   361  				if len(payload) > 0 {
   362  					return nil, parseError(msgUserAuthRequest)
   363  				}
   364  
   365  				if candidate.result == nil {
   366  					okMsg := userAuthPubKeyOkMsg{
   367  						Algo:   algo,
   368  						PubKey: pubKeyData,
   369  					}
   370  					if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
   371  						return nil, err
   372  					}
   373  					continue userAuthLoop
   374  				}
   375  				authErr = candidate.result
   376  			} else {
   377  				sig, payload, ok := parseSignature(payload)
   378  				if !ok || len(payload) > 0 {
   379  					return nil, parseError(msgUserAuthRequest)
   380  				}
   381  				// Ensure the public key algo and signature algo
   382  				// are supported.  Compare the private key
   383  				// algorithm name that corresponds to algo with
   384  				// sig.Format.  This is usually the same, but
   385  				// for certs, the names differ.
   386  				if !isAcceptableAlgo(sig.Format) {
   387  					break
   388  				}
   389  				signedData := buildDataSignedForAuth(s.transport.getSessionID(), userAuthReq, algoBytes, pubKeyData)
   390  
   391  				if err := pubKey.Verify(signedData, sig); err != nil {
   392  					return nil, err
   393  				}
   394  
   395  				authErr = candidate.result
   396  				perms = candidate.perms
   397  			}
   398  		default:
   399  			authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
   400  		}
   401  
   402  		if config.AuthLogCallback != nil {
   403  			config.AuthLogCallback(s, userAuthReq.Method, authErr)
   404  		}
   405  
   406  		if authErr == nil {
   407  			break userAuthLoop
   408  		}
   409  
   410  		var failureMsg userAuthFailureMsg
   411  		if config.PasswordCallback != nil {
   412  			failureMsg.Methods = append(failureMsg.Methods, "password")
   413  		}
   414  		if config.PublicKeyCallback != nil {
   415  			failureMsg.Methods = append(failureMsg.Methods, "publickey")
   416  		}
   417  		if config.KeyboardInteractiveCallback != nil {
   418  			failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
   419  		}
   420  
   421  		if len(failureMsg.Methods) == 0 {
   422  			return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
   423  		}
   424  
   425  		if err = s.transport.writePacket(Marshal(&failureMsg)); err != nil {
   426  			return nil, err
   427  		}
   428  	}
   429  
   430  	if err = s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
   431  		return nil, err
   432  	}
   433  	return perms, nil
   434  }
   435  
   436  // sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
   437  // asking the client on the other side of a ServerConn.
   438  type sshClientKeyboardInteractive struct {
   439  	*connection
   440  }
   441  
   442  func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
   443  	if len(questions) != len(echos) {
   444  		return nil, errors.New("ssh: echos and questions must have equal length")
   445  	}
   446  
   447  	var prompts []byte
   448  	for i := range questions {
   449  		prompts = appendString(prompts, questions[i])
   450  		prompts = appendBool(prompts, echos[i])
   451  	}
   452  
   453  	if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
   454  		Instruction: instruction,
   455  		NumPrompts:  uint32(len(questions)),
   456  		Prompts:     prompts,
   457  	})); err != nil {
   458  		return nil, err
   459  	}
   460  
   461  	packet, err := c.transport.readPacket()
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  	if packet[0] != msgUserAuthInfoResponse {
   466  		return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
   467  	}
   468  	packet = packet[1:]
   469  
   470  	n, packet, ok := parseUint32(packet)
   471  	if !ok || int(n) != len(questions) {
   472  		return nil, parseError(msgUserAuthInfoResponse)
   473  	}
   474  
   475  	for i := uint32(0); i < n; i++ {
   476  		ans, rest, ok := parseString(packet)
   477  		if !ok {
   478  			return nil, parseError(msgUserAuthInfoResponse)
   479  		}
   480  
   481  		answers = append(answers, string(ans))
   482  		packet = rest
   483  	}
   484  	if len(packet) != 0 {
   485  		return nil, errors.New("ssh: junk at end of message")
   486  	}
   487  
   488  	return answers, nil
   489  }