client_auth.go raw
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package ssh
6
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "io"
12 "slices"
13 "strings"
14 )
15
16 type authResult int
17
18 const (
19 authFailure authResult = iota
20 authPartialSuccess
21 authSuccess
22 )
23
24 // clientAuthenticate authenticates with the remote server. See RFC 4252.
25 func (c *connection) clientAuthenticate(config *ClientConfig) error {
26 // initiate user auth session
27 if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil {
28 return err
29 }
30 packet, err := c.transport.readPacket()
31 if err != nil {
32 return err
33 }
34 // The server may choose to send a SSH_MSG_EXT_INFO at this point (if we
35 // advertised willingness to receive one, which we always do) or not. See
36 // RFC 8308, Section 2.4.
37 extensions := make(map[string][]byte)
38 if len(packet) > 0 && packet[0] == msgExtInfo {
39 var extInfo extInfoMsg
40 if err := Unmarshal(packet, &extInfo); err != nil {
41 return err
42 }
43 payload := extInfo.Payload
44 for i := uint32(0); i < extInfo.NumExtensions; i++ {
45 name, rest, ok := parseString(payload)
46 if !ok {
47 return parseError(msgExtInfo)
48 }
49 value, rest, ok := parseString(rest)
50 if !ok {
51 return parseError(msgExtInfo)
52 }
53 extensions[string(name)] = value
54 payload = rest
55 }
56 packet, err = c.transport.readPacket()
57 if err != nil {
58 return err
59 }
60 }
61 var serviceAccept serviceAcceptMsg
62 if err := Unmarshal(packet, &serviceAccept); err != nil {
63 return err
64 }
65
66 // during the authentication phase the client first attempts the "none" method
67 // then any untried methods suggested by the server.
68 var tried []string
69 var lastMethods []string
70
71 sessionID := c.transport.getSessionID()
72 for auth := AuthMethod(new(noneAuth)); auth != nil; {
73 ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
74 if err != nil {
75 // On disconnect, return error immediately
76 if _, ok := err.(*disconnectMsg); ok {
77 return err
78 }
79 // We return the error later if there is no other method left to
80 // try.
81 ok = authFailure
82 }
83 if ok == authSuccess {
84 // success
85 return nil
86 } else if ok == authFailure {
87 if m := auth.method(); !slices.Contains(tried, m) {
88 tried = append(tried, m)
89 }
90 }
91 if methods == nil {
92 methods = lastMethods
93 }
94 lastMethods = methods
95
96 auth = nil
97
98 findNext:
99 for _, a := range config.Auth {
100 candidateMethod := a.method()
101 if slices.Contains(tried, candidateMethod) {
102 continue
103 }
104 for _, meth := range methods {
105 if meth == candidateMethod {
106 auth = a
107 break findNext
108 }
109 }
110 }
111
112 if auth == nil && err != nil {
113 // We have an error and there are no other authentication methods to
114 // try, so we return it.
115 return err
116 }
117 }
118 return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
119 }
120
121 // An AuthMethod represents an instance of an RFC 4252 authentication method.
122 type AuthMethod interface {
123 // auth authenticates user over transport t.
124 // Returns true if authentication is successful.
125 // If authentication is not successful, a []string of alternative
126 // method names is returned. If the slice is nil, it will be ignored
127 // and the previous set of possible methods will be reused.
128 auth(session []byte, user string, p packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error)
129
130 // method returns the RFC 4252 method name.
131 method() string
132 }
133
134 // "none" authentication, RFC 4252 section 5.2.
135 type noneAuth int
136
137 func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
138 if err := c.writePacket(Marshal(&userAuthRequestMsg{
139 User: user,
140 Service: serviceSSH,
141 Method: "none",
142 })); err != nil {
143 return authFailure, nil, err
144 }
145
146 return handleAuthResponse(c)
147 }
148
149 func (n *noneAuth) method() string {
150 return "none"
151 }
152
153 // passwordCallback is an AuthMethod that fetches the password through
154 // a function call, e.g. by prompting the user.
155 type passwordCallback func() (password string, err error)
156
157 func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
158 type passwordAuthMsg struct {
159 User string `sshtype:"50"`
160 Service string
161 Method string
162 Reply bool
163 Password string
164 }
165
166 pw, err := cb()
167 // REVIEW NOTE: is there a need to support skipping a password attempt?
168 // The program may only find out that the user doesn't have a password
169 // when prompting.
170 if err != nil {
171 return authFailure, nil, err
172 }
173
174 if err := c.writePacket(Marshal(&passwordAuthMsg{
175 User: user,
176 Service: serviceSSH,
177 Method: cb.method(),
178 Reply: false,
179 Password: pw,
180 })); err != nil {
181 return authFailure, nil, err
182 }
183
184 return handleAuthResponse(c)
185 }
186
187 func (cb passwordCallback) method() string {
188 return "password"
189 }
190
191 // Password returns an AuthMethod using the given password.
192 func Password(secret string) AuthMethod {
193 return passwordCallback(func() (string, error) { return secret, nil })
194 }
195
196 // PasswordCallback returns an AuthMethod that uses a callback for
197 // fetching a password.
198 func PasswordCallback(prompt func() (secret string, err error)) AuthMethod {
199 return passwordCallback(prompt)
200 }
201
202 type publickeyAuthMsg struct {
203 User string `sshtype:"50"`
204 Service string
205 Method string
206 // HasSig indicates to the receiver packet that the auth request is signed and
207 // should be used for authentication of the request.
208 HasSig bool
209 Algoname string
210 PubKey []byte
211 // Sig is tagged with "rest" so Marshal will exclude it during
212 // validateKey
213 Sig []byte `ssh:"rest"`
214 }
215
216 // publicKeyCallback is an AuthMethod that uses a set of key
217 // pairs for authentication.
218 type publicKeyCallback func() ([]Signer, error)
219
220 func (cb publicKeyCallback) method() string {
221 return "publickey"
222 }
223
224 func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiAlgorithmSigner, string, error) {
225 var as MultiAlgorithmSigner
226 keyFormat := signer.PublicKey().Type()
227
228 // If the signer implements MultiAlgorithmSigner we use the algorithms it
229 // support, if it implements AlgorithmSigner we assume it supports all
230 // algorithms, otherwise only the key format one.
231 switch s := signer.(type) {
232 case MultiAlgorithmSigner:
233 as = s
234 case AlgorithmSigner:
235 as = &multiAlgorithmSigner{
236 AlgorithmSigner: s,
237 supportedAlgorithms: algorithmsForKeyFormat(underlyingAlgo(keyFormat)),
238 }
239 default:
240 as = &multiAlgorithmSigner{
241 AlgorithmSigner: algorithmSignerWrapper{signer},
242 supportedAlgorithms: []string{underlyingAlgo(keyFormat)},
243 }
244 }
245
246 getFallbackAlgo := func() (string, error) {
247 // Fallback to use if there is no "server-sig-algs" extension or a
248 // common algorithm cannot be found. We use the public key format if the
249 // MultiAlgorithmSigner supports it, otherwise we return an error.
250 if !slices.Contains(as.Algorithms(), underlyingAlgo(keyFormat)) {
251 return "", fmt.Errorf("ssh: no common public key signature algorithm, server only supports %q for key type %q, signer only supports %v",
252 underlyingAlgo(keyFormat), keyFormat, as.Algorithms())
253 }
254 return keyFormat, nil
255 }
256
257 extPayload, ok := extensions["server-sig-algs"]
258 if !ok {
259 // If there is no "server-sig-algs" extension use the fallback
260 // algorithm.
261 algo, err := getFallbackAlgo()
262 return as, algo, err
263 }
264
265 // The server-sig-algs extension only carries underlying signature
266 // algorithm, but we are trying to select a protocol-level public key
267 // algorithm, which might be a certificate type. Extend the list of server
268 // supported algorithms to include the corresponding certificate algorithms.
269 serverAlgos := strings.Split(string(extPayload), ",")
270 for _, algo := range serverAlgos {
271 if certAlgo, ok := certificateAlgo(algo); ok {
272 serverAlgos = append(serverAlgos, certAlgo)
273 }
274 }
275
276 // Filter algorithms based on those supported by MultiAlgorithmSigner.
277 var keyAlgos []string
278 for _, algo := range algorithmsForKeyFormat(keyFormat) {
279 if slices.Contains(as.Algorithms(), underlyingAlgo(algo)) {
280 keyAlgos = append(keyAlgos, algo)
281 }
282 }
283
284 algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos, true)
285 if err != nil {
286 // If there is no overlap, return the fallback algorithm to support
287 // servers that fail to list all supported algorithms.
288 algo, err := getFallbackAlgo()
289 return as, algo, err
290 }
291 return as, algo, nil
292 }
293
294 func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
295 // Authentication is performed by sending an enquiry to test if a key is
296 // acceptable to the remote. If the key is acceptable, the client will
297 // attempt to authenticate with the valid key. If not the client will repeat
298 // the process with the remaining keys.
299
300 signers, err := cb()
301 if err != nil {
302 return authFailure, nil, err
303 }
304 var methods []string
305 var errSigAlgo error
306
307 origSignersLen := len(signers)
308 for idx := 0; idx < len(signers); idx++ {
309 signer := signers[idx]
310 pub := signer.PublicKey()
311 as, algo, err := pickSignatureAlgorithm(signer, extensions)
312 if err != nil && errSigAlgo == nil {
313 // If we cannot negotiate a signature algorithm store the first
314 // error so we can return it to provide a more meaningful message if
315 // no other signers work.
316 errSigAlgo = err
317 continue
318 }
319 ok, err := validateKey(pub, algo, user, c)
320 if err != nil {
321 return authFailure, nil, err
322 }
323 // OpenSSH 7.2-7.7 advertises support for rsa-sha2-256 and rsa-sha2-512
324 // in the "server-sig-algs" extension but doesn't support these
325 // algorithms for certificate authentication, so if the server rejects
326 // the key try to use the obtained algorithm as if "server-sig-algs" had
327 // not been implemented if supported from the algorithm signer.
328 if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 {
329 if slices.Contains(as.Algorithms(), KeyAlgoRSA) {
330 // We retry using the compat algorithm after all signers have
331 // been tried normally.
332 signers = append(signers, &multiAlgorithmSigner{
333 AlgorithmSigner: as,
334 supportedAlgorithms: []string{KeyAlgoRSA},
335 })
336 }
337 }
338 if !ok {
339 continue
340 }
341
342 pubKey := pub.Marshal()
343 data := buildDataSignedForAuth(session, userAuthRequestMsg{
344 User: user,
345 Service: serviceSSH,
346 Method: cb.method(),
347 }, algo, pubKey)
348 sign, err := as.SignWithAlgorithm(rand, data, underlyingAlgo(algo))
349 if err != nil {
350 return authFailure, nil, err
351 }
352
353 // manually wrap the serialized signature in a string
354 s := Marshal(sign)
355 sig := make([]byte, stringLength(len(s)))
356 marshalString(sig, s)
357 msg := publickeyAuthMsg{
358 User: user,
359 Service: serviceSSH,
360 Method: cb.method(),
361 HasSig: true,
362 Algoname: algo,
363 PubKey: pubKey,
364 Sig: sig,
365 }
366 p := Marshal(&msg)
367 if err := c.writePacket(p); err != nil {
368 return authFailure, nil, err
369 }
370 var success authResult
371 success, methods, err = handleAuthResponse(c)
372 if err != nil {
373 return authFailure, nil, err
374 }
375
376 // If authentication succeeds or the list of available methods does not
377 // contain the "publickey" method, do not attempt to authenticate with any
378 // other keys. According to RFC 4252 Section 7, the latter can occur when
379 // additional authentication methods are required.
380 if success == authSuccess || !slices.Contains(methods, cb.method()) {
381 return success, methods, err
382 }
383 }
384
385 return authFailure, methods, errSigAlgo
386 }
387
388 // validateKey validates the key provided is acceptable to the server.
389 func validateKey(key PublicKey, algo string, user string, c packetConn) (bool, error) {
390 pubKey := key.Marshal()
391 msg := publickeyAuthMsg{
392 User: user,
393 Service: serviceSSH,
394 Method: "publickey",
395 HasSig: false,
396 Algoname: algo,
397 PubKey: pubKey,
398 }
399 if err := c.writePacket(Marshal(&msg)); err != nil {
400 return false, err
401 }
402
403 return confirmKeyAck(key, c)
404 }
405
406 func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
407 pubKey := key.Marshal()
408
409 for {
410 packet, err := c.readPacket()
411 if err != nil {
412 return false, err
413 }
414 switch packet[0] {
415 case msgUserAuthBanner:
416 if err := handleBannerResponse(c, packet); err != nil {
417 return false, err
418 }
419 case msgUserAuthPubKeyOk:
420 var msg userAuthPubKeyOkMsg
421 if err := Unmarshal(packet, &msg); err != nil {
422 return false, err
423 }
424 // According to RFC 4252 Section 7 the algorithm in
425 // SSH_MSG_USERAUTH_PK_OK should match that of the request but some
426 // servers send the key type instead. OpenSSH allows any algorithm
427 // that matches the public key, so we do the same.
428 // https://github.com/openssh/openssh-portable/blob/86bdd385/sshconnect2.c#L709
429 if !slices.Contains(algorithmsForKeyFormat(key.Type()), msg.Algo) {
430 return false, nil
431 }
432 if !bytes.Equal(msg.PubKey, pubKey) {
433 return false, nil
434 }
435 return true, nil
436 case msgUserAuthFailure:
437 return false, nil
438 default:
439 return false, unexpectedMessageError(msgUserAuthPubKeyOk, packet[0])
440 }
441 }
442 }
443
444 // PublicKeys returns an AuthMethod that uses the given key
445 // pairs.
446 func PublicKeys(signers ...Signer) AuthMethod {
447 return publicKeyCallback(func() ([]Signer, error) { return signers, nil })
448 }
449
450 // PublicKeysCallback returns an AuthMethod that runs the given
451 // function to obtain a list of key pairs.
452 func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod {
453 return publicKeyCallback(getSigners)
454 }
455
456 // handleAuthResponse returns whether the preceding authentication request succeeded
457 // along with a list of remaining authentication methods to try next and
458 // an error if an unexpected response was received.
459 func handleAuthResponse(c packetConn) (authResult, []string, error) {
460 gotMsgExtInfo := false
461 for {
462 packet, err := c.readPacket()
463 if err != nil {
464 return authFailure, nil, err
465 }
466
467 switch packet[0] {
468 case msgUserAuthBanner:
469 if err := handleBannerResponse(c, packet); err != nil {
470 return authFailure, nil, err
471 }
472 case msgExtInfo:
473 // Ignore post-authentication RFC 8308 extensions, once.
474 if gotMsgExtInfo {
475 return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
476 }
477 gotMsgExtInfo = true
478 case msgUserAuthFailure:
479 var msg userAuthFailureMsg
480 if err := Unmarshal(packet, &msg); err != nil {
481 return authFailure, nil, err
482 }
483 if msg.PartialSuccess {
484 return authPartialSuccess, msg.Methods, nil
485 }
486 return authFailure, msg.Methods, nil
487 case msgUserAuthSuccess:
488 return authSuccess, nil, nil
489 default:
490 return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
491 }
492 }
493 }
494
495 func handleBannerResponse(c packetConn, packet []byte) error {
496 var msg userAuthBannerMsg
497 if err := Unmarshal(packet, &msg); err != nil {
498 return err
499 }
500
501 transport, ok := c.(*handshakeTransport)
502 if !ok {
503 return nil
504 }
505
506 if transport.bannerCallback != nil {
507 return transport.bannerCallback(msg.Message)
508 }
509
510 return nil
511 }
512
513 // KeyboardInteractiveChallenge should print questions, optionally
514 // disabling echoing (e.g. for passwords), and return all the answers.
515 // Challenge may be called multiple times in a single session. After
516 // successful authentication, the server may send a challenge with no
517 // questions, for which the name and instruction messages should be
518 // printed. RFC 4256 section 3.3 details how the UI should behave for
519 // both CLI and GUI environments.
520 type KeyboardInteractiveChallenge func(name, instruction string, questions []string, echos []bool) (answers []string, err error)
521
522 // KeyboardInteractive returns an AuthMethod using a prompt/response
523 // sequence controlled by the server.
524 func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
525 return challenge
526 }
527
528 func (cb KeyboardInteractiveChallenge) method() string {
529 return "keyboard-interactive"
530 }
531
532 func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
533 type initiateMsg struct {
534 User string `sshtype:"50"`
535 Service string
536 Method string
537 Language string
538 Submethods string
539 }
540
541 if err := c.writePacket(Marshal(&initiateMsg{
542 User: user,
543 Service: serviceSSH,
544 Method: "keyboard-interactive",
545 })); err != nil {
546 return authFailure, nil, err
547 }
548
549 gotMsgExtInfo := false
550 gotUserAuthInfoRequest := false
551 for {
552 packet, err := c.readPacket()
553 if err != nil {
554 return authFailure, nil, err
555 }
556
557 // like handleAuthResponse, but with less options.
558 switch packet[0] {
559 case msgUserAuthBanner:
560 if err := handleBannerResponse(c, packet); err != nil {
561 return authFailure, nil, err
562 }
563 continue
564 case msgExtInfo:
565 // Ignore post-authentication RFC 8308 extensions, once.
566 if gotMsgExtInfo {
567 return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
568 }
569 gotMsgExtInfo = true
570 continue
571 case msgUserAuthInfoRequest:
572 // OK
573 case msgUserAuthFailure:
574 var msg userAuthFailureMsg
575 if err := Unmarshal(packet, &msg); err != nil {
576 return authFailure, nil, err
577 }
578 if msg.PartialSuccess {
579 return authPartialSuccess, msg.Methods, nil
580 }
581 if !gotUserAuthInfoRequest {
582 return authFailure, msg.Methods, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
583 }
584 return authFailure, msg.Methods, nil
585 case msgUserAuthSuccess:
586 return authSuccess, nil, nil
587 default:
588 return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
589 }
590
591 var msg userAuthInfoRequestMsg
592 if err := Unmarshal(packet, &msg); err != nil {
593 return authFailure, nil, err
594 }
595 gotUserAuthInfoRequest = true
596
597 // Manually unpack the prompt/echo pairs.
598 rest := msg.Prompts
599 var prompts []string
600 var echos []bool
601 for i := 0; i < int(msg.NumPrompts); i++ {
602 prompt, r, ok := parseString(rest)
603 if !ok || len(r) == 0 {
604 return authFailure, nil, errors.New("ssh: prompt format error")
605 }
606 prompts = append(prompts, string(prompt))
607 echos = append(echos, r[0] != 0)
608 rest = r[1:]
609 }
610
611 if len(rest) != 0 {
612 return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
613 }
614
615 answers, err := cb(msg.Name, msg.Instruction, prompts, echos)
616 if err != nil {
617 return authFailure, nil, err
618 }
619
620 if len(answers) != len(prompts) {
621 return authFailure, nil, fmt.Errorf("ssh: incorrect number of answers from keyboard-interactive callback %d (expected %d)", len(answers), len(prompts))
622 }
623 responseLength := 1 + 4
624 for _, a := range answers {
625 responseLength += stringLength(len(a))
626 }
627 serialized := make([]byte, responseLength)
628 p := serialized
629 p[0] = msgUserAuthInfoResponse
630 p = p[1:]
631 p = marshalUint32(p, uint32(len(answers)))
632 for _, a := range answers {
633 p = marshalString(p, []byte(a))
634 }
635
636 if err := c.writePacket(serialized); err != nil {
637 return authFailure, nil, err
638 }
639 }
640 }
641
642 type retryableAuthMethod struct {
643 authMethod AuthMethod
644 maxTries int
645 }
646
647 func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (ok authResult, methods []string, err error) {
648 for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
649 ok, methods, err = r.authMethod.auth(session, user, c, rand, extensions)
650 if ok != authFailure || err != nil { // either success, partial success or error terminate
651 return ok, methods, err
652 }
653 }
654 return ok, methods, err
655 }
656
657 func (r *retryableAuthMethod) method() string {
658 return r.authMethod.method()
659 }
660
661 // RetryableAuthMethod is a decorator for other auth methods enabling them to
662 // be retried up to maxTries before considering that AuthMethod itself failed.
663 // If maxTries is <= 0, will retry indefinitely
664 //
665 // This is useful for interactive clients using challenge/response type
666 // authentication (e.g. Keyboard-Interactive, Password, etc) where the user
667 // could mistype their response resulting in the server issuing a
668 // SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
669 // [keyboard-interactive]); Without this decorator, the non-retryable
670 // AuthMethod would be removed from future consideration, and never tried again
671 // (and so the user would never be able to retry their entry).
672 func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
673 return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
674 }
675
676 // GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication.
677 // See RFC 4462 section 3
678 // gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details.
679 // target is the server host you want to log in to.
680 func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod {
681 if gssAPIClient == nil {
682 panic("gss-api client must be not nil with enable gssapi-with-mic")
683 }
684 return &gssAPIWithMICCallback{gssAPIClient: gssAPIClient, target: target}
685 }
686
687 type gssAPIWithMICCallback struct {
688 gssAPIClient GSSAPIClient
689 target string
690 }
691
692 func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
693 m := &userAuthRequestMsg{
694 User: user,
695 Service: serviceSSH,
696 Method: g.method(),
697 }
698 // The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST.
699 // See RFC 4462 section 3.2.
700 m.Payload = appendU32(m.Payload, 1)
701 m.Payload = appendString(m.Payload, string(krb5OID))
702 if err := c.writePacket(Marshal(m)); err != nil {
703 return authFailure, nil, err
704 }
705 // The server responds to the SSH_MSG_USERAUTH_REQUEST with either an
706 // SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or
707 // with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE.
708 // See RFC 4462 section 3.3.
709 // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check
710 // selected mech if it is valid.
711 packet, err := c.readPacket()
712 if err != nil {
713 return authFailure, nil, err
714 }
715 userAuthGSSAPIResp := &userAuthGSSAPIResponse{}
716 if err := Unmarshal(packet, userAuthGSSAPIResp); err != nil {
717 return authFailure, nil, err
718 }
719 // Start the loop into the exchange token.
720 // See RFC 4462 section 3.4.
721 var token []byte
722 defer g.gssAPIClient.DeleteSecContext()
723 for {
724 // Initiates the establishment of a security context between the application and a remote peer.
725 nextToken, needContinue, err := g.gssAPIClient.InitSecContext("host@"+g.target, token, false)
726 if err != nil {
727 return authFailure, nil, err
728 }
729 if len(nextToken) > 0 {
730 if err := c.writePacket(Marshal(&userAuthGSSAPIToken{
731 Token: nextToken,
732 })); err != nil {
733 return authFailure, nil, err
734 }
735 }
736 if !needContinue {
737 break
738 }
739 packet, err = c.readPacket()
740 if err != nil {
741 return authFailure, nil, err
742 }
743 switch packet[0] {
744 case msgUserAuthFailure:
745 var msg userAuthFailureMsg
746 if err := Unmarshal(packet, &msg); err != nil {
747 return authFailure, nil, err
748 }
749 if msg.PartialSuccess {
750 return authPartialSuccess, msg.Methods, nil
751 }
752 return authFailure, msg.Methods, nil
753 case msgUserAuthGSSAPIError:
754 userAuthGSSAPIErrorResp := &userAuthGSSAPIError{}
755 if err := Unmarshal(packet, userAuthGSSAPIErrorResp); err != nil {
756 return authFailure, nil, err
757 }
758 return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+
759 "Major Status: %d\n"+
760 "Minor Status: %d\n"+
761 "Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus,
762 userAuthGSSAPIErrorResp.Message)
763 case msgUserAuthGSSAPIToken:
764 userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
765 if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
766 return authFailure, nil, err
767 }
768 token = userAuthGSSAPITokenReq.Token
769 }
770 }
771 // Binding Encryption Keys.
772 // See RFC 4462 section 3.5.
773 micField := buildMIC(string(session), user, "ssh-connection", "gssapi-with-mic")
774 micToken, err := g.gssAPIClient.GetMIC(micField)
775 if err != nil {
776 return authFailure, nil, err
777 }
778 if err := c.writePacket(Marshal(&userAuthGSSAPIMIC{
779 MIC: micToken,
780 })); err != nil {
781 return authFailure, nil, err
782 }
783 return handleAuthResponse(c)
784 }
785
786 func (g *gssAPIWithMICCallback) method() string {
787 return "gssapi-with-mic"
788 }
789