mirror of
https://github.com/cheat/cheat.git
synced 2026-03-07 19:23:34 +01:00
chore: modernize CI and update Go toolchain
- Bump Go from 1.19 to 1.26 and update all dependencies - Rewrite CI workflow with matrix strategy (Linux, macOS, Windows) - Update GitHub Actions to current versions (checkout@v4, setup-go@v5) - Update CodeQL actions from v1 to v3 - Fix cross-platform bug in mock/path.go (path.Join -> filepath.Join) - Clean up dependabot config (weekly schedule, remove stale ignore) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
307
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
307
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -43,6 +44,9 @@ type Permissions struct {
|
||||
// pass data from the authentication callbacks to the server
|
||||
// application layer.
|
||||
Extensions map[string]string
|
||||
|
||||
// ExtraData allows to store user defined data.
|
||||
ExtraData map[any]any
|
||||
}
|
||||
|
||||
type GSSAPIWithMICConfig struct {
|
||||
@@ -59,6 +63,27 @@ type GSSAPIWithMICConfig struct {
|
||||
Server GSSAPIServer
|
||||
}
|
||||
|
||||
// SendAuthBanner implements [ServerPreAuthConn].
|
||||
func (s *connection) SendAuthBanner(msg string) error {
|
||||
return s.transport.writePacket(Marshal(&userAuthBannerMsg{
|
||||
Message: msg,
|
||||
}))
|
||||
}
|
||||
|
||||
func (*connection) unexportedMethodForFutureProofing() {}
|
||||
|
||||
// ServerPreAuthConn is the interface available on an incoming server
|
||||
// connection before authentication has completed.
|
||||
type ServerPreAuthConn interface {
|
||||
unexportedMethodForFutureProofing() // permits growing ServerPreAuthConn safely later, ala testing.TB
|
||||
|
||||
ConnMetadata
|
||||
|
||||
// SendAuthBanner sends a banner message to the client.
|
||||
// It returns an error once the authentication phase has ended.
|
||||
SendAuthBanner(string) error
|
||||
}
|
||||
|
||||
// ServerConfig holds server specific configuration data.
|
||||
type ServerConfig struct {
|
||||
// Config contains configuration shared between client and server.
|
||||
@@ -105,6 +130,21 @@ type ServerConfig struct {
|
||||
// Permissions.Extensions entry.
|
||||
PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
|
||||
|
||||
// VerifiedPublicKeyCallback, if non-nil, is called after a client
|
||||
// successfully confirms having control over a key that was previously
|
||||
// approved by PublicKeyCallback. The permissions object passed to the
|
||||
// callback is the one returned by PublicKeyCallback for the given public
|
||||
// key and its ownership is transferred to the callback. The returned
|
||||
// Permissions object can be the same object, optionally modified, or a
|
||||
// completely new object. If VerifiedPublicKeyCallback is non-nil,
|
||||
// PublicKeyCallback is not allowed to return a PartialSuccessError, which
|
||||
// can instead be returned by VerifiedPublicKeyCallback.
|
||||
//
|
||||
// VerifiedPublicKeyCallback does not affect which authentication methods
|
||||
// are included in the list of methods that can be attempted by the client.
|
||||
VerifiedPublicKeyCallback func(conn ConnMetadata, key PublicKey, permissions *Permissions,
|
||||
signatureAlgorithm string) (*Permissions, error)
|
||||
|
||||
// KeyboardInteractiveCallback, if non-nil, is called when
|
||||
// keyboard-interactive authentication is selected (RFC
|
||||
// 4256). The client object's Challenge function should be
|
||||
@@ -118,6 +158,12 @@ type ServerConfig struct {
|
||||
// attempts.
|
||||
AuthLogCallback func(conn ConnMetadata, method string, err error)
|
||||
|
||||
// PreAuthConnCallback, if non-nil, is called upon receiving a new connection
|
||||
// before any authentication has started. The provided ServerPreAuthConn
|
||||
// can be used at any time before authentication is complete, including
|
||||
// after this callback has returned.
|
||||
PreAuthConnCallback func(ServerPreAuthConn)
|
||||
|
||||
// ServerVersion is the version identification string to announce in
|
||||
// the public handshake.
|
||||
// If empty, a reasonable default is used.
|
||||
@@ -149,7 +195,7 @@ func (s *ServerConfig) AddHostKey(key Signer) {
|
||||
}
|
||||
|
||||
// cachedPubKey contains the results of querying whether a public key is
|
||||
// acceptable for a user.
|
||||
// acceptable for a user. This is a FIFO cache.
|
||||
type cachedPubKey struct {
|
||||
user string
|
||||
pubKeyData []byte
|
||||
@@ -157,7 +203,13 @@ type cachedPubKey struct {
|
||||
perms *Permissions
|
||||
}
|
||||
|
||||
const maxCachedPubKeys = 16
|
||||
// maxCachedPubKeys is the number of cache entries we store.
|
||||
//
|
||||
// Due to consistent misuse of the PublicKeyCallback API, we have reduced this
|
||||
// to 1, such that the only key in the cache is the most recently seen one. This
|
||||
// forces the behavior that the last call to PublicKeyCallback will always be
|
||||
// with the key that is used for authentication.
|
||||
const maxCachedPubKeys = 1
|
||||
|
||||
// pubKeyCache caches tests for public keys. Since SSH clients
|
||||
// will query whether a public key is acceptable before attempting to
|
||||
@@ -179,9 +231,10 @@ func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
|
||||
|
||||
// add adds the given tuple to the cache.
|
||||
func (c *pubKeyCache) add(candidate cachedPubKey) {
|
||||
if len(c.keys) < maxCachedPubKeys {
|
||||
c.keys = append(c.keys, candidate)
|
||||
if len(c.keys) >= maxCachedPubKeys {
|
||||
c.keys = c.keys[1:]
|
||||
}
|
||||
c.keys = append(c.keys, candidate)
|
||||
}
|
||||
|
||||
// ServerConn is an authenticated SSH connection, as seen from the
|
||||
@@ -209,22 +262,15 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
|
||||
fullConf.MaxAuthTries = 6
|
||||
}
|
||||
if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
|
||||
fullConf.PublicKeyAuthAlgorithms = supportedPubKeyAuthAlgos
|
||||
fullConf.PublicKeyAuthAlgorithms = defaultPubKeyAuthAlgos
|
||||
} else {
|
||||
for _, algo := range fullConf.PublicKeyAuthAlgorithms {
|
||||
if !contains(supportedPubKeyAuthAlgos, algo) {
|
||||
if !slices.Contains(SupportedAlgorithms().PublicKeyAuths, algo) && !slices.Contains(InsecureAlgorithms().PublicKeyAuths, algo) {
|
||||
c.Close()
|
||||
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if the config contains any unsupported key exchanges
|
||||
for _, kex := range fullConf.KeyExchanges {
|
||||
if _, ok := serverForbiddenKexAlgos[kex]; ok {
|
||||
c.Close()
|
||||
return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
|
||||
}
|
||||
}
|
||||
|
||||
s := &connection{
|
||||
sshConn: sshConn{conn: c},
|
||||
@@ -281,6 +327,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
||||
|
||||
// We just did the key change, so the session ID is established.
|
||||
s.sessionID = s.transport.getSessionID()
|
||||
s.algorithms = s.transport.getAlgorithms()
|
||||
|
||||
var packet []byte
|
||||
if packet, err = s.transport.readPacket(); err != nil {
|
||||
@@ -426,6 +473,35 @@ func (l ServerAuthError) Error() string {
|
||||
return "[" + strings.Join(errs, ", ") + "]"
|
||||
}
|
||||
|
||||
// ServerAuthCallbacks defines server-side authentication callbacks.
|
||||
type ServerAuthCallbacks struct {
|
||||
// PasswordCallback behaves like [ServerConfig.PasswordCallback].
|
||||
PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
|
||||
|
||||
// PublicKeyCallback behaves like [ServerConfig.PublicKeyCallback].
|
||||
PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
|
||||
|
||||
// KeyboardInteractiveCallback behaves like [ServerConfig.KeyboardInteractiveCallback].
|
||||
KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
|
||||
|
||||
// GSSAPIWithMICConfig behaves like [ServerConfig.GSSAPIWithMICConfig].
|
||||
GSSAPIWithMICConfig *GSSAPIWithMICConfig
|
||||
}
|
||||
|
||||
// PartialSuccessError can be returned by any of the [ServerConfig]
|
||||
// authentication callbacks to indicate to the client that authentication has
|
||||
// partially succeeded, but further steps are required.
|
||||
type PartialSuccessError struct {
|
||||
// Next defines the authentication callbacks to apply to further steps. The
|
||||
// available methods communicated to the client are based on the non-nil
|
||||
// ServerAuthCallbacks fields.
|
||||
Next ServerAuthCallbacks
|
||||
}
|
||||
|
||||
func (p *PartialSuccessError) Error() string {
|
||||
return "ssh: authenticated with partial success"
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -433,14 +509,46 @@ func (l ServerAuthError) Error() string {
|
||||
// It is returned in ServerAuthError.Errors from NewServerConn.
|
||||
var ErrNoAuth = errors.New("ssh: no auth passed yet")
|
||||
|
||||
// BannerError is an error that can be returned by authentication handlers in
|
||||
// ServerConfig to send a banner message to the client.
|
||||
type BannerError struct {
|
||||
Err error
|
||||
Message string
|
||||
}
|
||||
|
||||
func (b *BannerError) Unwrap() error {
|
||||
return b.Err
|
||||
}
|
||||
|
||||
func (b *BannerError) Error() string {
|
||||
if b.Err == nil {
|
||||
return b.Message
|
||||
}
|
||||
return b.Err.Error()
|
||||
}
|
||||
|
||||
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
|
||||
if config.PreAuthConnCallback != nil {
|
||||
config.PreAuthConnCallback(s)
|
||||
}
|
||||
|
||||
sessionID := s.transport.getSessionID()
|
||||
var cache pubKeyCache
|
||||
var perms *Permissions
|
||||
|
||||
authFailures := 0
|
||||
noneAuthCount := 0
|
||||
var authErrs []error
|
||||
var displayedBanner bool
|
||||
var calledBannerCallback bool
|
||||
partialSuccessReturned := false
|
||||
// Set the initial authentication callbacks from the config. They can be
|
||||
// changed if a PartialSuccessError is returned.
|
||||
authConfig := ServerAuthCallbacks{
|
||||
PasswordCallback: config.PasswordCallback,
|
||||
PublicKeyCallback: config.PublicKeyCallback,
|
||||
KeyboardInteractiveCallback: config.KeyboardInteractiveCallback,
|
||||
GSSAPIWithMICConfig: config.GSSAPIWithMICConfig,
|
||||
}
|
||||
|
||||
userAuthLoop:
|
||||
for {
|
||||
@@ -453,8 +561,8 @@ userAuthLoop:
|
||||
if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, discMsg
|
||||
authErrs = append(authErrs, discMsg)
|
||||
return nil, &ServerAuthError{Errors: authErrs}
|
||||
}
|
||||
|
||||
var userAuthReq userAuthRequestMsg
|
||||
@@ -471,16 +579,17 @@ userAuthLoop:
|
||||
return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
|
||||
}
|
||||
|
||||
if s.user != userAuthReq.User && partialSuccessReturned {
|
||||
return nil, fmt.Errorf("ssh: client changed the user after a partial success authentication, previous user %q, current user %q",
|
||||
s.user, userAuthReq.User)
|
||||
}
|
||||
|
||||
s.user = userAuthReq.User
|
||||
|
||||
if !displayedBanner && config.BannerCallback != nil {
|
||||
displayedBanner = true
|
||||
msg := config.BannerCallback(s)
|
||||
if msg != "" {
|
||||
bannerMsg := &userAuthBannerMsg{
|
||||
Message: msg,
|
||||
}
|
||||
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
|
||||
if !calledBannerCallback && config.BannerCallback != nil {
|
||||
calledBannerCallback = true
|
||||
if msg := config.BannerCallback(s); msg != "" {
|
||||
if err := s.SendAuthBanner(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -491,20 +600,18 @@ userAuthLoop:
|
||||
|
||||
switch userAuthReq.Method {
|
||||
case "none":
|
||||
if config.NoClientAuth {
|
||||
noneAuthCount++
|
||||
// We don't allow none authentication after a partial success
|
||||
// response.
|
||||
if config.NoClientAuth && !partialSuccessReturned {
|
||||
if config.NoClientAuthCallback != nil {
|
||||
perms, authErr = config.NoClientAuthCallback(s)
|
||||
} else {
|
||||
authErr = nil
|
||||
}
|
||||
}
|
||||
|
||||
// allow initial attempt of 'none' without penalty
|
||||
if authFailures == 0 {
|
||||
authFailures--
|
||||
}
|
||||
case "password":
|
||||
if config.PasswordCallback == nil {
|
||||
if authConfig.PasswordCallback == nil {
|
||||
authErr = errors.New("ssh: password auth not configured")
|
||||
break
|
||||
}
|
||||
@@ -518,17 +625,17 @@ userAuthLoop:
|
||||
return nil, parseError(msgUserAuthRequest)
|
||||
}
|
||||
|
||||
perms, authErr = config.PasswordCallback(s, password)
|
||||
perms, authErr = authConfig.PasswordCallback(s, password)
|
||||
case "keyboard-interactive":
|
||||
if config.KeyboardInteractiveCallback == nil {
|
||||
if authConfig.KeyboardInteractiveCallback == nil {
|
||||
authErr = errors.New("ssh: keyboard-interactive auth not configured")
|
||||
break
|
||||
}
|
||||
|
||||
prompter := &sshClientKeyboardInteractive{s}
|
||||
perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge)
|
||||
perms, authErr = authConfig.KeyboardInteractiveCallback(s, prompter.Challenge)
|
||||
case "publickey":
|
||||
if config.PublicKeyCallback == nil {
|
||||
if authConfig.PublicKeyCallback == nil {
|
||||
authErr = errors.New("ssh: publickey auth not configured")
|
||||
break
|
||||
}
|
||||
@@ -543,7 +650,7 @@ userAuthLoop:
|
||||
return nil, parseError(msgUserAuthRequest)
|
||||
}
|
||||
algo := string(algoBytes)
|
||||
if !contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
|
||||
if !slices.Contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
|
||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
|
||||
break
|
||||
}
|
||||
@@ -562,11 +669,21 @@ userAuthLoop:
|
||||
if !ok {
|
||||
candidate.user = s.user
|
||||
candidate.pubKeyData = pubKeyData
|
||||
candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey)
|
||||
if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
|
||||
candidate.result = checkSourceAddress(
|
||||
candidate.perms, candidate.result = authConfig.PublicKeyCallback(s, pubKey)
|
||||
_, isPartialSuccessError := candidate.result.(*PartialSuccessError)
|
||||
if isPartialSuccessError && config.VerifiedPublicKeyCallback != nil {
|
||||
return nil, errors.New("ssh: invalid library usage: PublicKeyCallback must not return partial success when VerifiedPublicKeyCallback is defined")
|
||||
}
|
||||
|
||||
if (candidate.result == nil || isPartialSuccessError) &&
|
||||
candidate.perms != nil &&
|
||||
candidate.perms.CriticalOptions != nil &&
|
||||
candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
|
||||
if err := checkSourceAddress(
|
||||
s.RemoteAddr(),
|
||||
candidate.perms.CriticalOptions[sourceAddressCriticalOption])
|
||||
candidate.perms.CriticalOptions[sourceAddressCriticalOption]); err != nil {
|
||||
candidate.result = err
|
||||
}
|
||||
}
|
||||
cache.add(candidate)
|
||||
}
|
||||
@@ -578,8 +695,8 @@ userAuthLoop:
|
||||
if len(payload) > 0 {
|
||||
return nil, parseError(msgUserAuthRequest)
|
||||
}
|
||||
|
||||
if candidate.result == nil {
|
||||
_, isPartialSuccessError := candidate.result.(*PartialSuccessError)
|
||||
if candidate.result == nil || isPartialSuccessError {
|
||||
okMsg := userAuthPubKeyOkMsg{
|
||||
Algo: algo,
|
||||
PubKey: pubKeyData,
|
||||
@@ -600,7 +717,7 @@ userAuthLoop:
|
||||
// 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(pubKey.Type()), algo) {
|
||||
if !slices.Contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
|
||||
authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
|
||||
pubKey.Type(), algo)
|
||||
break
|
||||
@@ -610,7 +727,7 @@ userAuthLoop:
|
||||
// algorithm name that corresponds to algo with
|
||||
// sig.Format. This is usually the same, but
|
||||
// for certs, the names differ.
|
||||
if !contains(config.PublicKeyAuthAlgorithms, sig.Format) {
|
||||
if !slices.Contains(config.PublicKeyAuthAlgorithms, sig.Format) {
|
||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
|
||||
break
|
||||
}
|
||||
@@ -627,13 +744,19 @@ userAuthLoop:
|
||||
|
||||
authErr = candidate.result
|
||||
perms = candidate.perms
|
||||
if authErr == nil && config.VerifiedPublicKeyCallback != nil {
|
||||
// Only call VerifiedPublicKeyCallback after the key has been accepted
|
||||
// and successfully verified. If authErr is non-nil, the key is not
|
||||
// considered verified and the callback must not run.
|
||||
perms, authErr = config.VerifiedPublicKeyCallback(s, pubKey, perms, algo)
|
||||
}
|
||||
}
|
||||
case "gssapi-with-mic":
|
||||
if config.GSSAPIWithMICConfig == nil {
|
||||
if authConfig.GSSAPIWithMICConfig == nil {
|
||||
authErr = errors.New("ssh: gssapi-with-mic auth not configured")
|
||||
break
|
||||
}
|
||||
gssapiConfig := config.GSSAPIWithMICConfig
|
||||
gssapiConfig := authConfig.GSSAPIWithMICConfig
|
||||
userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload)
|
||||
if err != nil {
|
||||
return nil, parseError(msgUserAuthRequest)
|
||||
@@ -685,53 +808,83 @@ userAuthLoop:
|
||||
config.AuthLogCallback(s, userAuthReq.Method, authErr)
|
||||
}
|
||||
|
||||
var bannerErr *BannerError
|
||||
if errors.As(authErr, &bannerErr) {
|
||||
if bannerErr.Message != "" {
|
||||
if err := s.SendAuthBanner(bannerErr.Message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if authErr == nil {
|
||||
break userAuthLoop
|
||||
}
|
||||
|
||||
authFailures++
|
||||
if config.MaxAuthTries > 0 && authFailures >= config.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 failureMsg userAuthFailureMsg
|
||||
|
||||
if partialSuccess, ok := authErr.(*PartialSuccessError); ok {
|
||||
// After a partial success error we don't allow changing the user
|
||||
// name and execute the NoClientAuthCallback.
|
||||
partialSuccessReturned = true
|
||||
|
||||
// In case a partial success is returned, the server may send
|
||||
// a new set of authentication methods.
|
||||
authConfig = partialSuccess.Next
|
||||
|
||||
// Reset pubkey cache, as the new PublicKeyCallback might
|
||||
// accept a different set of public keys.
|
||||
cache = pubKeyCache{}
|
||||
|
||||
// Send back a partial success message to the user.
|
||||
failureMsg.PartialSuccess = true
|
||||
} else {
|
||||
// Allow initial attempt of 'none' without penalty.
|
||||
if authFailures > 0 || userAuthReq.Method != "none" || noneAuthCount != 1 {
|
||||
authFailures++
|
||||
}
|
||||
if config.MaxAuthTries > 0 && authFailures >= config.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 authentication attempt, and given they are typically
|
||||
// considered the golden implementation it seems reasonable
|
||||
// to match that behavior.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
var failureMsg userAuthFailureMsg
|
||||
if config.PasswordCallback != nil {
|
||||
if authConfig.PasswordCallback != nil {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "password")
|
||||
}
|
||||
if config.PublicKeyCallback != nil {
|
||||
if authConfig.PublicKeyCallback != nil {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "publickey")
|
||||
}
|
||||
if config.KeyboardInteractiveCallback != nil {
|
||||
if authConfig.KeyboardInteractiveCallback != nil {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
|
||||
}
|
||||
if config.GSSAPIWithMICConfig != nil && config.GSSAPIWithMICConfig.Server != nil &&
|
||||
config.GSSAPIWithMICConfig.AllowLogin != nil {
|
||||
if authConfig.GSSAPIWithMICConfig != nil && authConfig.GSSAPIWithMICConfig.Server != nil &&
|
||||
authConfig.GSSAPIWithMICConfig.AllowLogin != nil {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic")
|
||||
}
|
||||
|
||||
if len(failureMsg.Methods) == 0 {
|
||||
return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
|
||||
return nil, errors.New("ssh: no authentication methods available")
|
||||
}
|
||||
|
||||
if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user