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