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