...

Source file src/golang.org/x/crypto/ssh/server.go

Documentation: golang.org/x/crypto/ssh

     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  	"strings"
    14  )
    15  
    16  // The Permissions type holds fine-grained permissions that are
    17  // specific to a user or a specific authentication method for a user.
    18  // The Permissions value for a successful authentication attempt is
    19  // available in ServerConn, so it can be used to pass information from
    20  // the user-authentication phase to the application layer.
    21  type Permissions struct {
    22  	// CriticalOptions indicate restrictions to the default
    23  	// permissions, and are typically used in conjunction with
    24  	// user certificates. The standard for SSH certificates
    25  	// defines "force-command" (only allow the given command to
    26  	// execute) and "source-address" (only allow connections from
    27  	// the given address). The SSH package currently only enforces
    28  	// the "source-address" critical option. It is up to server
    29  	// implementations to enforce other critical options, such as
    30  	// "force-command", by checking them after the SSH handshake
    31  	// is successful. In general, SSH servers should reject
    32  	// connections that specify critical options that are unknown
    33  	// or not supported.
    34  	CriticalOptions map[string]string
    35  
    36  	// Extensions are extra functionality that the server may
    37  	// offer on authenticated connections. Lack of support for an
    38  	// extension does not preclude authenticating a user. Common
    39  	// extensions are "permit-agent-forwarding",
    40  	// "permit-X11-forwarding". The Go SSH library currently does
    41  	// not act on any extension, and it is up to server
    42  	// implementations to honor them. Extensions can be used to
    43  	// pass data from the authentication callbacks to the server
    44  	// application layer.
    45  	Extensions map[string]string
    46  }
    47  
    48  type GSSAPIWithMICConfig struct {
    49  	// AllowLogin, must be set, is called when gssapi-with-mic
    50  	// authentication is selected (RFC 4462 section 3). The srcName is from the
    51  	// results of the GSS-API authentication. The format is username@DOMAIN.
    52  	// GSSAPI just guarantees to the server who the user is, but not if they can log in, and with what permissions.
    53  	// This callback is called after the user identity is established with GSSAPI to decide if the user can login with
    54  	// which permissions. If the user is allowed to login, it should return a nil error.
    55  	AllowLogin func(conn ConnMetadata, srcName string) (*Permissions, error)
    56  
    57  	// Server must be set. It's the implementation
    58  	// of the GSSAPIServer interface. See GSSAPIServer interface for details.
    59  	Server GSSAPIServer
    60  }
    61  
    62  // ServerConfig holds server specific configuration data.
    63  type ServerConfig struct {
    64  	// Config contains configuration shared between client and server.
    65  	Config
    66  
    67  	// PublicKeyAuthAlgorithms specifies the supported client public key
    68  	// authentication algorithms. Note that this should not include certificate
    69  	// types since those use the underlying algorithm. This list is sent to the
    70  	// client if it supports the server-sig-algs extension. Order is irrelevant.
    71  	// If unspecified then a default set of algorithms is used.
    72  	PublicKeyAuthAlgorithms []string
    73  
    74  	hostKeys []Signer
    75  
    76  	// NoClientAuth is true if clients are allowed to connect without
    77  	// authenticating.
    78  	// To determine NoClientAuth at runtime, set NoClientAuth to true
    79  	// and the optional NoClientAuthCallback to a non-nil value.
    80  	NoClientAuth bool
    81  
    82  	// NoClientAuthCallback, if non-nil, is called when a user
    83  	// attempts to authenticate with auth method "none".
    84  	// NoClientAuth must also be set to true for this be used, or
    85  	// this func is unused.
    86  	NoClientAuthCallback func(ConnMetadata) (*Permissions, error)
    87  
    88  	// MaxAuthTries specifies the maximum number of authentication attempts
    89  	// permitted per connection. If set to a negative number, the number of
    90  	// attempts are unlimited. If set to zero, the number of attempts are limited
    91  	// to 6.
    92  	MaxAuthTries int
    93  
    94  	// PasswordCallback, if non-nil, is called when a user
    95  	// attempts to authenticate using a password.
    96  	PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
    97  
    98  	// PublicKeyCallback, if non-nil, is called when a client
    99  	// offers a public key for authentication. It must return a nil error
   100  	// if the given public key can be used to authenticate the
   101  	// given user. For example, see CertChecker.Authenticate. A
   102  	// call to this function does not guarantee that the key
   103  	// offered is in fact used to authenticate. To record any data
   104  	// depending on the public key, store it inside a
   105  	// Permissions.Extensions entry.
   106  	PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
   107  
   108  	// KeyboardInteractiveCallback, if non-nil, is called when
   109  	// keyboard-interactive authentication is selected (RFC
   110  	// 4256). The client object's Challenge function should be
   111  	// used to query the user. The callback may offer multiple
   112  	// Challenge rounds. To avoid information leaks, the client
   113  	// should be presented a challenge even if the user is
   114  	// unknown.
   115  	KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
   116  
   117  	// AuthLogCallback, if non-nil, is called to log all authentication
   118  	// attempts.
   119  	AuthLogCallback func(conn ConnMetadata, method string, err error)
   120  
   121  	// ServerVersion is the version identification string to announce in
   122  	// the public handshake.
   123  	// If empty, a reasonable default is used.
   124  	// Note that RFC 4253 section 4.2 requires that this string start with
   125  	// "SSH-2.0-".
   126  	ServerVersion string
   127  
   128  	// BannerCallback, if present, is called and the return string is sent to
   129  	// the client after key exchange completed but before authentication.
   130  	BannerCallback func(conn ConnMetadata) string
   131  
   132  	// GSSAPIWithMICConfig includes gssapi server and callback, which if both non-nil, is used
   133  	// when gssapi-with-mic authentication is selected (RFC 4462 section 3).
   134  	GSSAPIWithMICConfig *GSSAPIWithMICConfig
   135  }
   136  
   137  // AddHostKey adds a private key as a host key. If an existing host
   138  // key exists with the same public key format, it is replaced. Each server
   139  // config must have at least one host key.
   140  func (s *ServerConfig) AddHostKey(key Signer) {
   141  	for i, k := range s.hostKeys {
   142  		if k.PublicKey().Type() == key.PublicKey().Type() {
   143  			s.hostKeys[i] = key
   144  			return
   145  		}
   146  	}
   147  
   148  	s.hostKeys = append(s.hostKeys, key)
   149  }
   150  
   151  // cachedPubKey contains the results of querying whether a public key is
   152  // acceptable for a user. This is a FIFO cache.
   153  type cachedPubKey struct {
   154  	user       string
   155  	pubKeyData []byte
   156  	result     error
   157  	perms      *Permissions
   158  }
   159  
   160  // maxCachedPubKeys is the number of cache entries we store.
   161  //
   162  // Due to consistent misuse of the PublicKeyCallback API, we have reduced this
   163  // to 1, such that the only key in the cache is the most recently seen one. This
   164  // forces the behavior that the last call to PublicKeyCallback will always be
   165  // with the key that is used for authentication.
   166  const maxCachedPubKeys = 1
   167  
   168  // pubKeyCache caches tests for public keys.  Since SSH clients
   169  // will query whether a public key is acceptable before attempting to
   170  // authenticate with it, we end up with duplicate queries for public
   171  // key validity.  The cache only applies to a single ServerConn.
   172  type pubKeyCache struct {
   173  	keys []cachedPubKey
   174  }
   175  
   176  // get returns the result for a given user/algo/key tuple.
   177  func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
   178  	for _, k := range c.keys {
   179  		if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
   180  			return k, true
   181  		}
   182  	}
   183  	return cachedPubKey{}, false
   184  }
   185  
   186  // add adds the given tuple to the cache.
   187  func (c *pubKeyCache) add(candidate cachedPubKey) {
   188  	if len(c.keys) >= maxCachedPubKeys {
   189  		c.keys = c.keys[1:]
   190  	}
   191  	c.keys = append(c.keys, candidate)
   192  }
   193  
   194  // ServerConn is an authenticated SSH connection, as seen from the
   195  // server
   196  type ServerConn struct {
   197  	Conn
   198  
   199  	// If the succeeding authentication callback returned a
   200  	// non-nil Permissions pointer, it is stored here.
   201  	Permissions *Permissions
   202  }
   203  
   204  // NewServerConn starts a new SSH server with c as the underlying
   205  // transport.  It starts with a handshake and, if the handshake is
   206  // unsuccessful, it closes the connection and returns an error.  The
   207  // Request and NewChannel channels must be serviced, or the connection
   208  // will hang.
   209  //
   210  // The returned error may be of type *ServerAuthError for
   211  // authentication errors.
   212  func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
   213  	fullConf := *config
   214  	fullConf.SetDefaults()
   215  	if fullConf.MaxAuthTries == 0 {
   216  		fullConf.MaxAuthTries = 6
   217  	}
   218  	if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
   219  		fullConf.PublicKeyAuthAlgorithms = supportedPubKeyAuthAlgos
   220  	} else {
   221  		for _, algo := range fullConf.PublicKeyAuthAlgorithms {
   222  			if !contains(supportedPubKeyAuthAlgos, algo) {
   223  				c.Close()
   224  				return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
   225  			}
   226  		}
   227  	}
   228  	// Check if the config contains any unsupported key exchanges
   229  	for _, kex := range fullConf.KeyExchanges {
   230  		if _, ok := serverForbiddenKexAlgos[kex]; ok {
   231  			c.Close()
   232  			return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
   233  		}
   234  	}
   235  
   236  	s := &connection{
   237  		sshConn: sshConn{conn: c},
   238  	}
   239  	perms, err := s.serverHandshake(&fullConf)
   240  	if err != nil {
   241  		c.Close()
   242  		return nil, nil, nil, err
   243  	}
   244  	return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
   245  }
   246  
   247  // signAndMarshal signs the data with the appropriate algorithm,
   248  // and serializes the result in SSH wire format. algo is the negotiate
   249  // algorithm and may be a certificate type.
   250  func signAndMarshal(k AlgorithmSigner, rand io.Reader, data []byte, algo string) ([]byte, error) {
   251  	sig, err := k.SignWithAlgorithm(rand, data, underlyingAlgo(algo))
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  
   256  	return Marshal(sig), nil
   257  }
   258  
   259  // handshake performs key exchange and user authentication.
   260  func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
   261  	if len(config.hostKeys) == 0 {
   262  		return nil, errors.New("ssh: server has no host keys")
   263  	}
   264  
   265  	if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil &&
   266  		config.KeyboardInteractiveCallback == nil && (config.GSSAPIWithMICConfig == nil ||
   267  		config.GSSAPIWithMICConfig.AllowLogin == nil || config.GSSAPIWithMICConfig.Server == nil) {
   268  		return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
   269  	}
   270  
   271  	if config.ServerVersion != "" {
   272  		s.serverVersion = []byte(config.ServerVersion)
   273  	} else {
   274  		s.serverVersion = []byte(packageVersion)
   275  	}
   276  	var err error
   277  	s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
   283  	s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
   284  
   285  	if err := s.transport.waitSession(); err != nil {
   286  		return nil, err
   287  	}
   288  
   289  	// We just did the key change, so the session ID is established.
   290  	s.sessionID = s.transport.getSessionID()
   291  
   292  	var packet []byte
   293  	if packet, err = s.transport.readPacket(); err != nil {
   294  		return nil, err
   295  	}
   296  
   297  	var serviceRequest serviceRequestMsg
   298  	if err = Unmarshal(packet, &serviceRequest); err != nil {
   299  		return nil, err
   300  	}
   301  	if serviceRequest.Service != serviceUserAuth {
   302  		return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
   303  	}
   304  	serviceAccept := serviceAcceptMsg{
   305  		Service: serviceUserAuth,
   306  	}
   307  	if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	perms, err := s.serverAuthenticate(config)
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  	s.mux = newMux(s.transport)
   316  	return perms, err
   317  }
   318  
   319  func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
   320  	if addr == nil {
   321  		return errors.New("ssh: no address known for client, but source-address match required")
   322  	}
   323  
   324  	tcpAddr, ok := addr.(*net.TCPAddr)
   325  	if !ok {
   326  		return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
   327  	}
   328  
   329  	for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
   330  		if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
   331  			if allowedIP.Equal(tcpAddr.IP) {
   332  				return nil
   333  			}
   334  		} else {
   335  			_, ipNet, err := net.ParseCIDR(sourceAddr)
   336  			if err != nil {
   337  				return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
   338  			}
   339  
   340  			if ipNet.Contains(tcpAddr.IP) {
   341  				return nil
   342  			}
   343  		}
   344  	}
   345  
   346  	return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
   347  }
   348  
   349  func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, token []byte, s *connection,
   350  	sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
   351  	gssAPIServer := gssapiConfig.Server
   352  	defer gssAPIServer.DeleteSecContext()
   353  	var srcName string
   354  	for {
   355  		var (
   356  			outToken     []byte
   357  			needContinue bool
   358  		)
   359  		outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(token)
   360  		if err != nil {
   361  			return err, nil, nil
   362  		}
   363  		if len(outToken) != 0 {
   364  			if err := s.transport.writePacket(Marshal(&userAuthGSSAPIToken{
   365  				Token: outToken,
   366  			})); err != nil {
   367  				return nil, nil, err
   368  			}
   369  		}
   370  		if !needContinue {
   371  			break
   372  		}
   373  		packet, err := s.transport.readPacket()
   374  		if err != nil {
   375  			return nil, nil, err
   376  		}
   377  		userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
   378  		if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
   379  			return nil, nil, err
   380  		}
   381  		token = userAuthGSSAPITokenReq.Token
   382  	}
   383  	packet, err := s.transport.readPacket()
   384  	if err != nil {
   385  		return nil, nil, err
   386  	}
   387  	userAuthGSSAPIMICReq := &userAuthGSSAPIMIC{}
   388  	if err := Unmarshal(packet, userAuthGSSAPIMICReq); err != nil {
   389  		return nil, nil, err
   390  	}
   391  	mic := buildMIC(string(sessionID), userAuthReq.User, userAuthReq.Service, userAuthReq.Method)
   392  	if err := gssAPIServer.VerifyMIC(mic, userAuthGSSAPIMICReq.MIC); err != nil {
   393  		return err, nil, nil
   394  	}
   395  	perms, authErr = gssapiConfig.AllowLogin(s, srcName)
   396  	return authErr, perms, nil
   397  }
   398  
   399  // isAlgoCompatible checks if the signature format is compatible with the
   400  // selected algorithm taking into account edge cases that occur with old
   401  // clients.
   402  func isAlgoCompatible(algo, sigFormat string) bool {
   403  	// Compatibility for old clients.
   404  	//
   405  	// For certificate authentication with OpenSSH 7.2-7.7 signature format can
   406  	// be rsa-sha2-256 or rsa-sha2-512 for the algorithm
   407  	// ssh-rsa-cert-v01@openssh.com.
   408  	//
   409  	// With gpg-agent < 2.2.6 the algorithm can be rsa-sha2-256 or rsa-sha2-512
   410  	// for signature format ssh-rsa.
   411  	if isRSA(algo) && isRSA(sigFormat) {
   412  		return true
   413  	}
   414  	// Standard case: the underlying algorithm must match the signature format.
   415  	return underlyingAlgo(algo) == sigFormat
   416  }
   417  
   418  // ServerAuthError represents server authentication errors and is
   419  // sometimes returned by NewServerConn. It appends any authentication
   420  // errors that may occur, and is returned if all of the authentication
   421  // methods provided by the user failed to authenticate.
   422  type ServerAuthError struct {
   423  	// Errors contains authentication errors returned by the authentication
   424  	// callback methods. The first entry is typically ErrNoAuth.
   425  	Errors []error
   426  }
   427  
   428  func (l ServerAuthError) Error() string {
   429  	var errs []string
   430  	for _, err := range l.Errors {
   431  		errs = append(errs, err.Error())
   432  	}
   433  	return "[" + strings.Join(errs, ", ") + "]"
   434  }
   435  
   436  // ServerAuthCallbacks defines server-side authentication callbacks.
   437  type ServerAuthCallbacks struct {
   438  	// PasswordCallback behaves like [ServerConfig.PasswordCallback].
   439  	PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
   440  
   441  	// PublicKeyCallback behaves like [ServerConfig.PublicKeyCallback].
   442  	PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
   443  
   444  	// KeyboardInteractiveCallback behaves like [ServerConfig.KeyboardInteractiveCallback].
   445  	KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
   446  
   447  	// GSSAPIWithMICConfig behaves like [ServerConfig.GSSAPIWithMICConfig].
   448  	GSSAPIWithMICConfig *GSSAPIWithMICConfig
   449  }
   450  
   451  // PartialSuccessError can be returned by any of the [ServerConfig]
   452  // authentication callbacks to indicate to the client that authentication has
   453  // partially succeeded, but further steps are required.
   454  type PartialSuccessError struct {
   455  	// Next defines the authentication callbacks to apply to further steps. The
   456  	// available methods communicated to the client are based on the non-nil
   457  	// ServerAuthCallbacks fields.
   458  	Next ServerAuthCallbacks
   459  }
   460  
   461  func (p *PartialSuccessError) Error() string {
   462  	return "ssh: authenticated with partial success"
   463  }
   464  
   465  // ErrNoAuth is the error value returned if no
   466  // authentication method has been passed yet. This happens as a normal
   467  // part of the authentication loop, since the client first tries
   468  // 'none' authentication to discover available methods.
   469  // It is returned in ServerAuthError.Errors from NewServerConn.
   470  var ErrNoAuth = errors.New("ssh: no auth passed yet")
   471  
   472  // BannerError is an error that can be returned by authentication handlers in
   473  // ServerConfig to send a banner message to the client.
   474  type BannerError struct {
   475  	Err     error
   476  	Message string
   477  }
   478  
   479  func (b *BannerError) Unwrap() error {
   480  	return b.Err
   481  }
   482  
   483  func (b *BannerError) Error() string {
   484  	if b.Err == nil {
   485  		return b.Message
   486  	}
   487  	return b.Err.Error()
   488  }
   489  
   490  func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
   491  	sessionID := s.transport.getSessionID()
   492  	var cache pubKeyCache
   493  	var perms *Permissions
   494  
   495  	authFailures := 0
   496  	noneAuthCount := 0
   497  	var authErrs []error
   498  	var displayedBanner bool
   499  	partialSuccessReturned := false
   500  	// Set the initial authentication callbacks from the config. They can be
   501  	// changed if a PartialSuccessError is returned.
   502  	authConfig := ServerAuthCallbacks{
   503  		PasswordCallback:            config.PasswordCallback,
   504  		PublicKeyCallback:           config.PublicKeyCallback,
   505  		KeyboardInteractiveCallback: config.KeyboardInteractiveCallback,
   506  		GSSAPIWithMICConfig:         config.GSSAPIWithMICConfig,
   507  	}
   508  
   509  userAuthLoop:
   510  	for {
   511  		if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 {
   512  			discMsg := &disconnectMsg{
   513  				Reason:  2,
   514  				Message: "too many authentication failures",
   515  			}
   516  
   517  			if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
   518  				return nil, err
   519  			}
   520  			authErrs = append(authErrs, discMsg)
   521  			return nil, &ServerAuthError{Errors: authErrs}
   522  		}
   523  
   524  		var userAuthReq userAuthRequestMsg
   525  		if packet, err := s.transport.readPacket(); err != nil {
   526  			if err == io.EOF {
   527  				return nil, &ServerAuthError{Errors: authErrs}
   528  			}
   529  			return nil, err
   530  		} else if err = Unmarshal(packet, &userAuthReq); err != nil {
   531  			return nil, err
   532  		}
   533  
   534  		if userAuthReq.Service != serviceSSH {
   535  			return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
   536  		}
   537  
   538  		if s.user != userAuthReq.User && partialSuccessReturned {
   539  			return nil, fmt.Errorf("ssh: client changed the user after a partial success authentication, previous user %q, current user %q",
   540  				s.user, userAuthReq.User)
   541  		}
   542  
   543  		s.user = userAuthReq.User
   544  
   545  		if !displayedBanner && config.BannerCallback != nil {
   546  			displayedBanner = true
   547  			msg := config.BannerCallback(s)
   548  			if msg != "" {
   549  				bannerMsg := &userAuthBannerMsg{
   550  					Message: msg,
   551  				}
   552  				if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
   553  					return nil, err
   554  				}
   555  			}
   556  		}
   557  
   558  		perms = nil
   559  		authErr := ErrNoAuth
   560  
   561  		switch userAuthReq.Method {
   562  		case "none":
   563  			noneAuthCount++
   564  			// We don't allow none authentication after a partial success
   565  			// response.
   566  			if config.NoClientAuth && !partialSuccessReturned {
   567  				if config.NoClientAuthCallback != nil {
   568  					perms, authErr = config.NoClientAuthCallback(s)
   569  				} else {
   570  					authErr = nil
   571  				}
   572  			}
   573  		case "password":
   574  			if authConfig.PasswordCallback == nil {
   575  				authErr = errors.New("ssh: password auth not configured")
   576  				break
   577  			}
   578  			payload := userAuthReq.Payload
   579  			if len(payload) < 1 || payload[0] != 0 {
   580  				return nil, parseError(msgUserAuthRequest)
   581  			}
   582  			payload = payload[1:]
   583  			password, payload, ok := parseString(payload)
   584  			if !ok || len(payload) > 0 {
   585  				return nil, parseError(msgUserAuthRequest)
   586  			}
   587  
   588  			perms, authErr = authConfig.PasswordCallback(s, password)
   589  		case "keyboard-interactive":
   590  			if authConfig.KeyboardInteractiveCallback == nil {
   591  				authErr = errors.New("ssh: keyboard-interactive auth not configured")
   592  				break
   593  			}
   594  
   595  			prompter := &sshClientKeyboardInteractive{s}
   596  			perms, authErr = authConfig.KeyboardInteractiveCallback(s, prompter.Challenge)
   597  		case "publickey":
   598  			if authConfig.PublicKeyCallback == nil {
   599  				authErr = errors.New("ssh: publickey auth not configured")
   600  				break
   601  			}
   602  			payload := userAuthReq.Payload
   603  			if len(payload) < 1 {
   604  				return nil, parseError(msgUserAuthRequest)
   605  			}
   606  			isQuery := payload[0] == 0
   607  			payload = payload[1:]
   608  			algoBytes, payload, ok := parseString(payload)
   609  			if !ok {
   610  				return nil, parseError(msgUserAuthRequest)
   611  			}
   612  			algo := string(algoBytes)
   613  			if !contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
   614  				authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
   615  				break
   616  			}
   617  
   618  			pubKeyData, payload, ok := parseString(payload)
   619  			if !ok {
   620  				return nil, parseError(msgUserAuthRequest)
   621  			}
   622  
   623  			pubKey, err := ParsePublicKey(pubKeyData)
   624  			if err != nil {
   625  				return nil, err
   626  			}
   627  
   628  			candidate, ok := cache.get(s.user, pubKeyData)
   629  			if !ok {
   630  				candidate.user = s.user
   631  				candidate.pubKeyData = pubKeyData
   632  				candidate.perms, candidate.result = authConfig.PublicKeyCallback(s, pubKey)
   633  				_, isPartialSuccessError := candidate.result.(*PartialSuccessError)
   634  
   635  				if (candidate.result == nil || isPartialSuccessError) &&
   636  					candidate.perms != nil &&
   637  					candidate.perms.CriticalOptions != nil &&
   638  					candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
   639  					if err := checkSourceAddress(
   640  						s.RemoteAddr(),
   641  						candidate.perms.CriticalOptions[sourceAddressCriticalOption]); err != nil {
   642  						candidate.result = err
   643  					}
   644  				}
   645  				cache.add(candidate)
   646  			}
   647  
   648  			if isQuery {
   649  				// The client can query if the given public key
   650  				// would be okay.
   651  
   652  				if len(payload) > 0 {
   653  					return nil, parseError(msgUserAuthRequest)
   654  				}
   655  				_, isPartialSuccessError := candidate.result.(*PartialSuccessError)
   656  				if candidate.result == nil || isPartialSuccessError {
   657  					okMsg := userAuthPubKeyOkMsg{
   658  						Algo:   algo,
   659  						PubKey: pubKeyData,
   660  					}
   661  					if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
   662  						return nil, err
   663  					}
   664  					continue userAuthLoop
   665  				}
   666  				authErr = candidate.result
   667  			} else {
   668  				sig, payload, ok := parseSignature(payload)
   669  				if !ok || len(payload) > 0 {
   670  					return nil, parseError(msgUserAuthRequest)
   671  				}
   672  				// Ensure the declared public key algo is compatible with the
   673  				// decoded one. This check will ensure we don't accept e.g.
   674  				// ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public
   675  				// key type. The algorithm and public key type must be
   676  				// consistent: both must be certificate algorithms, or neither.
   677  				if !contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
   678  					authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
   679  						pubKey.Type(), algo)
   680  					break
   681  				}
   682  				// Ensure the public key algo and signature algo
   683  				// are supported.  Compare the private key
   684  				// algorithm name that corresponds to algo with
   685  				// sig.Format.  This is usually the same, but
   686  				// for certs, the names differ.
   687  				if !contains(config.PublicKeyAuthAlgorithms, sig.Format) {
   688  					authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
   689  					break
   690  				}
   691  				if !isAlgoCompatible(algo, sig.Format) {
   692  					authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
   693  					break
   694  				}
   695  
   696  				signedData := buildDataSignedForAuth(sessionID, userAuthReq, algo, pubKeyData)
   697  
   698  				if err := pubKey.Verify(signedData, sig); err != nil {
   699  					return nil, err
   700  				}
   701  
   702  				authErr = candidate.result
   703  				perms = candidate.perms
   704  			}
   705  		case "gssapi-with-mic":
   706  			if authConfig.GSSAPIWithMICConfig == nil {
   707  				authErr = errors.New("ssh: gssapi-with-mic auth not configured")
   708  				break
   709  			}
   710  			gssapiConfig := authConfig.GSSAPIWithMICConfig
   711  			userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload)
   712  			if err != nil {
   713  				return nil, parseError(msgUserAuthRequest)
   714  			}
   715  			// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication.
   716  			if userAuthRequestGSSAPI.N == 0 {
   717  				authErr = fmt.Errorf("ssh: Mechanism negotiation is not supported")
   718  				break
   719  			}
   720  			var i uint32
   721  			present := false
   722  			for i = 0; i < userAuthRequestGSSAPI.N; i++ {
   723  				if userAuthRequestGSSAPI.OIDS[i].Equal(krb5Mesh) {
   724  					present = true
   725  					break
   726  				}
   727  			}
   728  			if !present {
   729  				authErr = fmt.Errorf("ssh: GSSAPI authentication must use the Kerberos V5 mechanism")
   730  				break
   731  			}
   732  			// Initial server response, see RFC 4462 section 3.3.
   733  			if err := s.transport.writePacket(Marshal(&userAuthGSSAPIResponse{
   734  				SupportMech: krb5OID,
   735  			})); err != nil {
   736  				return nil, err
   737  			}
   738  			// Exchange token, see RFC 4462 section 3.4.
   739  			packet, err := s.transport.readPacket()
   740  			if err != nil {
   741  				return nil, err
   742  			}
   743  			userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
   744  			if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
   745  				return nil, err
   746  			}
   747  			authErr, perms, err = gssExchangeToken(gssapiConfig, userAuthGSSAPITokenReq.Token, s, sessionID,
   748  				userAuthReq)
   749  			if err != nil {
   750  				return nil, err
   751  			}
   752  		default:
   753  			authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
   754  		}
   755  
   756  		authErrs = append(authErrs, authErr)
   757  
   758  		if config.AuthLogCallback != nil {
   759  			config.AuthLogCallback(s, userAuthReq.Method, authErr)
   760  		}
   761  
   762  		var bannerErr *BannerError
   763  		if errors.As(authErr, &bannerErr) {
   764  			if bannerErr.Message != "" {
   765  				bannerMsg := &userAuthBannerMsg{
   766  					Message: bannerErr.Message,
   767  				}
   768  				if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
   769  					return nil, err
   770  				}
   771  			}
   772  		}
   773  
   774  		if authErr == nil {
   775  			break userAuthLoop
   776  		}
   777  
   778  		var failureMsg userAuthFailureMsg
   779  
   780  		if partialSuccess, ok := authErr.(*PartialSuccessError); ok {
   781  			// After a partial success error we don't allow changing the user
   782  			// name and execute the NoClientAuthCallback.
   783  			partialSuccessReturned = true
   784  
   785  			// In case a partial success is returned, the server may send
   786  			// a new set of authentication methods.
   787  			authConfig = partialSuccess.Next
   788  
   789  			// Reset pubkey cache, as the new PublicKeyCallback might
   790  			// accept a different set of public keys.
   791  			cache = pubKeyCache{}
   792  
   793  			// Send back a partial success message to the user.
   794  			failureMsg.PartialSuccess = true
   795  		} else {
   796  			// Allow initial attempt of 'none' without penalty.
   797  			if authFailures > 0 || userAuthReq.Method != "none" || noneAuthCount != 1 {
   798  				authFailures++
   799  			}
   800  			if config.MaxAuthTries > 0 && authFailures >= config.MaxAuthTries {
   801  				// If we have hit the max attempts, don't bother sending the
   802  				// final SSH_MSG_USERAUTH_FAILURE message, since there are
   803  				// no more authentication methods which can be attempted,
   804  				// and this message may cause the client to re-attempt
   805  				// authentication while we send the disconnect message.
   806  				// Continue, and trigger the disconnect at the start of
   807  				// the loop.
   808  				//
   809  				// The SSH specification is somewhat confusing about this,
   810  				// RFC 4252 Section 5.1 requires each authentication failure
   811  				// be responded to with a respective SSH_MSG_USERAUTH_FAILURE
   812  				// message, but Section 4 says the server should disconnect
   813  				// after some number of attempts, but it isn't explicit which
   814  				// message should take precedence (i.e. should there be a failure
   815  				// message than a disconnect message, or if we are going to
   816  				// disconnect, should we only send that message.)
   817  				//
   818  				// Either way, OpenSSH disconnects immediately after the last
   819  				// failed authentication attempt, and given they are typically
   820  				// considered the golden implementation it seems reasonable
   821  				// to match that behavior.
   822  				continue
   823  			}
   824  		}
   825  
   826  		if authConfig.PasswordCallback != nil {
   827  			failureMsg.Methods = append(failureMsg.Methods, "password")
   828  		}
   829  		if authConfig.PublicKeyCallback != nil {
   830  			failureMsg.Methods = append(failureMsg.Methods, "publickey")
   831  		}
   832  		if authConfig.KeyboardInteractiveCallback != nil {
   833  			failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
   834  		}
   835  		if authConfig.GSSAPIWithMICConfig != nil && authConfig.GSSAPIWithMICConfig.Server != nil &&
   836  			authConfig.GSSAPIWithMICConfig.AllowLogin != nil {
   837  			failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic")
   838  		}
   839  
   840  		if len(failureMsg.Methods) == 0 {
   841  			return nil, errors.New("ssh: no authentication methods available")
   842  		}
   843  
   844  		if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {
   845  			return nil, err
   846  		}
   847  	}
   848  
   849  	if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
   850  		return nil, err
   851  	}
   852  	return perms, nil
   853  }
   854  
   855  // sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
   856  // asking the client on the other side of a ServerConn.
   857  type sshClientKeyboardInteractive struct {
   858  	*connection
   859  }
   860  
   861  func (c *sshClientKeyboardInteractive) Challenge(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
   862  	if len(questions) != len(echos) {
   863  		return nil, errors.New("ssh: echos and questions must have equal length")
   864  	}
   865  
   866  	var prompts []byte
   867  	for i := range questions {
   868  		prompts = appendString(prompts, questions[i])
   869  		prompts = appendBool(prompts, echos[i])
   870  	}
   871  
   872  	if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
   873  		Name:        name,
   874  		Instruction: instruction,
   875  		NumPrompts:  uint32(len(questions)),
   876  		Prompts:     prompts,
   877  	})); err != nil {
   878  		return nil, err
   879  	}
   880  
   881  	packet, err := c.transport.readPacket()
   882  	if err != nil {
   883  		return nil, err
   884  	}
   885  	if packet[0] != msgUserAuthInfoResponse {
   886  		return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
   887  	}
   888  	packet = packet[1:]
   889  
   890  	n, packet, ok := parseUint32(packet)
   891  	if !ok || int(n) != len(questions) {
   892  		return nil, parseError(msgUserAuthInfoResponse)
   893  	}
   894  
   895  	for i := uint32(0); i < n; i++ {
   896  		ans, rest, ok := parseString(packet)
   897  		if !ok {
   898  			return nil, parseError(msgUserAuthInfoResponse)
   899  		}
   900  
   901  		answers = append(answers, string(ans))
   902  		packet = rest
   903  	}
   904  	if len(packet) != 0 {
   905  		return nil, errors.New("ssh: junk at end of message")
   906  	}
   907  
   908  	return answers, nil
   909  }
   910  

View as plain text