@@ -71,7 +71,9 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
7171 for auth := AuthMethod (new (noneAuth )); auth != nil ; {
7272 ok , methods , err := auth .auth (sessionID , config .User , c .transport , config .Rand , extensions )
7373 if err != nil {
74- return err
74+ // We return the error later if there is no other method left to
75+ // try.
76+ ok = authFailure
7577 }
7678 if ok == authSuccess {
7779 // success
@@ -101,6 +103,12 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
101103 }
102104 }
103105 }
106+
107+ if auth == nil && err != nil {
108+ // We have an error and there are no other authentication methods to
109+ // try, so we return it.
110+ return err
111+ }
104112 }
105113 return fmt .Errorf ("ssh: unable to authenticate, attempted methods %v, no supported methods remain" , tried )
106114}
@@ -217,21 +225,45 @@ func (cb publicKeyCallback) method() string {
217225 return "publickey"
218226}
219227
220- func pickSignatureAlgorithm (signer Signer , extensions map [string ][]byte ) (as AlgorithmSigner , algo string ) {
228+ func pickSignatureAlgorithm (signer Signer , extensions map [string ][]byte ) (MultiAlgorithmSigner , string , error ) {
229+ var as MultiAlgorithmSigner
221230 keyFormat := signer .PublicKey ().Type ()
222231
223- // Like in sendKexInit, if the public key implements AlgorithmSigner we
224- // assume it supports all algorithms, otherwise only the key format one.
225- as , ok := signer .(AlgorithmSigner )
226- if ! ok {
227- return algorithmSignerWrapper {signer }, keyFormat
232+ // If the signer implements MultiAlgorithmSigner we use the algorithms it
233+ // support, if it implements AlgorithmSigner we assume it supports all
234+ // algorithms, otherwise only the key format one.
235+ switch s := signer .(type ) {
236+ case MultiAlgorithmSigner :
237+ as = s
238+ case AlgorithmSigner :
239+ as = & multiAlgorithmSigner {
240+ AlgorithmSigner : s ,
241+ supportedAlgorithms : algorithmsForKeyFormat (underlyingAlgo (keyFormat )),
242+ }
243+ default :
244+ as = & multiAlgorithmSigner {
245+ AlgorithmSigner : algorithmSignerWrapper {signer },
246+ supportedAlgorithms : []string {underlyingAlgo (keyFormat )},
247+ }
248+ }
249+
250+ getFallbackAlgo := func () (string , error ) {
251+ // Fallback to use if there is no "server-sig-algs" extension or a
252+ // common algorithm cannot be found. We use the public key format if the
253+ // MultiAlgorithmSigner supports it, otherwise we return an error.
254+ if ! contains (as .Algorithms (), underlyingAlgo (keyFormat )) {
255+ return "" , fmt .Errorf ("ssh: no common public key signature algorithm, server only supports %q for key type %q, signer only supports %v" ,
256+ underlyingAlgo (keyFormat ), keyFormat , as .Algorithms ())
257+ }
258+ return keyFormat , nil
228259 }
229260
230261 extPayload , ok := extensions ["server-sig-algs" ]
231262 if ! ok {
232- // If there is no "server-sig-algs" extension, fall back to the key
233- // format algorithm.
234- return as , keyFormat
263+ // If there is no "server-sig-algs" extension use the fallback
264+ // algorithm.
265+ algo , err := getFallbackAlgo ()
266+ return as , algo , err
235267 }
236268
237269 // The server-sig-algs extension only carries underlying signature
@@ -245,15 +277,22 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (as Alg
245277 }
246278 }
247279
248- keyAlgos := algorithmsForKeyFormat (keyFormat )
280+ // Filter algorithms based on those supported by MultiAlgorithmSigner.
281+ var keyAlgos []string
282+ for _ , algo := range algorithmsForKeyFormat (keyFormat ) {
283+ if contains (as .Algorithms (), underlyingAlgo (algo )) {
284+ keyAlgos = append (keyAlgos , algo )
285+ }
286+ }
287+
249288 algo , err := findCommon ("public key signature algorithm" , keyAlgos , serverAlgos )
250289 if err != nil {
251- // If there is no overlap, try the key anyway with the key format
252- // algorithm, to support servers that fail to list all supported
253- // algorithms.
254- return as , keyFormat
290+ // If there is no overlap, return the fallback algorithm to support
291+ // servers that fail to list all supported algorithms.
292+ algo , err := getFallbackAlgo ()
293+ return as , algo , err
255294 }
256- return as , algo
295+ return as , algo , nil
257296}
258297
259298func (cb publicKeyCallback ) auth (session []byte , user string , c packetConn , rand io.Reader , extensions map [string ][]byte ) (authResult , []string , error ) {
@@ -267,10 +306,17 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
267306 return authFailure , nil , err
268307 }
269308 var methods []string
309+ var errSigAlgo error
270310 for _ , signer := range signers {
271311 pub := signer .PublicKey ()
272- as , algo := pickSignatureAlgorithm (signer , extensions )
273-
312+ as , algo , err := pickSignatureAlgorithm (signer , extensions )
313+ if err != nil && errSigAlgo == nil {
314+ // If we cannot negotiate a signature algorithm store the first
315+ // error so we can return it to provide a more meaningful message if
316+ // no other signers work.
317+ errSigAlgo = err
318+ continue
319+ }
274320 ok , err := validateKey (pub , algo , user , c )
275321 if err != nil {
276322 return authFailure , nil , err
@@ -317,22 +363,12 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
317363 // contain the "publickey" method, do not attempt to authenticate with any
318364 // other keys. According to RFC 4252 Section 7, the latter can occur when
319365 // additional authentication methods are required.
320- if success == authSuccess || ! containsMethod (methods , cb .method ()) {
366+ if success == authSuccess || ! contains (methods , cb .method ()) {
321367 return success , methods , err
322368 }
323369 }
324370
325- return authFailure , methods , nil
326- }
327-
328- func containsMethod (methods []string , method string ) bool {
329- for _ , m := range methods {
330- if m == method {
331- return true
332- }
333- }
334-
335- return false
371+ return authFailure , methods , errSigAlgo
336372}
337373
338374// validateKey validates the key provided is acceptable to the server.
0 commit comments