@@ -71,7 +71,9 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
71
71
for auth := AuthMethod (new (noneAuth )); auth != nil ; {
72
72
ok , methods , err := auth .auth (sessionID , config .User , c .transport , config .Rand , extensions )
73
73
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
75
77
}
76
78
if ok == authSuccess {
77
79
// success
@@ -101,6 +103,12 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
101
103
}
102
104
}
103
105
}
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
+ }
104
112
}
105
113
return fmt .Errorf ("ssh: unable to authenticate, attempted methods %v, no supported methods remain" , tried )
106
114
}
@@ -217,21 +225,45 @@ func (cb publicKeyCallback) method() string {
217
225
return "publickey"
218
226
}
219
227
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
221
230
keyFormat := signer .PublicKey ().Type ()
222
231
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
228
259
}
229
260
230
261
extPayload , ok := extensions ["server-sig-algs" ]
231
262
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
235
267
}
236
268
237
269
// The server-sig-algs extension only carries underlying signature
@@ -245,15 +277,22 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (as Alg
245
277
}
246
278
}
247
279
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
+
249
288
algo , err := findCommon ("public key signature algorithm" , keyAlgos , serverAlgos )
250
289
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
255
294
}
256
- return as , algo
295
+ return as , algo , nil
257
296
}
258
297
259
298
func (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
267
306
return authFailure , nil , err
268
307
}
269
308
var methods []string
309
+ var errSigAlgo error
270
310
for _ , signer := range signers {
271
311
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
+ }
274
320
ok , err := validateKey (pub , algo , user , c )
275
321
if err != nil {
276
322
return authFailure , nil , err
@@ -317,22 +363,12 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
317
363
// contain the "publickey" method, do not attempt to authenticate with any
318
364
// other keys. According to RFC 4252 Section 7, the latter can occur when
319
365
// additional authentication methods are required.
320
- if success == authSuccess || ! containsMethod (methods , cb .method ()) {
366
+ if success == authSuccess || ! contains (methods , cb .method ()) {
321
367
return success , methods , err
322
368
}
323
369
}
324
370
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
336
372
}
337
373
338
374
// validateKey validates the key provided is acceptable to the server.
0 commit comments