@@ -76,6 +76,7 @@ type ServerConfig struct {
76
76
// attempts to authenticate with auth method "none".
77
77
// NoClientAuth must also be set to true for this be used, or
78
78
// this func is unused.
79
+ // If the function returns ErrDenied, the connection is terminated.
79
80
NoClientAuthCallback func (ConnMetadata ) (* Permissions , error )
80
81
81
82
// MaxAuthTries specifies the maximum number of authentication attempts
@@ -86,6 +87,7 @@ type ServerConfig struct {
86
87
87
88
// PasswordCallback, if non-nil, is called when a user
88
89
// attempts to authenticate using a password.
90
+ // If the function returns ErrDenied, the connection is terminated.
89
91
PasswordCallback func (conn ConnMetadata , password []byte ) (* Permissions , error )
90
92
91
93
// PublicKeyCallback, if non-nil, is called when a client
@@ -96,6 +98,7 @@ type ServerConfig struct {
96
98
// offered is in fact used to authenticate. To record any data
97
99
// depending on the public key, store it inside a
98
100
// Permissions.Extensions entry.
101
+ // If the function returns ErrDenied, the connection is terminated.
99
102
PublicKeyCallback func (conn ConnMetadata , key PublicKey ) (* Permissions , error )
100
103
101
104
// KeyboardInteractiveCallback, if non-nil, is called when
@@ -105,6 +108,7 @@ type ServerConfig struct {
105
108
// Challenge rounds. To avoid information leaks, the client
106
109
// should be presented a challenge even if the user is
107
110
// unknown.
111
+ // If the function returns ErrDenied, the connection is terminated.
108
112
KeyboardInteractiveCallback func (conn ConnMetadata , client KeyboardInteractiveChallenge ) (* Permissions , error )
109
113
110
114
// AuthLogCallback, if non-nil, is called to log all authentication
@@ -397,12 +401,19 @@ func (l ServerAuthError) Error() string {
397
401
return "[" + strings .Join (errs , ", " ) + "]"
398
402
}
399
403
400
- // ErrNoAuth is the error value returned if no
401
- // authentication method has been passed yet. This happens as a normal
402
- // part of the authentication loop, since the client first tries
403
- // 'none' authentication to discover available methods.
404
- // It is returned in ServerAuthError.Errors from NewServerConn.
405
- var ErrNoAuth = errors .New ("ssh: no auth passed yet" )
404
+ var (
405
+ // ErrDenied can be returned from an authentication callback to inform the
406
+ // client that access is denied and that no further attempt will be accepted
407
+ // on the connection.
408
+ ErrDenied = errors .New ("ssh: access denied" )
409
+
410
+ // ErrNoAuth is the error value returned if no
411
+ // authentication method has been passed yet. This happens as a normal
412
+ // part of the authentication loop, since the client first tries
413
+ // 'none' authentication to discover available methods.
414
+ // It is returned in ServerAuthError.Errors from NewServerConn.
415
+ ErrNoAuth = errors .New ("ssh: no auth passed yet" )
416
+ )
406
417
407
418
func (s * connection ) serverAuthenticate (config * ServerConfig ) (* Permissions , error ) {
408
419
sessionID := s .transport .getSessionID ()
@@ -651,6 +662,15 @@ userAuthLoop:
651
662
break userAuthLoop
652
663
}
653
664
665
+ if errors .Is (authErr , ErrDenied ) {
666
+ var failureMsg userAuthFailureMsg
667
+ if err := s .transport .writePacket (Marshal (failureMsg )); err != nil {
668
+ return nil , err
669
+ }
670
+
671
+ return nil , nil
672
+ }
673
+
654
674
authFailures ++
655
675
if config .MaxAuthTries > 0 && authFailures >= config .MaxAuthTries {
656
676
// If we have hit the max attempts, don't bother sending the
0 commit comments