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

type authResult int

const (
	authFailure authResult = iota
	authPartialSuccess
	authSuccess
)

// clientAuthenticate authenticates with the remote server. See RFC 4252.
func ( *connection) ( *ClientConfig) error {
	// initiate user auth session
	if  := .transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth}));  != nil {
		return 
	}
	,  := .transport.readPacket()
	if  != nil {
		return 
	}
	// The server may choose to send a SSH_MSG_EXT_INFO at this point (if we
	// advertised willingness to receive one, which we always do) or not. See
	// RFC 8308, Section 2.4.
	 := make(map[string][]byte)
	if len() > 0 && [0] == msgExtInfo {
		var  extInfoMsg
		if  := Unmarshal(, &);  != nil {
			return 
		}
		 := .Payload
		for  := uint32(0);  < .NumExtensions; ++ {
			, ,  := parseString()
			if ! {
				return parseError(msgExtInfo)
			}
			, ,  := parseString()
			if ! {
				return parseError(msgExtInfo)
			}
			[string()] = 
			 = 
		}
		,  = .transport.readPacket()
		if  != nil {
			return 
		}
	}
	var  serviceAcceptMsg
	if  := Unmarshal(, &);  != nil {
		return 
	}

	// during the authentication phase the client first attempts the "none" method
	// then any untried methods suggested by the server.
	var  []string
	var  []string

	 := .transport.getSessionID()
	for  := AuthMethod(new(noneAuth));  != nil; {
		, ,  := .auth(, .User, .transport, .Rand, )
		if  != nil {
			// We return the error later if there is no other method left to
			// try.
			 = authFailure
		}
		if  == authSuccess {
			// success
			return nil
		} else if  == authFailure {
			if  := .method(); !contains(, ) {
				 = append(, )
			}
		}
		if  == nil {
			 = 
		}
		 = 

		 = nil

	:
		for ,  := range .Auth {
			 := .method()
			if contains(, ) {
				continue
			}
			for ,  := range  {
				if  ==  {
					 = 
					break 
				}
			}
		}

		if  == nil &&  != nil {
			// We have an error and there are no other authentication methods to
			// try, so we return it.
			return 
		}
	}
	return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", )
}

func contains( []string,  string) bool {
	for ,  := range  {
		if  ==  {
			return true
		}
	}
	return false
}

// An AuthMethod represents an instance of an RFC 4252 authentication method.
type AuthMethod interface {
	// auth authenticates user over transport t.
	// Returns true if authentication is successful.
	// If authentication is not successful, a []string of alternative
	// method names is returned. If the slice is nil, it will be ignored
	// and the previous set of possible methods will be reused.
	auth(session []byte, user string, p packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error)

	// method returns the RFC 4252 method name.
	method() string
}

// "none" authentication, RFC 4252 section 5.2.
type noneAuth int

func ( *noneAuth) ( []byte,  string,  packetConn,  io.Reader,  map[string][]byte) (authResult, []string, error) {
	if  := .writePacket(Marshal(&userAuthRequestMsg{
		User:    ,
		Service: serviceSSH,
		Method:  "none",
	}));  != nil {
		return authFailure, nil, 
	}

	return handleAuthResponse()
}

func ( *noneAuth) () string {
	return "none"
}

// passwordCallback is an AuthMethod that fetches the password through
// a function call, e.g. by prompting the user.
type passwordCallback func() (password string, err error)

func ( passwordCallback) ( []byte,  string,  packetConn,  io.Reader,  map[string][]byte) (authResult, []string, error) {
	type  struct {
		     string `sshtype:"50"`
		  string
		   string
		    bool
		 string
	}

	,  := ()
	// REVIEW NOTE: is there a need to support skipping a password attempt?
	// The program may only find out that the user doesn't have a password
	// when prompting.
	if  != nil {
		return authFailure, nil, 
	}

	if  := .writePacket(Marshal(&{
		:     ,
		:  serviceSSH,
		:   .method(),
		:    false,
		: ,
	}));  != nil {
		return authFailure, nil, 
	}

	return handleAuthResponse()
}

func ( passwordCallback) () string {
	return "password"
}

// Password returns an AuthMethod using the given password.
func ( string) AuthMethod {
	return passwordCallback(func() (string, error) { return , nil })
}

// PasswordCallback returns an AuthMethod that uses a callback for
// fetching a password.
func ( func() ( string,  error)) AuthMethod {
	return passwordCallback()
}

type publickeyAuthMsg struct {
	User    string `sshtype:"50"`
	Service string
	Method  string
	// HasSig indicates to the receiver packet that the auth request is signed and
	// should be used for authentication of the request.
	HasSig   bool
	Algoname string
	PubKey   []byte
	// Sig is tagged with "rest" so Marshal will exclude it during
	// validateKey
	Sig []byte `ssh:"rest"`
}

// publicKeyCallback is an AuthMethod that uses a set of key
// pairs for authentication.
type publicKeyCallback func() ([]Signer, error)

func ( publicKeyCallback) () string {
	return "publickey"
}

func pickSignatureAlgorithm( Signer,  map[string][]byte) (MultiAlgorithmSigner, string, error) {
	var  MultiAlgorithmSigner
	 := .PublicKey().Type()

	// If the signer implements MultiAlgorithmSigner we use the algorithms it
	// support, if it implements AlgorithmSigner we assume it supports all
	// algorithms, otherwise only the key format one.
	switch s := .(type) {
	case MultiAlgorithmSigner:
		 = 
	case AlgorithmSigner:
		 = &multiAlgorithmSigner{
			AlgorithmSigner:     ,
			supportedAlgorithms: algorithmsForKeyFormat(underlyingAlgo()),
		}
	default:
		 = &multiAlgorithmSigner{
			AlgorithmSigner:     algorithmSignerWrapper{},
			supportedAlgorithms: []string{underlyingAlgo()},
		}
	}

	 := func() (string, error) {
		// Fallback to use if there is no "server-sig-algs" extension or a
		// common algorithm cannot be found. We use the public key format if the
		// MultiAlgorithmSigner supports it, otherwise we return an error.
		if !contains(.Algorithms(), underlyingAlgo()) {
			return "", fmt.Errorf("ssh: no common public key signature algorithm, server only supports %q for key type %q, signer only supports %v",
				underlyingAlgo(), , .Algorithms())
		}
		return , nil
	}

	,  := ["server-sig-algs"]
	if ! {
		// If there is no "server-sig-algs" extension use the fallback
		// algorithm.
		,  := ()
		return , , 
	}

	// The server-sig-algs extension only carries underlying signature
	// algorithm, but we are trying to select a protocol-level public key
	// algorithm, which might be a certificate type. Extend the list of server
	// supported algorithms to include the corresponding certificate algorithms.
	 := strings.Split(string(), ",")
	for ,  := range  {
		if ,  := certificateAlgo();  {
			 = append(, )
		}
	}

	// Filter algorithms based on those supported by MultiAlgorithmSigner.
	var  []string
	for ,  := range algorithmsForKeyFormat() {
		if contains(.Algorithms(), underlyingAlgo()) {
			 = append(, )
		}
	}

	,  := findCommon("public key signature algorithm", , )
	if  != nil {
		// If there is no overlap, return the fallback algorithm to support
		// servers that fail to list all supported algorithms.
		,  := ()
		return , , 
	}
	return , , nil
}

func ( publicKeyCallback) ( []byte,  string,  packetConn,  io.Reader,  map[string][]byte) (authResult, []string, error) {
	// Authentication is performed by sending an enquiry to test if a key is
	// acceptable to the remote. If the key is acceptable, the client will
	// attempt to authenticate with the valid key.  If not the client will repeat
	// the process with the remaining keys.

	,  := ()
	if  != nil {
		return authFailure, nil, 
	}
	var  []string
	var  error
	for ,  := range  {
		 := .PublicKey()
		, ,  := pickSignatureAlgorithm(, )
		if  != nil &&  == nil {
			// If we cannot negotiate a signature algorithm store the first
			// error so we can return it to provide a more meaningful message if
			// no other signers work.
			 = 
			continue
		}
		,  := validateKey(, , , )
		if  != nil {
			return authFailure, nil, 
		}
		if ! {
			continue
		}

		 := .Marshal()
		 := buildDataSignedForAuth(, userAuthRequestMsg{
			User:    ,
			Service: serviceSSH,
			Method:  .method(),
		}, , )
		,  := .SignWithAlgorithm(, , underlyingAlgo())
		if  != nil {
			return authFailure, nil, 
		}

		// manually wrap the serialized signature in a string
		 := Marshal()
		 := make([]byte, stringLength(len()))
		marshalString(, )
		 := publickeyAuthMsg{
			User:     ,
			Service:  serviceSSH,
			Method:   .method(),
			HasSig:   true,
			Algoname: ,
			PubKey:   ,
			Sig:      ,
		}
		 := Marshal(&)
		if  := .writePacket();  != nil {
			return authFailure, nil, 
		}
		var  authResult
		, ,  = handleAuthResponse()
		if  != nil {
			return authFailure, nil, 
		}

		// If authentication succeeds or the list of available methods does not
		// contain the "publickey" method, do not attempt to authenticate with any
		// other keys.  According to RFC 4252 Section 7, the latter can occur when
		// additional authentication methods are required.
		if  == authSuccess || !contains(, .method()) {
			return , , 
		}
	}

	return authFailure, , 
}

// validateKey validates the key provided is acceptable to the server.
func validateKey( PublicKey,  string,  string,  packetConn) (bool, error) {
	 := .Marshal()
	 := publickeyAuthMsg{
		User:     ,
		Service:  serviceSSH,
		Method:   "publickey",
		HasSig:   false,
		Algoname: ,
		PubKey:   ,
	}
	if  := .writePacket(Marshal(&));  != nil {
		return false, 
	}

	return confirmKeyAck(, , )
}

func confirmKeyAck( PublicKey,  string,  packetConn) (bool, error) {
	 := .Marshal()

	for {
		,  := .readPacket()
		if  != nil {
			return false, 
		}
		switch [0] {
		case msgUserAuthBanner:
			if  := handleBannerResponse(, );  != nil {
				return false, 
			}
		case msgUserAuthPubKeyOk:
			var  userAuthPubKeyOkMsg
			if  := Unmarshal(, &);  != nil {
				return false, 
			}
			if .Algo !=  || !bytes.Equal(.PubKey, ) {
				return false, nil
			}
			return true, nil
		case msgUserAuthFailure:
			return false, nil
		default:
			return false, unexpectedMessageError(msgUserAuthPubKeyOk, [0])
		}
	}
}

// PublicKeys returns an AuthMethod that uses the given key
// pairs.
func ( ...Signer) AuthMethod {
	return publicKeyCallback(func() ([]Signer, error) { return , nil })
}

// PublicKeysCallback returns an AuthMethod that runs the given
// function to obtain a list of key pairs.
func ( func() ( []Signer,  error)) AuthMethod {
	return publicKeyCallback()
}

// handleAuthResponse returns whether the preceding authentication request succeeded
// along with a list of remaining authentication methods to try next and
// an error if an unexpected response was received.
func handleAuthResponse( packetConn) (authResult, []string, error) {
	 := false
	for {
		,  := .readPacket()
		if  != nil {
			return authFailure, nil, 
		}

		switch [0] {
		case msgUserAuthBanner:
			if  := handleBannerResponse(, );  != nil {
				return authFailure, nil, 
			}
		case msgExtInfo:
			// Ignore post-authentication RFC 8308 extensions, once.
			if  {
				return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, [0])
			}
			 = true
		case msgUserAuthFailure:
			var  userAuthFailureMsg
			if  := Unmarshal(, &);  != nil {
				return authFailure, nil, 
			}
			if .PartialSuccess {
				return authPartialSuccess, .Methods, nil
			}
			return authFailure, .Methods, nil
		case msgUserAuthSuccess:
			return authSuccess, nil, nil
		default:
			return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, [0])
		}
	}
}

func handleBannerResponse( packetConn,  []byte) error {
	var  userAuthBannerMsg
	if  := Unmarshal(, &);  != nil {
		return 
	}

	,  := .(*handshakeTransport)
	if ! {
		return nil
	}

	if .bannerCallback != nil {
		return .bannerCallback(.Message)
	}

	return nil
}

// KeyboardInteractiveChallenge should print questions, optionally
// disabling echoing (e.g. for passwords), and return all the answers.
// Challenge may be called multiple times in a single session. After
// successful authentication, the server may send a challenge with no
// questions, for which the name and instruction messages should be
// printed.  RFC 4256 section 3.3 details how the UI should behave for
// both CLI and GUI environments.
type KeyboardInteractiveChallenge func(name, instruction string, questions []string, echos []bool) (answers []string, err error)

// KeyboardInteractive returns an AuthMethod using a prompt/response
// sequence controlled by the server.
func ( KeyboardInteractiveChallenge) AuthMethod {
	return 
}

func ( KeyboardInteractiveChallenge) () string {
	return "keyboard-interactive"
}

func ( KeyboardInteractiveChallenge) ( []byte,  string,  packetConn,  io.Reader,  map[string][]byte) (authResult, []string, error) {
	type  struct {
		       string `sshtype:"50"`
		    string
		     string
		   string
		 string
	}

	if  := .writePacket(Marshal(&{
		:    ,
		: serviceSSH,
		:  "keyboard-interactive",
	}));  != nil {
		return authFailure, nil, 
	}

	 := false
	for {
		,  := .readPacket()
		if  != nil {
			return authFailure, nil, 
		}

		// like handleAuthResponse, but with less options.
		switch [0] {
		case msgUserAuthBanner:
			if  := handleBannerResponse(, );  != nil {
				return authFailure, nil, 
			}
			continue
		case msgExtInfo:
			// Ignore post-authentication RFC 8308 extensions, once.
			if  {
				return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, [0])
			}
			 = true
			continue
		case msgUserAuthInfoRequest:
			// OK
		case msgUserAuthFailure:
			var  userAuthFailureMsg
			if  := Unmarshal(, &);  != nil {
				return authFailure, nil, 
			}
			if .PartialSuccess {
				return authPartialSuccess, .Methods, nil
			}
			return authFailure, .Methods, nil
		case msgUserAuthSuccess:
			return authSuccess, nil, nil
		default:
			return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, [0])
		}

		var  userAuthInfoRequestMsg
		if  := Unmarshal(, &);  != nil {
			return authFailure, nil, 
		}

		// Manually unpack the prompt/echo pairs.
		 := .Prompts
		var  []string
		var  []bool
		for  := 0;  < int(.NumPrompts); ++ {
			, ,  := parseString()
			if ! || len() == 0 {
				return authFailure, nil, errors.New("ssh: prompt format error")
			}
			 = append(, string())
			 = append(, [0] != 0)
			 = [1:]
		}

		if len() != 0 {
			return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
		}

		,  := (.Name, .Instruction, , )
		if  != nil {
			return authFailure, nil, 
		}

		if len() != len() {
			return authFailure, nil, fmt.Errorf("ssh: incorrect number of answers from keyboard-interactive callback %d (expected %d)", len(), len())
		}
		 := 1 + 4
		for ,  := range  {
			 += stringLength(len())
		}
		 := make([]byte, )
		 := 
		[0] = msgUserAuthInfoResponse
		 = [1:]
		 = marshalUint32(, uint32(len()))
		for ,  := range  {
			 = marshalString(, []byte())
		}

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

type retryableAuthMethod struct {
	authMethod AuthMethod
	maxTries   int
}

func ( *retryableAuthMethod) ( []byte,  string,  packetConn,  io.Reader,  map[string][]byte) ( authResult,  []string,  error) {
	for  := 0; .maxTries <= 0 ||  < .maxTries; ++ {
		, ,  = .authMethod.auth(, , , , )
		if  != authFailure ||  != nil { // either success, partial success or error terminate
			return , , 
		}
	}
	return , , 
}

func ( *retryableAuthMethod) () string {
	return .authMethod.method()
}

// RetryableAuthMethod is a decorator for other auth methods enabling them to
// be retried up to maxTries before considering that AuthMethod itself failed.
// If maxTries is <= 0, will retry indefinitely
//
// This is useful for interactive clients using challenge/response type
// authentication (e.g. Keyboard-Interactive, Password, etc) where the user
// could mistype their response resulting in the server issuing a
// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
// [keyboard-interactive]); Without this decorator, the non-retryable
// AuthMethod would be removed from future consideration, and never tried again
// (and so the user would never be able to retry their entry).
func ( AuthMethod,  int) AuthMethod {
	return &retryableAuthMethod{authMethod: , maxTries: }
}

// GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication.
// See RFC 4462 section 3
// gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details.
// target is the server host you want to log in to.
func ( GSSAPIClient,  string) AuthMethod {
	if  == nil {
		panic("gss-api client must be not nil with enable gssapi-with-mic")
	}
	return &gssAPIWithMICCallback{gssAPIClient: , target: }
}

type gssAPIWithMICCallback struct {
	gssAPIClient GSSAPIClient
	target       string
}

func ( *gssAPIWithMICCallback) ( []byte,  string,  packetConn,  io.Reader,  map[string][]byte) (authResult, []string, error) {
	 := &userAuthRequestMsg{
		User:    ,
		Service: serviceSSH,
		Method:  .method(),
	}
	// The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST.
	// See RFC 4462 section 3.2.
	.Payload = appendU32(.Payload, 1)
	.Payload = appendString(.Payload, string(krb5OID))
	if  := .writePacket(Marshal());  != nil {
		return authFailure, nil, 
	}
	// The server responds to the SSH_MSG_USERAUTH_REQUEST with either an
	// SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or
	// with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE.
	// See RFC 4462 section 3.3.
	// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check
	// selected mech if it is valid.
	,  := .readPacket()
	if  != nil {
		return authFailure, nil, 
	}
	 := &userAuthGSSAPIResponse{}
	if  := Unmarshal(, );  != nil {
		return authFailure, nil, 
	}
	// Start the loop into the exchange token.
	// See RFC 4462 section 3.4.
	var  []byte
	defer .gssAPIClient.DeleteSecContext()
	for {
		// Initiates the establishment of a security context between the application and a remote peer.
		, ,  := .gssAPIClient.InitSecContext("host@"+.target, , false)
		if  != nil {
			return authFailure, nil, 
		}
		if len() > 0 {
			if  := .writePacket(Marshal(&userAuthGSSAPIToken{
				Token: ,
			}));  != nil {
				return authFailure, nil, 
			}
		}
		if ! {
			break
		}
		,  = .readPacket()
		if  != nil {
			return authFailure, nil, 
		}
		switch [0] {
		case msgUserAuthFailure:
			var  userAuthFailureMsg
			if  := Unmarshal(, &);  != nil {
				return authFailure, nil, 
			}
			if .PartialSuccess {
				return authPartialSuccess, .Methods, nil
			}
			return authFailure, .Methods, nil
		case msgUserAuthGSSAPIError:
			 := &userAuthGSSAPIError{}
			if  := Unmarshal(, );  != nil {
				return authFailure, nil, 
			}
			return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+
				"Major Status: %d\n"+
				"Minor Status: %d\n"+
				"Error Message: %s\n", .MajorStatus, .MinorStatus,
				.Message)
		case msgUserAuthGSSAPIToken:
			 := &userAuthGSSAPIToken{}
			if  := Unmarshal(, );  != nil {
				return authFailure, nil, 
			}
			 = .Token
		}
	}
	// Binding Encryption Keys.
	// See RFC 4462 section 3.5.
	 := buildMIC(string(), , "ssh-connection", "gssapi-with-mic")
	,  := .gssAPIClient.GetMIC()
	if  != nil {
		return authFailure, nil, 
	}
	if  := .writePacket(Marshal(&userAuthGSSAPIMIC{
		MIC: ,
	}));  != nil {
		return authFailure, nil, 
	}
	return handleAuthResponse()
}

func ( *gssAPIWithMICCallback) () string {
	return "gssapi-with-mic"
}