// 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 (
	
	
	
	
	
	
)

// The Permissions type holds fine-grained permissions that are
// specific to a user or a specific authentication method for a user.
// The Permissions value for a successful authentication attempt is
// available in ServerConn, so it can be used to pass information from
// the user-authentication phase to the application layer.
type Permissions struct {
	// CriticalOptions indicate restrictions to the default
	// permissions, and are typically used in conjunction with
	// user certificates. The standard for SSH certificates
	// defines "force-command" (only allow the given command to
	// execute) and "source-address" (only allow connections from
	// the given address). The SSH package currently only enforces
	// the "source-address" critical option. It is up to server
	// implementations to enforce other critical options, such as
	// "force-command", by checking them after the SSH handshake
	// is successful. In general, SSH servers should reject
	// connections that specify critical options that are unknown
	// or not supported.
	CriticalOptions map[string]string

	// Extensions are extra functionality that the server may
	// offer on authenticated connections. Lack of support for an
	// extension does not preclude authenticating a user. Common
	// extensions are "permit-agent-forwarding",
	// "permit-X11-forwarding". The Go SSH library currently does
	// not act on any extension, and it is up to server
	// implementations to honor them. Extensions can be used to
	// pass data from the authentication callbacks to the server
	// application layer.
	Extensions map[string]string
}

type GSSAPIWithMICConfig struct {
	// AllowLogin, must be set, is called when gssapi-with-mic
	// authentication is selected (RFC 4462 section 3). The srcName is from the
	// results of the GSS-API authentication. The format is username@DOMAIN.
	// GSSAPI just guarantees to the server who the user is, but not if they can log in, and with what permissions.
	// This callback is called after the user identity is established with GSSAPI to decide if the user can login with
	// which permissions. If the user is allowed to login, it should return a nil error.
	AllowLogin func(conn ConnMetadata, srcName string) (*Permissions, error)

	// Server must be set. It's the implementation
	// of the GSSAPIServer interface. See GSSAPIServer interface for details.
	Server GSSAPIServer
}

// ServerConfig holds server specific configuration data.
type ServerConfig struct {
	// Config contains configuration shared between client and server.
	Config

	hostKeys []Signer

	// NoClientAuth is true if clients are allowed to connect without
	// authenticating.
	// To determine NoClientAuth at runtime, set NoClientAuth to true
	// and the optional NoClientAuthCallback to a non-nil value.
	NoClientAuth bool

	// NoClientAuthCallback, if non-nil, is called when a user
	// attempts to authenticate with auth method "none".
	// NoClientAuth must also be set to true for this be used, or
	// this func is unused.
	NoClientAuthCallback func(ConnMetadata) (*Permissions, error)

	// MaxAuthTries specifies the maximum number of authentication attempts
	// permitted per connection. If set to a negative number, the number of
	// attempts are unlimited. If set to zero, the number of attempts are limited
	// to 6.
	MaxAuthTries int

	// PasswordCallback, if non-nil, is called when a user
	// attempts to authenticate using a password.
	PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)

	// PublicKeyCallback, if non-nil, is called when a client
	// offers a public key for authentication. It must return a nil error
	// if the given public key can be used to authenticate the
	// given user. For example, see CertChecker.Authenticate. A
	// call to this function does not guarantee that the key
	// offered is in fact used to authenticate. To record any data
	// depending on the public key, store it inside a
	// Permissions.Extensions entry.
	PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)

	// KeyboardInteractiveCallback, if non-nil, is called when
	// keyboard-interactive authentication is selected (RFC
	// 4256). The client object's Challenge function should be
	// used to query the user. The callback may offer multiple
	// Challenge rounds. To avoid information leaks, the client
	// should be presented a challenge even if the user is
	// unknown.
	KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)

	// AuthLogCallback, if non-nil, is called to log all authentication
	// attempts.
	AuthLogCallback func(conn ConnMetadata, method string, err error)

	// ServerVersion is the version identification string to announce in
	// the public handshake.
	// If empty, a reasonable default is used.
	// Note that RFC 4253 section 4.2 requires that this string start with
	// "SSH-2.0-".
	ServerVersion string

	// BannerCallback, if present, is called and the return string is sent to
	// the client after key exchange completed but before authentication.
	BannerCallback func(conn ConnMetadata) string

	// GSSAPIWithMICConfig includes gssapi server and callback, which if both non-nil, is used
	// when gssapi-with-mic authentication is selected (RFC 4462 section 3).
	GSSAPIWithMICConfig *GSSAPIWithMICConfig
}

// AddHostKey adds a private key as a host key. If an existing host
// key exists with the same public key format, it is replaced. Each server
// config must have at least one host key.
func ( *ServerConfig) ( Signer) {
	for ,  := range .hostKeys {
		if .PublicKey().Type() == .PublicKey().Type() {
			.hostKeys[] = 
			return
		}
	}

	.hostKeys = append(.hostKeys, )
}

// cachedPubKey contains the results of querying whether a public key is
// acceptable for a user.
type cachedPubKey struct {
	user       string
	pubKeyData []byte
	result     error
	perms      *Permissions
}

const maxCachedPubKeys = 16

// pubKeyCache caches 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.  The cache only applies to a single ServerConn.
type pubKeyCache struct {
	keys []cachedPubKey
}

// get returns the result for a given user/algo/key tuple.
func ( *pubKeyCache) ( string,  []byte) (cachedPubKey, bool) {
	for ,  := range .keys {
		if .user ==  && bytes.Equal(.pubKeyData, ) {
			return , true
		}
	}
	return cachedPubKey{}, false
}

// add adds the given tuple to the cache.
func ( *pubKeyCache) ( cachedPubKey) {
	if len(.keys) < maxCachedPubKeys {
		.keys = append(.keys, )
	}
}

// ServerConn is an authenticated SSH connection, as seen from the
// server
type ServerConn struct {
	Conn

	// If the succeeding authentication callback returned a
	// non-nil Permissions pointer, it is stored here.
	Permissions *Permissions
}

// NewServerConn starts a new SSH server with c as the underlying
// transport.  It starts with a handshake and, if the handshake is
// unsuccessful, it closes the connection and returns an error.  The
// Request and NewChannel channels must be serviced, or the connection
// will hang.
//
// The returned error may be of type *ServerAuthError for
// authentication errors.
func ( net.Conn,  *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
	 := *
	.SetDefaults()
	if .MaxAuthTries == 0 {
		.MaxAuthTries = 6
	}
	// Check if the config contains any unsupported key exchanges
	for ,  := range .KeyExchanges {
		if ,  := serverForbiddenKexAlgos[];  {
			return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", )
		}
	}

	 := &connection{
		sshConn: sshConn{conn: },
	}
	,  := .serverHandshake(&)
	if  != nil {
		.Close()
		return nil, nil, nil, 
	}
	return &ServerConn{, }, .mux.incomingChannels, .mux.incomingRequests, nil
}

// signAndMarshal signs the data with the appropriate algorithm,
// and serializes the result in SSH wire format. algo is the negotiate
// algorithm and may be a certificate type.
func signAndMarshal( AlgorithmSigner,  io.Reader,  []byte,  string) ([]byte, error) {
	,  := .SignWithAlgorithm(, , underlyingAlgo())
	if  != nil {
		return nil, 
	}

	return Marshal(), nil
}

// handshake performs key exchange and user authentication.
func ( *connection) ( *ServerConfig) (*Permissions, error) {
	if len(.hostKeys) == 0 {
		return nil, errors.New("ssh: server has no host keys")
	}

	if !.NoClientAuth && .PasswordCallback == nil && .PublicKeyCallback == nil &&
		.KeyboardInteractiveCallback == nil && (.GSSAPIWithMICConfig == nil ||
		.GSSAPIWithMICConfig.AllowLogin == nil || .GSSAPIWithMICConfig.Server == nil) {
		return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
	}

	if .ServerVersion != "" {
		.serverVersion = []byte(.ServerVersion)
	} else {
		.serverVersion = []byte(packageVersion)
	}
	var  error
	.clientVersion,  = exchangeVersions(.sshConn.conn, .serverVersion)
	if  != nil {
		return nil, 
	}

	 := newTransport(.sshConn.conn, .Rand, false /* not client */)
	.transport = newServerTransport(, .clientVersion, .serverVersion, )

	if  := .transport.waitSession();  != nil {
		return nil, 
	}

	// We just did the key change, so the session ID is established.
	.sessionID = .transport.getSessionID()

	var  []byte
	if ,  = .transport.readPacket();  != nil {
		return nil, 
	}

	var  serviceRequestMsg
	if  = Unmarshal(, &);  != nil {
		return nil, 
	}
	if .Service != serviceUserAuth {
		return nil, errors.New("ssh: requested service '" + .Service + "' before authenticating")
	}
	 := serviceAcceptMsg{
		Service: serviceUserAuth,
	}
	if  := .transport.writePacket(Marshal(&));  != nil {
		return nil, 
	}

	,  := .serverAuthenticate()
	if  != nil {
		return nil, 
	}
	.mux = newMux(.transport)
	return , 
}

func checkSourceAddress( net.Addr,  string) error {
	if  == nil {
		return errors.New("ssh: no address known for client, but source-address match required")
	}

	,  := .(*net.TCPAddr)
	if ! {
		return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", )
	}

	for ,  := range strings.Split(, ",") {
		if  := net.ParseIP();  != nil {
			if .Equal(.IP) {
				return nil
			}
		} else {
			, ,  := net.ParseCIDR()
			if  != nil {
				return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", , )
			}

			if .Contains(.IP) {
				return nil
			}
		}
	}

	return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", )
}

func gssExchangeToken( *GSSAPIWithMICConfig,  []byte,  *connection,
	 []byte,  userAuthRequestMsg) ( error,  *Permissions,  error) {
	 := .Server
	defer .DeleteSecContext()
	var  string
	for {
		var (
			     []byte
			 bool
		)
		, , ,  = .AcceptSecContext()
		if  != nil {
			return , nil, nil
		}
		if len() != 0 {
			if  := .transport.writePacket(Marshal(&userAuthGSSAPIToken{
				Token: ,
			}));  != nil {
				return nil, nil, 
			}
		}
		if ! {
			break
		}
		,  := .transport.readPacket()
		if  != nil {
			return nil, nil, 
		}
		 := &userAuthGSSAPIToken{}
		if  := Unmarshal(, );  != nil {
			return nil, nil, 
		}
	}
	,  := .transport.readPacket()
	if  != nil {
		return nil, nil, 
	}
	 := &userAuthGSSAPIMIC{}
	if  := Unmarshal(, );  != nil {
		return nil, nil, 
	}
	 := buildMIC(string(), .User, .Service, .Method)
	if  := .VerifyMIC(, .MIC);  != nil {
		return , nil, nil
	}
	,  = .AllowLogin(, )
	return , , nil
}

// isAlgoCompatible checks if the signature format is compatible with the
// selected algorithm taking into account edge cases that occur with old
// clients.
func isAlgoCompatible(,  string) bool {
	// Compatibility for old clients.
	//
	// For certificate authentication with OpenSSH 7.2-7.7 signature format can
	// be rsa-sha2-256 or rsa-sha2-512 for the algorithm
	// ssh-rsa-cert-v01@openssh.com.
	//
	// With gpg-agent < 2.2.6 the algorithm can be rsa-sha2-256 or rsa-sha2-512
	// for signature format ssh-rsa.
	if isRSA() && isRSA() {
		return true
	}
	// Standard case: the underlying algorithm must match the signature format.
	return underlyingAlgo() == 
}

// ServerAuthError represents server authentication errors and is
// sometimes returned by NewServerConn. It appends any authentication
// errors that may occur, and is returned if all of the authentication
// methods provided by the user failed to authenticate.
type ServerAuthError struct {
	// Errors contains authentication errors returned by the authentication
	// callback methods. The first entry is typically ErrNoAuth.
	Errors []error
}

func ( ServerAuthError) () string {
	var  []string
	for ,  := range .Errors {
		 = append(, .Error())
	}
	return "[" + strings.Join(, ", ") + "]"
}

// ErrNoAuth is the error value returned if no
// authentication method has been passed yet. This happens as a normal
// part of the authentication loop, since the client first tries
// 'none' authentication to discover available methods.
// It is returned in ServerAuthError.Errors from NewServerConn.
var ErrNoAuth = errors.New("ssh: no auth passed yet")

func ( *connection) ( *ServerConfig) (*Permissions, error) {
	 := .transport.getSessionID()
	var  pubKeyCache
	var  *Permissions

	 := 0
	var  []error
	var  bool

:
	for {
		if  >= .MaxAuthTries && .MaxAuthTries > 0 {
			 := &disconnectMsg{
				Reason:  2,
				Message: "too many authentication failures",
			}

			if  := .transport.writePacket(Marshal());  != nil {
				return nil, 
			}

			return nil, 
		}

		var  userAuthRequestMsg
		if ,  := .transport.readPacket();  != nil {
			if  == io.EOF {
				return nil, &ServerAuthError{Errors: }
			}
			return nil, 
		} else if  = Unmarshal(, &);  != nil {
			return nil, 
		}

		if .Service != serviceSSH {
			return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + .Service)
		}

		.user = .User

		if ! && .BannerCallback != nil {
			 = true
			 := .BannerCallback()
			if  != "" {
				 := &userAuthBannerMsg{
					Message: ,
				}
				if  := .transport.writePacket(Marshal());  != nil {
					return nil, 
				}
			}
		}

		 = nil
		 := ErrNoAuth

		switch .Method {
		case "none":
			if .NoClientAuth {
				if .NoClientAuthCallback != nil {
					,  = .NoClientAuthCallback()
				} else {
					 = nil
				}
			}

			// allow initial attempt of 'none' without penalty
			if  == 0 {
				--
			}
		case "password":
			if .PasswordCallback == nil {
				 = errors.New("ssh: password auth not configured")
				break
			}
			 := .Payload
			if len() < 1 || [0] != 0 {
				return nil, parseError(msgUserAuthRequest)
			}
			 = [1:]
			, ,  := parseString()
			if ! || len() > 0 {
				return nil, parseError(msgUserAuthRequest)
			}

			,  = .PasswordCallback(, )
		case "keyboard-interactive":
			if .KeyboardInteractiveCallback == nil {
				 = errors.New("ssh: keyboard-interactive auth not configured")
				break
			}

			 := &sshClientKeyboardInteractive{}
			,  = .KeyboardInteractiveCallback(, .Challenge)
		case "publickey":
			if .PublicKeyCallback == nil {
				 = errors.New("ssh: publickey auth not configured")
				break
			}
			 := .Payload
			if len() < 1 {
				return nil, parseError(msgUserAuthRequest)
			}
			 := [0] == 0
			 = [1:]
			, ,  := parseString()
			if ! {
				return nil, parseError(msgUserAuthRequest)
			}
			 := string()
			if !contains(supportedPubKeyAuthAlgos, underlyingAlgo()) {
				 = fmt.Errorf("ssh: algorithm %q not accepted", )
				break
			}

			, ,  := parseString()
			if ! {
				return nil, parseError(msgUserAuthRequest)
			}

			,  := ParsePublicKey()
			if  != nil {
				return nil, 
			}

			,  := .get(.user, )
			if ! {
				.user = .user
				.pubKeyData = 
				.perms, .result = .PublicKeyCallback(, )
				if .result == nil && .perms != nil && .perms.CriticalOptions != nil && .perms.CriticalOptions[sourceAddressCriticalOption] != "" {
					.result = checkSourceAddress(
						.RemoteAddr(),
						.perms.CriticalOptions[sourceAddressCriticalOption])
				}
				.add()
			}

			if  {
				// The client can query if the given public key
				// would be okay.

				if len() > 0 {
					return nil, parseError(msgUserAuthRequest)
				}

				if .result == nil {
					 := userAuthPubKeyOkMsg{
						Algo:   ,
						PubKey: ,
					}
					if  = .transport.writePacket(Marshal(&));  != nil {
						return nil, 
					}
					continue 
				}
				 = .result
			} else {
				, ,  := parseSignature()
				if ! || len() > 0 {
					return nil, parseError(msgUserAuthRequest)
				}
				// Ensure the declared public key algo is compatible with the
				// decoded one. This check will ensure we don't accept e.g.
				// ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public
				// key type. The algorithm and public key type must be
				// consistent: both must be certificate algorithms, or neither.
				if !contains(algorithmsForKeyFormat(.Type()), ) {
					 = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
						.Type(), )
					break
				}
				// Ensure the public key algo and signature algo
				// are supported.  Compare the private key
				// algorithm name that corresponds to algo with
				// sig.Format.  This is usually the same, but
				// for certs, the names differ.
				if !contains(supportedPubKeyAuthAlgos, .Format) {
					 = fmt.Errorf("ssh: algorithm %q not accepted", .Format)
					break
				}
				if !isAlgoCompatible(, .Format) {
					 = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", .Format, )
					break
				}

				 := buildDataSignedForAuth(, , , )

				if  := .Verify(, );  != nil {
					return nil, 
				}

				 = .result
				 = .perms
			}
		case "gssapi-with-mic":
			if .GSSAPIWithMICConfig == nil {
				 = errors.New("ssh: gssapi-with-mic auth not configured")
				break
			}
			 := .GSSAPIWithMICConfig
			,  := parseGSSAPIPayload(.Payload)
			if  != nil {
				return nil, parseError(msgUserAuthRequest)
			}
			// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication.
			if .N == 0 {
				 = fmt.Errorf("ssh: Mechanism negotiation is not supported")
				break
			}
			var  uint32
			 := false
			for  = 0;  < .N; ++ {
				if .OIDS[].Equal(krb5Mesh) {
					 = true
					break
				}
			}
			if ! {
				 = fmt.Errorf("ssh: GSSAPI authentication must use the Kerberos V5 mechanism")
				break
			}
			// Initial server response, see RFC 4462 section 3.3.
			if  := .transport.writePacket(Marshal(&userAuthGSSAPIResponse{
				SupportMech: krb5OID,
			}));  != nil {
				return nil, 
			}
			// Exchange token, see RFC 4462 section 3.4.
			,  := .transport.readPacket()
			if  != nil {
				return nil, 
			}
			 := &userAuthGSSAPIToken{}
			if  := Unmarshal(, );  != nil {
				return nil, 
			}
			, ,  = gssExchangeToken(, .Token, , ,
				)
			if  != nil {
				return nil, 
			}
		default:
			 = fmt.Errorf("ssh: unknown method %q", .Method)
		}

		 = append(, )

		if .AuthLogCallback != nil {
			.AuthLogCallback(, .Method, )
		}

		if  == nil {
			break 
		}

		++
		if .MaxAuthTries > 0 &&  >= .MaxAuthTries {
			// If we have hit the max attempts, don't bother sending the
			// final SSH_MSG_USERAUTH_FAILURE message, since there are
			// no more authentication methods which can be attempted,
			// and this message may cause the client to re-attempt
			// authentication while we send the disconnect message.
			// Continue, and trigger the disconnect at the start of
			// the loop.
			//
			// The SSH specification is somewhat confusing about this,
			// RFC 4252 Section 5.1 requires each authentication failure
			// be responded to with a respective SSH_MSG_USERAUTH_FAILURE
			// message, but Section 4 says the server should disconnect
			// after some number of attempts, but it isn't explicit which
			// message should take precedence (i.e. should there be a failure
			// message than a disconnect message, or if we are going to
			// disconnect, should we only send that message.)
			//
			// Either way, OpenSSH disconnects immediately after the last
			// failed authnetication attempt, and given they are typically
			// considered the golden implementation it seems reasonable
			// to match that behavior.
			continue
		}

		var  userAuthFailureMsg
		if .PasswordCallback != nil {
			.Methods = append(.Methods, "password")
		}
		if .PublicKeyCallback != nil {
			.Methods = append(.Methods, "publickey")
		}
		if .KeyboardInteractiveCallback != nil {
			.Methods = append(.Methods, "keyboard-interactive")
		}
		if .GSSAPIWithMICConfig != nil && .GSSAPIWithMICConfig.Server != nil &&
			.GSSAPIWithMICConfig.AllowLogin != nil {
			.Methods = append(.Methods, "gssapi-with-mic")
		}

		if len(.Methods) == 0 {
			return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
		}

		if  := .transport.writePacket(Marshal(&));  != nil {
			return nil, 
		}
	}

	if  := .transport.writePacket([]byte{msgUserAuthSuccess});  != nil {
		return nil, 
	}
	return , nil
}

// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
// asking the client on the other side of a ServerConn.
type sshClientKeyboardInteractive struct {
	*connection
}

func ( *sshClientKeyboardInteractive) (,  string,  []string,  []bool) ( []string,  error) {
	if len() != len() {
		return nil, errors.New("ssh: echos and questions must have equal length")
	}

	var  []byte
	for  := range  {
		 = appendString(, [])
		 = appendBool(, [])
	}

	if  := .transport.writePacket(Marshal(&userAuthInfoRequestMsg{
		Name:        ,
		Instruction: ,
		NumPrompts:  uint32(len()),
		Prompts:     ,
	}));  != nil {
		return nil, 
	}

	,  := .transport.readPacket()
	if  != nil {
		return nil, 
	}
	if [0] != msgUserAuthInfoResponse {
		return nil, unexpectedMessageError(msgUserAuthInfoResponse, [0])
	}
	 = [1:]

	, ,  := parseUint32()
	if ! || int() != len() {
		return nil, parseError(msgUserAuthInfoResponse)
	}

	for  := uint32(0);  < ; ++ {
		, ,  := parseString()
		if ! {
			return nil, parseError(msgUserAuthInfoResponse)
		}

		 = append(, string())
		 = 
	}
	if len() != 0 {
		return nil, errors.New("ssh: junk at end of message")
	}

	return , nil
}