github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/crypto/ssh/client_auth_test.go (about) 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 "crypto/rand" 10 "errors" 11 "fmt" 12 "io" 13 "log" 14 "net" 15 "os" 16 "strings" 17 "testing" 18 ) 19 20 type keyboardInteractive map[string]string 21 22 func (cr keyboardInteractive) Challenge(user string, instruction string, questions []string, echos []bool) ([]string, error) { 23 var answers []string 24 for _, q := range questions { 25 answers = append(answers, cr[q]) 26 } 27 return answers, nil 28 } 29 30 // reused internally by tests 31 var clientPassword = "tiger" 32 33 // tryAuth runs a handshake with a given config against an SSH server 34 // with config serverConfig. Returns both client and server side errors. 35 func tryAuth(t *testing.T, config *ClientConfig) error { 36 err, _ := tryAuthBothSides(t, config, nil) 37 return err 38 } 39 40 // tryAuth runs a handshake with a given config against an SSH server 41 // with a given GSSAPIWithMICConfig and config serverConfig. Returns both client and server side errors. 42 func tryAuthWithGSSAPIWithMICConfig(t *testing.T, clientConfig *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) error { 43 err, _ := tryAuthBothSides(t, clientConfig, gssAPIWithMICConfig) 44 return err 45 } 46 47 // tryAuthBothSides runs the handshake and returns the resulting errors from both sides of the connection. 48 func tryAuthBothSides(t *testing.T, config *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) (clientError error, serverAuthErrors []error) { 49 c1, c2, err := netPipe() 50 if err != nil { 51 t.Fatalf("netPipe: %v", err) 52 } 53 defer c1.Close() 54 defer c2.Close() 55 56 certChecker := CertChecker{ 57 IsUserAuthority: func(k PublicKey) bool { 58 return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal()) 59 }, 60 UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { 61 if conn.User() == "testuser" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) { 62 return nil, nil 63 } 64 65 return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User()) 66 }, 67 IsRevoked: func(c *Certificate) bool { 68 return c.Serial == 666 69 }, 70 } 71 serverConfig := &ServerConfig{ 72 PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) { 73 if conn.User() == "testuser" && string(pass) == clientPassword { 74 return nil, nil 75 } 76 return nil, errors.New("password auth failed") 77 }, 78 PublicKeyCallback: certChecker.Authenticate, 79 KeyboardInteractiveCallback: func(conn ConnMetadata, challenge KeyboardInteractiveChallenge) (*Permissions, error) { 80 ans, err := challenge("user", 81 "instruction", 82 []string{"question1", "question2"}, 83 []bool{true, true}) 84 if err != nil { 85 return nil, err 86 } 87 ok := conn.User() == "testuser" && ans[0] == "answer1" && ans[1] == "answer2" 88 if ok { 89 challenge("user", "motd", nil, nil) 90 return nil, nil 91 } 92 return nil, errors.New("keyboard-interactive failed") 93 }, 94 GSSAPIWithMICConfig: gssAPIWithMICConfig, 95 } 96 serverConfig.AddHostKey(testSigners["rsa"]) 97 98 serverConfig.AuthLogCallback = func(conn ConnMetadata, method string, err error) { 99 serverAuthErrors = append(serverAuthErrors, err) 100 } 101 102 go newServer(c1, serverConfig) 103 _, _, _, err = NewClientConn(c2, "", config) 104 return err, serverAuthErrors 105 } 106 107 func TestClientAuthPublicKey(t *testing.T) { 108 config := &ClientConfig{ 109 User: "testuser", 110 Auth: []AuthMethod{ 111 PublicKeys(testSigners["rsa"]), 112 }, 113 HostKeyCallback: InsecureIgnoreHostKey(), 114 } 115 if err := tryAuth(t, config); err != nil { 116 t.Fatalf("unable to dial remote side: %s", err) 117 } 118 } 119 120 func TestAuthMethodPassword(t *testing.T) { 121 config := &ClientConfig{ 122 User: "testuser", 123 Auth: []AuthMethod{ 124 Password(clientPassword), 125 }, 126 HostKeyCallback: InsecureIgnoreHostKey(), 127 } 128 129 if err := tryAuth(t, config); err != nil { 130 t.Fatalf("unable to dial remote side: %s", err) 131 } 132 } 133 134 func TestAuthMethodFallback(t *testing.T) { 135 var passwordCalled bool 136 config := &ClientConfig{ 137 User: "testuser", 138 Auth: []AuthMethod{ 139 PublicKeys(testSigners["rsa"]), 140 PasswordCallback( 141 func() (string, error) { 142 passwordCalled = true 143 return "WRONG", nil 144 }), 145 }, 146 HostKeyCallback: InsecureIgnoreHostKey(), 147 } 148 149 if err := tryAuth(t, config); err != nil { 150 t.Fatalf("unable to dial remote side: %s", err) 151 } 152 153 if passwordCalled { 154 t.Errorf("password auth tried before public-key auth.") 155 } 156 } 157 158 func TestAuthMethodWrongPassword(t *testing.T) { 159 config := &ClientConfig{ 160 User: "testuser", 161 Auth: []AuthMethod{ 162 Password("wrong"), 163 PublicKeys(testSigners["rsa"]), 164 }, 165 HostKeyCallback: InsecureIgnoreHostKey(), 166 } 167 168 if err := tryAuth(t, config); err != nil { 169 t.Fatalf("unable to dial remote side: %s", err) 170 } 171 } 172 173 func TestAuthMethodKeyboardInteractive(t *testing.T) { 174 answers := keyboardInteractive(map[string]string{ 175 "question1": "answer1", 176 "question2": "answer2", 177 }) 178 config := &ClientConfig{ 179 User: "testuser", 180 Auth: []AuthMethod{ 181 KeyboardInteractive(answers.Challenge), 182 }, 183 HostKeyCallback: InsecureIgnoreHostKey(), 184 } 185 186 if err := tryAuth(t, config); err != nil { 187 t.Fatalf("unable to dial remote side: %s", err) 188 } 189 } 190 191 func TestAuthMethodWrongKeyboardInteractive(t *testing.T) { 192 answers := keyboardInteractive(map[string]string{ 193 "question1": "answer1", 194 "question2": "WRONG", 195 }) 196 config := &ClientConfig{ 197 User: "testuser", 198 Auth: []AuthMethod{ 199 KeyboardInteractive(answers.Challenge), 200 }, 201 } 202 203 if err := tryAuth(t, config); err == nil { 204 t.Fatalf("wrong answers should not have authenticated with KeyboardInteractive") 205 } 206 } 207 208 // the mock server will only authenticate ssh-rsa keys 209 func TestAuthMethodInvalidPublicKey(t *testing.T) { 210 config := &ClientConfig{ 211 User: "testuser", 212 Auth: []AuthMethod{ 213 PublicKeys(testSigners["dsa"]), 214 }, 215 } 216 217 if err := tryAuth(t, config); err == nil { 218 t.Fatalf("dsa private key should not have authenticated with rsa public key") 219 } 220 } 221 222 // the client should authenticate with the second key 223 func TestAuthMethodRSAandDSA(t *testing.T) { 224 config := &ClientConfig{ 225 User: "testuser", 226 Auth: []AuthMethod{ 227 PublicKeys(testSigners["dsa"], testSigners["rsa"]), 228 }, 229 HostKeyCallback: InsecureIgnoreHostKey(), 230 } 231 if err := tryAuth(t, config); err != nil { 232 t.Fatalf("client could not authenticate with rsa key: %v", err) 233 } 234 } 235 236 type invalidAlgSigner struct { 237 Signer 238 } 239 240 func (s *invalidAlgSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { 241 sig, err := s.Signer.Sign(rand, data) 242 if sig != nil { 243 sig.Format = "invalid" 244 } 245 return sig, err 246 } 247 248 func TestMethodInvalidAlgorithm(t *testing.T) { 249 config := &ClientConfig{ 250 User: "testuser", 251 Auth: []AuthMethod{ 252 PublicKeys(&invalidAlgSigner{testSigners["rsa"]}), 253 }, 254 HostKeyCallback: InsecureIgnoreHostKey(), 255 } 256 257 err, serverErrors := tryAuthBothSides(t, config, nil) 258 if err == nil { 259 t.Fatalf("login succeeded") 260 } 261 262 found := false 263 want := "algorithm \"invalid\"" 264 265 var errStrings []string 266 for _, err := range serverErrors { 267 found = found || (err != nil && strings.Contains(err.Error(), want)) 268 errStrings = append(errStrings, err.Error()) 269 } 270 if !found { 271 t.Errorf("server got error %q, want substring %q", errStrings, want) 272 } 273 } 274 275 func TestClientHMAC(t *testing.T) { 276 for _, mac := range supportedMACs { 277 config := &ClientConfig{ 278 User: "testuser", 279 Auth: []AuthMethod{ 280 PublicKeys(testSigners["rsa"]), 281 }, 282 Config: Config{ 283 MACs: []string{mac}, 284 }, 285 HostKeyCallback: InsecureIgnoreHostKey(), 286 } 287 if err := tryAuth(t, config); err != nil { 288 t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err) 289 } 290 } 291 } 292 293 // issue 4285. 294 func TestClientUnsupportedCipher(t *testing.T) { 295 config := &ClientConfig{ 296 User: "testuser", 297 Auth: []AuthMethod{ 298 PublicKeys(), 299 }, 300 Config: Config{ 301 Ciphers: []string{"aes128-cbc"}, // not currently supported 302 }, 303 } 304 if err := tryAuth(t, config); err == nil { 305 t.Errorf("expected no ciphers in common") 306 } 307 } 308 309 func TestClientUnsupportedKex(t *testing.T) { 310 if os.Getenv("GO_BUILDER_NAME") != "" { 311 t.Skip("skipping known-flaky test on the Go build dashboard; see golang.org/issue/15198") 312 } 313 config := &ClientConfig{ 314 User: "testuser", 315 Auth: []AuthMethod{ 316 PublicKeys(), 317 }, 318 Config: Config{ 319 KeyExchanges: []string{"non-existent-kex"}, 320 }, 321 HostKeyCallback: InsecureIgnoreHostKey(), 322 } 323 if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "common algorithm") { 324 t.Errorf("got %v, expected 'common algorithm'", err) 325 } 326 } 327 328 func TestClientLoginCert(t *testing.T) { 329 cert := &Certificate{ 330 Key: testPublicKeys["rsa"], 331 ValidBefore: CertTimeInfinity, 332 CertType: UserCert, 333 } 334 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 335 certSigner, err := NewCertSigner(cert, testSigners["rsa"]) 336 if err != nil { 337 t.Fatalf("NewCertSigner: %v", err) 338 } 339 340 clientConfig := &ClientConfig{ 341 User: "user", 342 HostKeyCallback: InsecureIgnoreHostKey(), 343 } 344 clientConfig.Auth = append(clientConfig.Auth, PublicKeys(certSigner)) 345 346 // should succeed 347 if err := tryAuth(t, clientConfig); err != nil { 348 t.Errorf("cert login failed: %v", err) 349 } 350 351 // corrupted signature 352 cert.Signature.Blob[0]++ 353 if err := tryAuth(t, clientConfig); err == nil { 354 t.Errorf("cert login passed with corrupted sig") 355 } 356 357 // revoked 358 cert.Serial = 666 359 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 360 if err := tryAuth(t, clientConfig); err == nil { 361 t.Errorf("revoked cert login succeeded") 362 } 363 cert.Serial = 1 364 365 // sign with wrong key 366 cert.SignCert(rand.Reader, testSigners["dsa"]) 367 if err := tryAuth(t, clientConfig); err == nil { 368 t.Errorf("cert login passed with non-authoritative key") 369 } 370 371 // host cert 372 cert.CertType = HostCert 373 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 374 if err := tryAuth(t, clientConfig); err == nil { 375 t.Errorf("cert login passed with wrong type") 376 } 377 cert.CertType = UserCert 378 379 // principal specified 380 cert.ValidPrincipals = []string{"user"} 381 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 382 if err := tryAuth(t, clientConfig); err != nil { 383 t.Errorf("cert login failed: %v", err) 384 } 385 386 // wrong principal specified 387 cert.ValidPrincipals = []string{"fred"} 388 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 389 if err := tryAuth(t, clientConfig); err == nil { 390 t.Errorf("cert login passed with wrong principal") 391 } 392 cert.ValidPrincipals = nil 393 394 // added critical option 395 cert.CriticalOptions = map[string]string{"root-access": "yes"} 396 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 397 if err := tryAuth(t, clientConfig); err == nil { 398 t.Errorf("cert login passed with unrecognized critical option") 399 } 400 401 // allowed source address 402 cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24,::42/120"} 403 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 404 if err := tryAuth(t, clientConfig); err != nil { 405 t.Errorf("cert login with source-address failed: %v", err) 406 } 407 408 // disallowed source address 409 cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42,::42"} 410 cert.SignCert(rand.Reader, testSigners["ecdsa"]) 411 if err := tryAuth(t, clientConfig); err == nil { 412 t.Errorf("cert login with source-address succeeded") 413 } 414 } 415 416 func testPermissionsPassing(withPermissions bool, t *testing.T) { 417 serverConfig := &ServerConfig{ 418 PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { 419 if conn.User() == "nopermissions" { 420 return nil, nil 421 } 422 return &Permissions{}, nil 423 }, 424 } 425 serverConfig.AddHostKey(testSigners["rsa"]) 426 427 clientConfig := &ClientConfig{ 428 Auth: []AuthMethod{ 429 PublicKeys(testSigners["rsa"]), 430 }, 431 HostKeyCallback: InsecureIgnoreHostKey(), 432 } 433 if withPermissions { 434 clientConfig.User = "permissions" 435 } else { 436 clientConfig.User = "nopermissions" 437 } 438 439 c1, c2, err := netPipe() 440 if err != nil { 441 t.Fatalf("netPipe: %v", err) 442 } 443 defer c1.Close() 444 defer c2.Close() 445 446 go NewClientConn(c2, "", clientConfig) 447 serverConn, err := newServer(c1, serverConfig) 448 if err != nil { 449 t.Fatal(err) 450 } 451 if p := serverConn.Permissions; (p != nil) != withPermissions { 452 t.Fatalf("withPermissions is %t, but Permissions object is %#v", withPermissions, p) 453 } 454 } 455 456 func TestPermissionsPassing(t *testing.T) { 457 testPermissionsPassing(true, t) 458 } 459 460 func TestNoPermissionsPassing(t *testing.T) { 461 testPermissionsPassing(false, t) 462 } 463 464 func TestRetryableAuth(t *testing.T) { 465 n := 0 466 passwords := []string{"WRONG1", "WRONG2"} 467 468 config := &ClientConfig{ 469 User: "testuser", 470 Auth: []AuthMethod{ 471 RetryableAuthMethod(PasswordCallback(func() (string, error) { 472 p := passwords[n] 473 n++ 474 return p, nil 475 }), 2), 476 PublicKeys(testSigners["rsa"]), 477 }, 478 HostKeyCallback: InsecureIgnoreHostKey(), 479 } 480 481 if err := tryAuth(t, config); err != nil { 482 t.Fatalf("unable to dial remote side: %s", err) 483 } 484 if n != 2 { 485 t.Fatalf("Did not try all passwords") 486 } 487 } 488 489 func ExampleRetryableAuthMethod() { 490 user := "testuser" 491 NumberOfPrompts := 3 492 493 // Normally this would be a callback that prompts the user to answer the 494 // provided questions 495 Cb := func(user, instruction string, questions []string, echos []bool) (answers []string, err error) { 496 return []string{"answer1", "answer2"}, nil 497 } 498 499 config := &ClientConfig{ 500 HostKeyCallback: InsecureIgnoreHostKey(), 501 User: user, 502 Auth: []AuthMethod{ 503 RetryableAuthMethod(KeyboardInteractiveChallenge(Cb), NumberOfPrompts), 504 }, 505 } 506 507 host := "mysshserver" 508 netConn, err := net.Dial("tcp", host) 509 if err != nil { 510 log.Fatal(err) 511 } 512 513 sshConn, _, _, err := NewClientConn(netConn, host, config) 514 if err != nil { 515 log.Fatal(err) 516 } 517 _ = sshConn 518 } 519 520 // Test if username is received on server side when NoClientAuth is used 521 func TestClientAuthNone(t *testing.T) { 522 user := "testuser" 523 serverConfig := &ServerConfig{ 524 NoClientAuth: true, 525 } 526 serverConfig.AddHostKey(testSigners["rsa"]) 527 528 clientConfig := &ClientConfig{ 529 User: user, 530 HostKeyCallback: InsecureIgnoreHostKey(), 531 } 532 533 c1, c2, err := netPipe() 534 if err != nil { 535 t.Fatalf("netPipe: %v", err) 536 } 537 defer c1.Close() 538 defer c2.Close() 539 540 go NewClientConn(c2, "", clientConfig) 541 serverConn, err := newServer(c1, serverConfig) 542 if err != nil { 543 t.Fatalf("newServer: %v", err) 544 } 545 if serverConn.User() != user { 546 t.Fatalf("server: got %q, want %q", serverConn.User(), user) 547 } 548 } 549 550 // Test if authentication attempts are limited on server when MaxAuthTries is set 551 func TestClientAuthMaxAuthTries(t *testing.T) { 552 user := "testuser" 553 554 serverConfig := &ServerConfig{ 555 MaxAuthTries: 2, 556 PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) { 557 if conn.User() == "testuser" && string(pass) == "right" { 558 return nil, nil 559 } 560 return nil, errors.New("password auth failed") 561 }, 562 } 563 serverConfig.AddHostKey(testSigners["rsa"]) 564 565 expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{ 566 Reason: 2, 567 Message: "too many authentication failures", 568 }) 569 570 for tries := 2; tries < 4; tries++ { 571 n := tries 572 clientConfig := &ClientConfig{ 573 User: user, 574 Auth: []AuthMethod{ 575 RetryableAuthMethod(PasswordCallback(func() (string, error) { 576 n-- 577 if n == 0 { 578 return "right", nil 579 } 580 return "wrong", nil 581 }), tries), 582 }, 583 HostKeyCallback: InsecureIgnoreHostKey(), 584 } 585 586 c1, c2, err := netPipe() 587 if err != nil { 588 t.Fatalf("netPipe: %v", err) 589 } 590 defer c1.Close() 591 defer c2.Close() 592 593 go newServer(c1, serverConfig) 594 _, _, _, err = NewClientConn(c2, "", clientConfig) 595 if tries > 2 { 596 if err == nil { 597 t.Fatalf("client: got no error, want %s", expectedErr) 598 } else if err.Error() != expectedErr.Error() { 599 t.Fatalf("client: got %s, want %s", err, expectedErr) 600 } 601 } else { 602 if err != nil { 603 t.Fatalf("client: got %s, want no error", err) 604 } 605 } 606 } 607 } 608 609 // Test if authentication attempts are correctly limited on server 610 // when more public keys are provided then MaxAuthTries 611 func TestClientAuthMaxAuthTriesPublicKey(t *testing.T) { 612 signers := []Signer{} 613 for i := 0; i < 6; i++ { 614 signers = append(signers, testSigners["dsa"]) 615 } 616 617 validConfig := &ClientConfig{ 618 User: "testuser", 619 Auth: []AuthMethod{ 620 PublicKeys(append([]Signer{testSigners["rsa"]}, signers...)...), 621 }, 622 HostKeyCallback: InsecureIgnoreHostKey(), 623 } 624 if err := tryAuth(t, validConfig); err != nil { 625 t.Fatalf("unable to dial remote side: %s", err) 626 } 627 628 expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{ 629 Reason: 2, 630 Message: "too many authentication failures", 631 }) 632 invalidConfig := &ClientConfig{ 633 User: "testuser", 634 Auth: []AuthMethod{ 635 PublicKeys(append(signers, testSigners["rsa"])...), 636 }, 637 HostKeyCallback: InsecureIgnoreHostKey(), 638 } 639 if err := tryAuth(t, invalidConfig); err == nil { 640 t.Fatalf("client: got no error, want %s", expectedErr) 641 } else if err.Error() != expectedErr.Error() { 642 t.Fatalf("client: got %s, want %s", err, expectedErr) 643 } 644 } 645 646 // Test whether authentication errors are being properly logged if all 647 // authentication methods have been exhausted 648 func TestClientAuthErrorList(t *testing.T) { 649 publicKeyErr := errors.New("This is an error from PublicKeyCallback") 650 651 clientConfig := &ClientConfig{ 652 Auth: []AuthMethod{ 653 PublicKeys(testSigners["rsa"]), 654 }, 655 HostKeyCallback: InsecureIgnoreHostKey(), 656 } 657 serverConfig := &ServerConfig{ 658 PublicKeyCallback: func(_ ConnMetadata, _ PublicKey) (*Permissions, error) { 659 return nil, publicKeyErr 660 }, 661 } 662 serverConfig.AddHostKey(testSigners["rsa"]) 663 664 c1, c2, err := netPipe() 665 if err != nil { 666 t.Fatalf("netPipe: %v", err) 667 } 668 defer c1.Close() 669 defer c2.Close() 670 671 go NewClientConn(c2, "", clientConfig) 672 _, err = newServer(c1, serverConfig) 673 if err == nil { 674 t.Fatal("newServer: got nil, expected errors") 675 } 676 677 authErrs, ok := err.(*ServerAuthError) 678 if !ok { 679 t.Fatalf("errors: got %T, want *ssh.ServerAuthError", err) 680 } 681 for i, e := range authErrs.Errors { 682 switch i { 683 case 0: 684 if e != ErrNoAuth { 685 t.Fatalf("errors: got error %v, want ErrNoAuth", e) 686 } 687 case 1: 688 if e != publicKeyErr { 689 t.Fatalf("errors: got %v, want %v", e, publicKeyErr) 690 } 691 default: 692 t.Fatalf("errors: got %v, expected 2 errors", authErrs.Errors) 693 } 694 } 695 } 696 697 func TestAuthMethodGSSAPIWithMIC(t *testing.T) { 698 type testcase struct { 699 config *ClientConfig 700 gssConfig *GSSAPIWithMICConfig 701 clientWantErr string 702 serverWantErr string 703 } 704 testcases := []*testcase{ 705 { 706 config: &ClientConfig{ 707 User: "testuser", 708 Auth: []AuthMethod{ 709 GSSAPIWithMICAuthMethod( 710 &FakeClient{ 711 exchanges: []*exchange{ 712 { 713 outToken: "client-valid-token-1", 714 }, 715 { 716 expectedToken: "server-valid-token-1", 717 }, 718 }, 719 mic: []byte("valid-mic"), 720 maxRound: 2, 721 }, "testtarget", 722 ), 723 }, 724 HostKeyCallback: InsecureIgnoreHostKey(), 725 }, 726 gssConfig: &GSSAPIWithMICConfig{ 727 AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) { 728 if srcName != conn.User()+"@DOMAIN" { 729 return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User()) 730 } 731 return nil, nil 732 }, 733 Server: &FakeServer{ 734 exchanges: []*exchange{ 735 { 736 outToken: "server-valid-token-1", 737 expectedToken: "client-valid-token-1", 738 }, 739 }, 740 maxRound: 1, 741 expectedMIC: []byte("valid-mic"), 742 srcName: "testuser@DOMAIN", 743 }, 744 }, 745 }, 746 { 747 config: &ClientConfig{ 748 User: "testuser", 749 Auth: []AuthMethod{ 750 GSSAPIWithMICAuthMethod( 751 &FakeClient{ 752 exchanges: []*exchange{ 753 { 754 outToken: "client-valid-token-1", 755 }, 756 { 757 expectedToken: "server-valid-token-1", 758 }, 759 }, 760 mic: []byte("valid-mic"), 761 maxRound: 2, 762 }, "testtarget", 763 ), 764 }, 765 HostKeyCallback: InsecureIgnoreHostKey(), 766 }, 767 gssConfig: &GSSAPIWithMICConfig{ 768 AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) { 769 return nil, fmt.Errorf("user is not allowed to login") 770 }, 771 Server: &FakeServer{ 772 exchanges: []*exchange{ 773 { 774 outToken: "server-valid-token-1", 775 expectedToken: "client-valid-token-1", 776 }, 777 }, 778 maxRound: 1, 779 expectedMIC: []byte("valid-mic"), 780 srcName: "testuser@DOMAIN", 781 }, 782 }, 783 serverWantErr: "user is not allowed to login", 784 clientWantErr: "ssh: handshake failed: ssh: unable to authenticate", 785 }, 786 { 787 config: &ClientConfig{ 788 User: "testuser", 789 Auth: []AuthMethod{ 790 GSSAPIWithMICAuthMethod( 791 &FakeClient{ 792 exchanges: []*exchange{ 793 { 794 outToken: "client-valid-token-1", 795 }, 796 { 797 expectedToken: "server-valid-token-1", 798 }, 799 }, 800 mic: []byte("valid-mic"), 801 maxRound: 2, 802 }, "testtarget", 803 ), 804 }, 805 HostKeyCallback: InsecureIgnoreHostKey(), 806 }, 807 gssConfig: &GSSAPIWithMICConfig{ 808 AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) { 809 if srcName != conn.User() { 810 return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User()) 811 } 812 return nil, nil 813 }, 814 Server: &FakeServer{ 815 exchanges: []*exchange{ 816 { 817 outToken: "server-invalid-token-1", 818 expectedToken: "client-valid-token-1", 819 }, 820 }, 821 maxRound: 1, 822 expectedMIC: []byte("valid-mic"), 823 srcName: "testuser@DOMAIN", 824 }, 825 }, 826 clientWantErr: "ssh: handshake failed: got \"server-invalid-token-1\", want token \"server-valid-token-1\"", 827 }, 828 { 829 config: &ClientConfig{ 830 User: "testuser", 831 Auth: []AuthMethod{ 832 GSSAPIWithMICAuthMethod( 833 &FakeClient{ 834 exchanges: []*exchange{ 835 { 836 outToken: "client-valid-token-1", 837 }, 838 { 839 expectedToken: "server-valid-token-1", 840 }, 841 }, 842 mic: []byte("invalid-mic"), 843 maxRound: 2, 844 }, "testtarget", 845 ), 846 }, 847 HostKeyCallback: InsecureIgnoreHostKey(), 848 }, 849 gssConfig: &GSSAPIWithMICConfig{ 850 AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) { 851 if srcName != conn.User() { 852 return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User()) 853 } 854 return nil, nil 855 }, 856 Server: &FakeServer{ 857 exchanges: []*exchange{ 858 { 859 outToken: "server-valid-token-1", 860 expectedToken: "client-valid-token-1", 861 }, 862 }, 863 maxRound: 1, 864 expectedMIC: []byte("valid-mic"), 865 srcName: "testuser@DOMAIN", 866 }, 867 }, 868 serverWantErr: "got MICToken \"invalid-mic\", want \"valid-mic\"", 869 clientWantErr: "ssh: handshake failed: ssh: unable to authenticate", 870 }, 871 } 872 873 for i, c := range testcases { 874 clientErr, serverErrs := tryAuthBothSides(t, c.config, c.gssConfig) 875 if (c.clientWantErr == "") != (clientErr == nil) { 876 t.Fatalf("client got %v, want %s, case %d", clientErr, c.clientWantErr, i) 877 } 878 if (c.serverWantErr == "") != (len(serverErrs) == 2 && serverErrs[1] == nil || len(serverErrs) == 1) { 879 t.Fatalf("server got err %v, want %s", serverErrs, c.serverWantErr) 880 } 881 if c.clientWantErr != "" { 882 if clientErr != nil && !strings.Contains(clientErr.Error(), c.clientWantErr) { 883 t.Fatalf("client got %v, want %s, case %d", clientErr, c.clientWantErr, i) 884 } 885 } 886 found := false 887 var errStrings []string 888 if c.serverWantErr != "" { 889 for _, err := range serverErrs { 890 found = found || (err != nil && strings.Contains(err.Error(), c.serverWantErr)) 891 errStrings = append(errStrings, err.Error()) 892 } 893 if !found { 894 t.Errorf("server got error %q, want substring %q, case %d", errStrings, c.serverWantErr, i) 895 } 896 } 897 } 898 }