a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
at main 24 kB view 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 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}