99 "errors"
1010 "fmt"
1111 "io"
12+ "strings"
1213)
1314
1415type authResult int
@@ -29,6 +30,33 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
2930 if err != nil {
3031 return err
3132 }
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+ }
3260 var serviceAccept serviceAcceptMsg
3361 if err := Unmarshal (packet , & serviceAccept ); err != nil {
3462 return err
@@ -41,7 +69,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
4169
4270 sessionID := c .transport .getSessionID ()
4371 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 )
4573 if err != nil {
4674 return err
4775 }
@@ -93,7 +121,7 @@ type AuthMethod interface {
93121 // If authentication is not successful, a []string of alternative
94122 // method names is returned. If the slice is nil, it will be ignored
95123 // 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 )
97125
98126 // method returns the RFC 4252 method name.
99127 method () string
@@ -102,7 +130,7 @@ type AuthMethod interface {
102130// "none" authentication, RFC 4252 section 5.2.
103131type noneAuth int
104132
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 ) {
106134 if err := c .writePacket (Marshal (& userAuthRequestMsg {
107135 User : user ,
108136 Service : serviceSSH ,
@@ -122,7 +150,7 @@ func (n *noneAuth) method() string {
122150// a function call, e.g. by prompting the user.
123151type passwordCallback func () (password string , err error )
124152
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 ) {
126154 type passwordAuthMsg struct {
127155 User string `sshtype:"50"`
128156 Service string
@@ -189,7 +217,36 @@ func (cb publicKeyCallback) method() string {
189217 return "publickey"
190218}
191219
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 ) {
193250 // Authentication is performed by sending an enquiry to test if a key is
194251 // acceptable to the remote. If the key is acceptable, the client will
195252 // 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
201258 }
202259 var methods []string
203260 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 )
205265 if err != nil {
206266 return authFailure , nil , err
207267 }
208268 if ! ok {
209269 continue
210270 }
211271
212- pub := signer .PublicKey ()
213272 pubKey := pub .Marshal ()
214- sign , err := signer . Sign ( rand , buildDataSignedForAuth (session , userAuthRequestMsg {
273+ data := buildDataSignedForAuth (session , userAuthRequestMsg {
215274 User : user ,
216275 Service : serviceSSH ,
217276 Method : cb .method (),
218- }, []byte (pub .Type ()), pubKey ))
277+ }, algo , pubKey )
278+ sign , err := as .SignWithAlgorithm (rand , data , underlyingAlgo (algo ))
219279 if err != nil {
220280 return authFailure , nil , err
221281 }
@@ -229,7 +289,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
229289 Service : serviceSSH ,
230290 Method : cb .method (),
231291 HasSig : true ,
232- Algoname : pub . Type () ,
292+ Algoname : algo ,
233293 PubKey : pubKey ,
234294 Sig : sig ,
235295 }
@@ -266,26 +326,25 @@ func containsMethod(methods []string, method string) bool {
266326}
267327
268328// 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 ) {
270330 pubKey := key .Marshal ()
271331 msg := publickeyAuthMsg {
272332 User : user ,
273333 Service : serviceSSH ,
274334 Method : "publickey" ,
275335 HasSig : false ,
276- Algoname : key . Type () ,
336+ Algoname : algo ,
277337 PubKey : pubKey ,
278338 }
279339 if err := c .writePacket (Marshal (& msg )); err != nil {
280340 return false , err
281341 }
282342
283- return confirmKeyAck (key , c )
343+ return confirmKeyAck (key , algo , c )
284344}
285345
286- func confirmKeyAck (key PublicKey , c packetConn ) (bool , error ) {
346+ func confirmKeyAck (key PublicKey , algo string , c packetConn ) (bool , error ) {
287347 pubKey := key .Marshal ()
288- algoname := key .Type ()
289348
290349 for {
291350 packet , err := c .readPacket ()
@@ -302,14 +361,14 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
302361 if err := Unmarshal (packet , & msg ); err != nil {
303362 return false , err
304363 }
305- if msg .Algo != algoname || ! bytes .Equal (msg .PubKey , pubKey ) {
364+ if msg .Algo != algo || ! bytes .Equal (msg .PubKey , pubKey ) {
306365 return false , nil
307366 }
308367 return true , nil
309368 case msgUserAuthFailure :
310369 return false , nil
311370 default :
312- return false , unexpectedMessageError (msgUserAuthSuccess , packet [0 ])
371+ return false , unexpectedMessageError (msgUserAuthPubKeyOk , packet [0 ])
313372 }
314373 }
315374}
@@ -330,6 +389,7 @@ func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMet
330389// along with a list of remaining authentication methods to try next and
331390// an error if an unexpected response was received.
332391func handleAuthResponse (c packetConn ) (authResult , []string , error ) {
392+ gotMsgExtInfo := false
333393 for {
334394 packet , err := c .readPacket ()
335395 if err != nil {
@@ -341,6 +401,12 @@ func handleAuthResponse(c packetConn) (authResult, []string, error) {
341401 if err := handleBannerResponse (c , packet ); err != nil {
342402 return authFailure , nil , err
343403 }
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
344410 case msgUserAuthFailure :
345411 var msg userAuthFailureMsg
346412 if err := Unmarshal (packet , & msg ); err != nil {
@@ -395,7 +461,7 @@ func (cb KeyboardInteractiveChallenge) method() string {
395461 return "keyboard-interactive"
396462}
397463
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 ) {
399465 type initiateMsg struct {
400466 User string `sshtype:"50"`
401467 Service string
@@ -412,6 +478,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
412478 return authFailure , nil , err
413479 }
414480
481+ gotMsgExtInfo := false
415482 for {
416483 packet , err := c .readPacket ()
417484 if err != nil {
@@ -425,6 +492,13 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
425492 return authFailure , nil , err
426493 }
427494 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
428502 case msgUserAuthInfoRequest :
429503 // OK
430504 case msgUserAuthFailure :
@@ -497,9 +571,9 @@ type retryableAuthMethod struct {
497571 maxTries int
498572}
499573
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 ) {
501575 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 )
503577 if ok != authFailure || err != nil { // either success, partial success or error terminate
504578 return ok , methods , err
505579 }
@@ -542,7 +616,7 @@ type gssAPIWithMICCallback struct {
542616 target string
543617}
544618
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 ) {
546620 m := & userAuthRequestMsg {
547621 User : user ,
548622 Service : serviceSSH ,
0 commit comments