a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
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, true)
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}