9
9
"errors"
10
10
"fmt"
11
11
"io"
12
+ "strings"
12
13
)
13
14
14
15
type authResult int
@@ -29,6 +30,33 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
29
30
if err != nil {
30
31
return err
31
32
}
33
+ // The server may choose to send a SSH_MSG_EXT_INFO at this point (if we
34
+ // advertised willingness to receive one, which we always do) or not. See
35
+ // RFC 8308, Section 2.4.
36
+ extensions := make (map [string ][]byte )
37
+ if len (packet ) > 0 && packet [0 ] == msgExtInfo {
38
+ var extInfo extInfoMsg
39
+ if err := Unmarshal (packet , & extInfo ); err != nil {
40
+ return err
41
+ }
42
+ payload := extInfo .Payload
43
+ for i := uint32 (0 ); i < extInfo .NumExtensions ; i ++ {
44
+ name , rest , ok := parseString (payload )
45
+ if ! ok {
46
+ return parseError (msgExtInfo )
47
+ }
48
+ value , rest , ok := parseString (rest )
49
+ if ! ok {
50
+ return parseError (msgExtInfo )
51
+ }
52
+ extensions [string (name )] = value
53
+ payload = rest
54
+ }
55
+ packet , err = c .transport .readPacket ()
56
+ if err != nil {
57
+ return err
58
+ }
59
+ }
32
60
var serviceAccept serviceAcceptMsg
33
61
if err := Unmarshal (packet , & serviceAccept ); err != nil {
34
62
return err
@@ -41,7 +69,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
41
69
42
70
sessionID := c .transport .getSessionID ()
43
71
for auth := AuthMethod (new (noneAuth )); auth != nil ; {
44
- ok , methods , err := auth .auth (sessionID , config .User , c .transport , config .Rand )
72
+ ok , methods , err := auth .auth (sessionID , config .User , c .transport , config .Rand , extensions )
45
73
if err != nil {
46
74
return err
47
75
}
@@ -93,7 +121,7 @@ type AuthMethod interface {
93
121
// If authentication is not successful, a []string of alternative
94
122
// method names is returned. If the slice is nil, it will be ignored
95
123
// and the previous set of possible methods will be reused.
96
- auth (session []byte , user string , p packetConn , rand io.Reader ) (authResult , []string , error )
124
+ auth (session []byte , user string , p packetConn , rand io.Reader , extensions map [ string ][] byte ) (authResult , []string , error )
97
125
98
126
// method returns the RFC 4252 method name.
99
127
method () string
@@ -102,7 +130,7 @@ type AuthMethod interface {
102
130
// "none" authentication, RFC 4252 section 5.2.
103
131
type noneAuth int
104
132
105
- func (n * noneAuth ) auth (session []byte , user string , c packetConn , rand io.Reader ) (authResult , []string , error ) {
133
+ func (n * noneAuth ) auth (session []byte , user string , c packetConn , rand io.Reader , _ map [ string ][] byte ) (authResult , []string , error ) {
106
134
if err := c .writePacket (Marshal (& userAuthRequestMsg {
107
135
User : user ,
108
136
Service : serviceSSH ,
@@ -122,7 +150,7 @@ func (n *noneAuth) method() string {
122
150
// a function call, e.g. by prompting the user.
123
151
type passwordCallback func () (password string , err error )
124
152
125
- func (cb passwordCallback ) auth (session []byte , user string , c packetConn , rand io.Reader ) (authResult , []string , error ) {
153
+ func (cb passwordCallback ) auth (session []byte , user string , c packetConn , rand io.Reader , _ map [ string ][] byte ) (authResult , []string , error ) {
126
154
type passwordAuthMsg struct {
127
155
User string `sshtype:"50"`
128
156
Service string
@@ -189,7 +217,36 @@ func (cb publicKeyCallback) method() string {
189
217
return "publickey"
190
218
}
191
219
192
- func (cb publicKeyCallback ) auth (session []byte , user string , c packetConn , rand io.Reader ) (authResult , []string , error ) {
220
+ func pickSignatureAlgorithm (signer Signer , extensions map [string ][]byte ) (as AlgorithmSigner , algo string ) {
221
+ keyFormat := signer .PublicKey ().Type ()
222
+
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
228
+ }
229
+
230
+ extPayload , ok := extensions ["server-sig-algs" ]
231
+ if ! ok {
232
+ // If there is no "server-sig-algs" extension, fall back to the key
233
+ // format algorithm.
234
+ return as , keyFormat
235
+ }
236
+
237
+ serverAlgos := strings .Split (string (extPayload ), "," )
238
+ keyAlgos := algorithmsForKeyFormat (keyFormat )
239
+ algo , err := findCommon ("public key signature algorithm" , keyAlgos , serverAlgos )
240
+ if err != nil {
241
+ // If there is no overlap, try the key anyway with the key format
242
+ // algorithm, to support servers that fail to list all supported
243
+ // algorithms.
244
+ return as , keyFormat
245
+ }
246
+ return as , algo
247
+ }
248
+
249
+ func (cb publicKeyCallback ) auth (session []byte , user string , c packetConn , rand io.Reader , extensions map [string ][]byte ) (authResult , []string , error ) {
193
250
// Authentication is performed by sending an enquiry to test if a key is
194
251
// acceptable to the remote. If the key is acceptable, the client will
195
252
// attempt to authenticate with the valid key. If not the client will repeat
@@ -201,21 +258,24 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
201
258
}
202
259
var methods []string
203
260
for _ , signer := range signers {
204
- ok , err := validateKey (signer .PublicKey (), user , c )
261
+ pub := signer .PublicKey ()
262
+ as , algo := pickSignatureAlgorithm (signer , extensions )
263
+
264
+ ok , err := validateKey (pub , algo , user , c )
205
265
if err != nil {
206
266
return authFailure , nil , err
207
267
}
208
268
if ! ok {
209
269
continue
210
270
}
211
271
212
- pub := signer .PublicKey ()
213
272
pubKey := pub .Marshal ()
214
- sign , err := signer . Sign ( rand , buildDataSignedForAuth (session , userAuthRequestMsg {
273
+ data := buildDataSignedForAuth (session , userAuthRequestMsg {
215
274
User : user ,
216
275
Service : serviceSSH ,
217
276
Method : cb .method (),
218
- }, []byte (pub .Type ()), pubKey ))
277
+ }, algo , pubKey )
278
+ sign , err := as .SignWithAlgorithm (rand , data , underlyingAlgo (algo ))
219
279
if err != nil {
220
280
return authFailure , nil , err
221
281
}
@@ -229,7 +289,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
229
289
Service : serviceSSH ,
230
290
Method : cb .method (),
231
291
HasSig : true ,
232
- Algoname : pub . Type () ,
292
+ Algoname : algo ,
233
293
PubKey : pubKey ,
234
294
Sig : sig ,
235
295
}
@@ -266,26 +326,25 @@ func containsMethod(methods []string, method string) bool {
266
326
}
267
327
268
328
// validateKey validates the key provided is acceptable to the server.
269
- func validateKey (key PublicKey , user string , c packetConn ) (bool , error ) {
329
+ func validateKey (key PublicKey , algo string , user string , c packetConn ) (bool , error ) {
270
330
pubKey := key .Marshal ()
271
331
msg := publickeyAuthMsg {
272
332
User : user ,
273
333
Service : serviceSSH ,
274
334
Method : "publickey" ,
275
335
HasSig : false ,
276
- Algoname : key . Type () ,
336
+ Algoname : algo ,
277
337
PubKey : pubKey ,
278
338
}
279
339
if err := c .writePacket (Marshal (& msg )); err != nil {
280
340
return false , err
281
341
}
282
342
283
- return confirmKeyAck (key , c )
343
+ return confirmKeyAck (key , algo , c )
284
344
}
285
345
286
- func confirmKeyAck (key PublicKey , c packetConn ) (bool , error ) {
346
+ func confirmKeyAck (key PublicKey , algo string , c packetConn ) (bool , error ) {
287
347
pubKey := key .Marshal ()
288
- algoname := key .Type ()
289
348
290
349
for {
291
350
packet , err := c .readPacket ()
@@ -302,14 +361,14 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
302
361
if err := Unmarshal (packet , & msg ); err != nil {
303
362
return false , err
304
363
}
305
- if msg .Algo != algoname || ! bytes .Equal (msg .PubKey , pubKey ) {
364
+ if msg .Algo != algo || ! bytes .Equal (msg .PubKey , pubKey ) {
306
365
return false , nil
307
366
}
308
367
return true , nil
309
368
case msgUserAuthFailure :
310
369
return false , nil
311
370
default :
312
- return false , unexpectedMessageError (msgUserAuthSuccess , packet [0 ])
371
+ return false , unexpectedMessageError (msgUserAuthPubKeyOk , packet [0 ])
313
372
}
314
373
}
315
374
}
@@ -330,6 +389,7 @@ func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMet
330
389
// along with a list of remaining authentication methods to try next and
331
390
// an error if an unexpected response was received.
332
391
func handleAuthResponse (c packetConn ) (authResult , []string , error ) {
392
+ gotMsgExtInfo := false
333
393
for {
334
394
packet , err := c .readPacket ()
335
395
if err != nil {
@@ -341,6 +401,12 @@ func handleAuthResponse(c packetConn) (authResult, []string, error) {
341
401
if err := handleBannerResponse (c , packet ); err != nil {
342
402
return authFailure , nil , err
343
403
}
404
+ case msgExtInfo :
405
+ // Ignore post-authentication RFC 8308 extensions, once.
406
+ if gotMsgExtInfo {
407
+ return authFailure , nil , unexpectedMessageError (msgUserAuthSuccess , packet [0 ])
408
+ }
409
+ gotMsgExtInfo = true
344
410
case msgUserAuthFailure :
345
411
var msg userAuthFailureMsg
346
412
if err := Unmarshal (packet , & msg ); err != nil {
@@ -395,7 +461,7 @@ func (cb KeyboardInteractiveChallenge) method() string {
395
461
return "keyboard-interactive"
396
462
}
397
463
398
- func (cb KeyboardInteractiveChallenge ) auth (session []byte , user string , c packetConn , rand io.Reader ) (authResult , []string , error ) {
464
+ func (cb KeyboardInteractiveChallenge ) auth (session []byte , user string , c packetConn , rand io.Reader , _ map [ string ][] byte ) (authResult , []string , error ) {
399
465
type initiateMsg struct {
400
466
User string `sshtype:"50"`
401
467
Service string
@@ -412,6 +478,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
412
478
return authFailure , nil , err
413
479
}
414
480
481
+ gotMsgExtInfo := false
415
482
for {
416
483
packet , err := c .readPacket ()
417
484
if err != nil {
@@ -425,6 +492,13 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
425
492
return authFailure , nil , err
426
493
}
427
494
continue
495
+ case msgExtInfo :
496
+ // Ignore post-authentication RFC 8308 extensions, once.
497
+ if gotMsgExtInfo {
498
+ return authFailure , nil , unexpectedMessageError (msgUserAuthInfoRequest , packet [0 ])
499
+ }
500
+ gotMsgExtInfo = true
501
+ continue
428
502
case msgUserAuthInfoRequest :
429
503
// OK
430
504
case msgUserAuthFailure :
@@ -497,9 +571,9 @@ type retryableAuthMethod struct {
497
571
maxTries int
498
572
}
499
573
500
- func (r * retryableAuthMethod ) auth (session []byte , user string , c packetConn , rand io.Reader ) (ok authResult , methods []string , err error ) {
574
+ func (r * retryableAuthMethod ) auth (session []byte , user string , c packetConn , rand io.Reader , extensions map [ string ][] byte ) (ok authResult , methods []string , err error ) {
501
575
for i := 0 ; r .maxTries <= 0 || i < r .maxTries ; i ++ {
502
- ok , methods , err = r .authMethod .auth (session , user , c , rand )
576
+ ok , methods , err = r .authMethod .auth (session , user , c , rand , extensions )
503
577
if ok != authFailure || err != nil { // either success, partial success or error terminate
504
578
return ok , methods , err
505
579
}
@@ -542,7 +616,7 @@ type gssAPIWithMICCallback struct {
542
616
target string
543
617
}
544
618
545
- func (g * gssAPIWithMICCallback ) auth (session []byte , user string , c packetConn , rand io.Reader ) (authResult , []string , error ) {
619
+ func (g * gssAPIWithMICCallback ) auth (session []byte , user string , c packetConn , rand io.Reader , _ map [ string ][] byte ) (authResult , []string , error ) {
546
620
m := & userAuthRequestMsg {
547
621
User : user ,
548
622
Service : serviceSSH ,
0 commit comments