github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/crypto/ssh/session_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 // Session tests. 8 9 import ( 10 "bytes" 11 crypto_rand "crypto/rand" 12 "errors" 13 "io" 14 "io/ioutil" 15 "math/rand" 16 "net" 17 "testing" 18 19 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/terminal" 20 ) 21 22 type serverType func(Channel, <-chan *Request, *testing.T) 23 24 // dial constructs a new test server and returns a *ClientConn. 25 func dial(handler serverType, t *testing.T) *Client { 26 c1, c2, err := netPipe() 27 if err != nil { 28 t.Fatalf("netPipe: %v", err) 29 } 30 31 go func() { 32 defer c1.Close() 33 conf := ServerConfig{ 34 NoClientAuth: true, 35 } 36 conf.AddHostKey(testSigners["rsa"]) 37 38 conn, chans, reqs, err := NewServerConn(c1, &conf) 39 if err != nil { 40 t.Fatalf("Unable to handshake: %v", err) 41 } 42 go DiscardRequests(reqs) 43 44 for newCh := range chans { 45 if newCh.ChannelType() != "session" { 46 newCh.Reject(UnknownChannelType, "unknown channel type") 47 continue 48 } 49 50 ch, inReqs, err := newCh.Accept() 51 if err != nil { 52 t.Errorf("Accept: %v", err) 53 continue 54 } 55 go func() { 56 handler(ch, inReqs, t) 57 }() 58 } 59 if err := conn.Wait(); err != io.EOF { 60 t.Logf("server exit reason: %v", err) 61 } 62 }() 63 64 config := &ClientConfig{ 65 User: "testuser", 66 HostKeyCallback: InsecureIgnoreHostKey(), 67 } 68 69 conn, chans, reqs, err := NewClientConn(c2, "", config) 70 if err != nil { 71 t.Fatalf("unable to dial remote side: %v", err) 72 } 73 74 return NewClient(conn, chans, reqs) 75 } 76 77 // Test a simple string is returned to session.Stdout. 78 func TestSessionShell(t *testing.T) { 79 conn := dial(shellHandler, t) 80 defer conn.Close() 81 session, err := conn.NewSession() 82 if err != nil { 83 t.Fatalf("Unable to request new session: %v", err) 84 } 85 defer session.Close() 86 stdout := new(bytes.Buffer) 87 session.Stdout = stdout 88 if err := session.Shell(); err != nil { 89 t.Fatalf("Unable to execute command: %s", err) 90 } 91 if err := session.Wait(); err != nil { 92 t.Fatalf("Remote command did not exit cleanly: %v", err) 93 } 94 actual := stdout.String() 95 if actual != "golang" { 96 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) 97 } 98 } 99 100 // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it. 101 102 // Test a simple string is returned via StdoutPipe. 103 func TestSessionStdoutPipe(t *testing.T) { 104 conn := dial(shellHandler, t) 105 defer conn.Close() 106 session, err := conn.NewSession() 107 if err != nil { 108 t.Fatalf("Unable to request new session: %v", err) 109 } 110 defer session.Close() 111 stdout, err := session.StdoutPipe() 112 if err != nil { 113 t.Fatalf("Unable to request StdoutPipe(): %v", err) 114 } 115 var buf bytes.Buffer 116 if err := session.Shell(); err != nil { 117 t.Fatalf("Unable to execute command: %v", err) 118 } 119 done := make(chan bool, 1) 120 go func() { 121 if _, err := io.Copy(&buf, stdout); err != nil { 122 t.Errorf("Copy of stdout failed: %v", err) 123 } 124 done <- true 125 }() 126 if err := session.Wait(); err != nil { 127 t.Fatalf("Remote command did not exit cleanly: %v", err) 128 } 129 <-done 130 actual := buf.String() 131 if actual != "golang" { 132 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) 133 } 134 } 135 136 // Test that a simple string is returned via the Output helper, 137 // and that stderr is discarded. 138 func TestSessionOutput(t *testing.T) { 139 conn := dial(fixedOutputHandler, t) 140 defer conn.Close() 141 session, err := conn.NewSession() 142 if err != nil { 143 t.Fatalf("Unable to request new session: %v", err) 144 } 145 defer session.Close() 146 147 buf, err := session.Output("") // cmd is ignored by fixedOutputHandler 148 if err != nil { 149 t.Error("Remote command did not exit cleanly:", err) 150 } 151 w := "this-is-stdout." 152 g := string(buf) 153 if g != w { 154 t.Error("Remote command did not return expected string:") 155 t.Logf("want %q", w) 156 t.Logf("got %q", g) 157 } 158 } 159 160 // Test that both stdout and stderr are returned 161 // via the CombinedOutput helper. 162 func TestSessionCombinedOutput(t *testing.T) { 163 conn := dial(fixedOutputHandler, t) 164 defer conn.Close() 165 session, err := conn.NewSession() 166 if err != nil { 167 t.Fatalf("Unable to request new session: %v", err) 168 } 169 defer session.Close() 170 171 buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler 172 if err != nil { 173 t.Error("Remote command did not exit cleanly:", err) 174 } 175 const stdout = "this-is-stdout." 176 const stderr = "this-is-stderr." 177 g := string(buf) 178 if g != stdout+stderr && g != stderr+stdout { 179 t.Error("Remote command did not return expected string:") 180 t.Logf("want %q, or %q", stdout+stderr, stderr+stdout) 181 t.Logf("got %q", g) 182 } 183 } 184 185 // Test non-0 exit status is returned correctly. 186 func TestExitStatusNonZero(t *testing.T) { 187 conn := dial(exitStatusNonZeroHandler, t) 188 defer conn.Close() 189 session, err := conn.NewSession() 190 if err != nil { 191 t.Fatalf("Unable to request new session: %v", err) 192 } 193 defer session.Close() 194 if err := session.Shell(); err != nil { 195 t.Fatalf("Unable to execute command: %v", err) 196 } 197 err = session.Wait() 198 if err == nil { 199 t.Fatalf("expected command to fail but it didn't") 200 } 201 e, ok := err.(*ExitError) 202 if !ok { 203 t.Fatalf("expected *ExitError but got %T", err) 204 } 205 if e.ExitStatus() != 15 { 206 t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus()) 207 } 208 } 209 210 // Test 0 exit status is returned correctly. 211 func TestExitStatusZero(t *testing.T) { 212 conn := dial(exitStatusZeroHandler, t) 213 defer conn.Close() 214 session, err := conn.NewSession() 215 if err != nil { 216 t.Fatalf("Unable to request new session: %v", err) 217 } 218 defer session.Close() 219 220 if err := session.Shell(); err != nil { 221 t.Fatalf("Unable to execute command: %v", err) 222 } 223 err = session.Wait() 224 if err != nil { 225 t.Fatalf("expected nil but got %v", err) 226 } 227 } 228 229 // Test exit signal and status are both returned correctly. 230 func TestExitSignalAndStatus(t *testing.T) { 231 conn := dial(exitSignalAndStatusHandler, t) 232 defer conn.Close() 233 session, err := conn.NewSession() 234 if err != nil { 235 t.Fatalf("Unable to request new session: %v", err) 236 } 237 defer session.Close() 238 if err := session.Shell(); err != nil { 239 t.Fatalf("Unable to execute command: %v", err) 240 } 241 err = session.Wait() 242 if err == nil { 243 t.Fatalf("expected command to fail but it didn't") 244 } 245 e, ok := err.(*ExitError) 246 if !ok { 247 t.Fatalf("expected *ExitError but got %T", err) 248 } 249 if e.Signal() != "TERM" || e.ExitStatus() != 15 { 250 t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus()) 251 } 252 } 253 254 // Test exit signal and status are both returned correctly. 255 func TestKnownExitSignalOnly(t *testing.T) { 256 conn := dial(exitSignalHandler, t) 257 defer conn.Close() 258 session, err := conn.NewSession() 259 if err != nil { 260 t.Fatalf("Unable to request new session: %v", err) 261 } 262 defer session.Close() 263 if err := session.Shell(); err != nil { 264 t.Fatalf("Unable to execute command: %v", err) 265 } 266 err = session.Wait() 267 if err == nil { 268 t.Fatalf("expected command to fail but it didn't") 269 } 270 e, ok := err.(*ExitError) 271 if !ok { 272 t.Fatalf("expected *ExitError but got %T", err) 273 } 274 if e.Signal() != "TERM" || e.ExitStatus() != 143 { 275 t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus()) 276 } 277 } 278 279 // Test exit signal and status are both returned correctly. 280 func TestUnknownExitSignal(t *testing.T) { 281 conn := dial(exitSignalUnknownHandler, t) 282 defer conn.Close() 283 session, err := conn.NewSession() 284 if err != nil { 285 t.Fatalf("Unable to request new session: %v", err) 286 } 287 defer session.Close() 288 if err := session.Shell(); err != nil { 289 t.Fatalf("Unable to execute command: %v", err) 290 } 291 err = session.Wait() 292 if err == nil { 293 t.Fatalf("expected command to fail but it didn't") 294 } 295 e, ok := err.(*ExitError) 296 if !ok { 297 t.Fatalf("expected *ExitError but got %T", err) 298 } 299 if e.Signal() != "SYS" || e.ExitStatus() != 128 { 300 t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus()) 301 } 302 } 303 304 func TestExitWithoutStatusOrSignal(t *testing.T) { 305 conn := dial(exitWithoutSignalOrStatus, t) 306 defer conn.Close() 307 session, err := conn.NewSession() 308 if err != nil { 309 t.Fatalf("Unable to request new session: %v", err) 310 } 311 defer session.Close() 312 if err := session.Shell(); err != nil { 313 t.Fatalf("Unable to execute command: %v", err) 314 } 315 err = session.Wait() 316 if err == nil { 317 t.Fatalf("expected command to fail but it didn't") 318 } 319 if _, ok := err.(*ExitMissingError); !ok { 320 t.Fatalf("got %T want *ExitMissingError", err) 321 } 322 } 323 324 // windowTestBytes is the number of bytes that we'll send to the SSH server. 325 const windowTestBytes = 16000 * 200 326 327 // TestServerWindow writes random data to the server. The server is expected to echo 328 // the same data back, which is compared against the original. 329 func TestServerWindow(t *testing.T) { 330 origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) 331 io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes) 332 origBytes := origBuf.Bytes() 333 334 conn := dial(echoHandler, t) 335 defer conn.Close() 336 session, err := conn.NewSession() 337 if err != nil { 338 t.Fatal(err) 339 } 340 defer session.Close() 341 result := make(chan []byte) 342 343 go func() { 344 defer close(result) 345 echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) 346 serverStdout, err := session.StdoutPipe() 347 if err != nil { 348 t.Errorf("StdoutPipe failed: %v", err) 349 return 350 } 351 n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes) 352 if err != nil && err != io.EOF { 353 t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err) 354 } 355 result <- echoedBuf.Bytes() 356 }() 357 358 serverStdin, err := session.StdinPipe() 359 if err != nil { 360 t.Fatalf("StdinPipe failed: %v", err) 361 } 362 written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes) 363 if err != nil { 364 t.Errorf("failed to copy origBuf to serverStdin: %v", err) 365 } else if written != windowTestBytes { 366 t.Errorf("Wrote only %d of %d bytes to server", written, windowTestBytes) 367 } 368 369 echoedBytes := <-result 370 371 if !bytes.Equal(origBytes, echoedBytes) { 372 t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes)) 373 } 374 } 375 376 // Verify the client can handle a keepalive packet from the server. 377 func TestClientHandlesKeepalives(t *testing.T) { 378 conn := dial(channelKeepaliveSender, t) 379 defer conn.Close() 380 session, err := conn.NewSession() 381 if err != nil { 382 t.Fatal(err) 383 } 384 defer session.Close() 385 if err := session.Shell(); err != nil { 386 t.Fatalf("Unable to execute command: %v", err) 387 } 388 err = session.Wait() 389 if err != nil { 390 t.Fatalf("expected nil but got: %v", err) 391 } 392 } 393 394 type exitStatusMsg struct { 395 Status uint32 396 } 397 398 type exitSignalMsg struct { 399 Signal string 400 CoreDumped bool 401 Errmsg string 402 Lang string 403 } 404 405 func handleTerminalRequests(in <-chan *Request) { 406 for req := range in { 407 ok := false 408 switch req.Type { 409 case "shell": 410 ok = true 411 if len(req.Payload) > 0 { 412 // We don't accept any commands, only the default shell. 413 ok = false 414 } 415 case "env": 416 ok = true 417 } 418 req.Reply(ok, nil) 419 } 420 } 421 422 func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal { 423 term := terminal.NewTerminal(ch, prompt) 424 go handleTerminalRequests(in) 425 return term 426 } 427 428 func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { 429 defer ch.Close() 430 // this string is returned to stdout 431 shell := newServerShell(ch, in, "> ") 432 readLine(shell, t) 433 sendStatus(0, ch, t) 434 } 435 436 func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { 437 defer ch.Close() 438 shell := newServerShell(ch, in, "> ") 439 readLine(shell, t) 440 sendStatus(15, ch, t) 441 } 442 443 func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) { 444 defer ch.Close() 445 shell := newServerShell(ch, in, "> ") 446 readLine(shell, t) 447 sendStatus(15, ch, t) 448 sendSignal("TERM", ch, t) 449 } 450 451 func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) { 452 defer ch.Close() 453 shell := newServerShell(ch, in, "> ") 454 readLine(shell, t) 455 sendSignal("TERM", ch, t) 456 } 457 458 func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) { 459 defer ch.Close() 460 shell := newServerShell(ch, in, "> ") 461 readLine(shell, t) 462 sendSignal("SYS", ch, t) 463 } 464 465 func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) { 466 defer ch.Close() 467 shell := newServerShell(ch, in, "> ") 468 readLine(shell, t) 469 } 470 471 func shellHandler(ch Channel, in <-chan *Request, t *testing.T) { 472 defer ch.Close() 473 // this string is returned to stdout 474 shell := newServerShell(ch, in, "golang") 475 readLine(shell, t) 476 sendStatus(0, ch, t) 477 } 478 479 // Ignores the command, writes fixed strings to stderr and stdout. 480 // Strings are "this-is-stdout." and "this-is-stderr.". 481 func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) { 482 defer ch.Close() 483 _, err := ch.Read(nil) 484 485 req, ok := <-in 486 if !ok { 487 t.Fatalf("error: expected channel request, got: %#v", err) 488 return 489 } 490 491 // ignore request, always send some text 492 req.Reply(true, nil) 493 494 _, err = io.WriteString(ch, "this-is-stdout.") 495 if err != nil { 496 t.Fatalf("error writing on server: %v", err) 497 } 498 _, err = io.WriteString(ch.Stderr(), "this-is-stderr.") 499 if err != nil { 500 t.Fatalf("error writing on server: %v", err) 501 } 502 sendStatus(0, ch, t) 503 } 504 505 func readLine(shell *terminal.Terminal, t *testing.T) { 506 if _, err := shell.ReadLine(); err != nil && err != io.EOF { 507 t.Errorf("unable to read line: %v", err) 508 } 509 } 510 511 func sendStatus(status uint32, ch Channel, t *testing.T) { 512 msg := exitStatusMsg{ 513 Status: status, 514 } 515 if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil { 516 t.Errorf("unable to send status: %v", err) 517 } 518 } 519 520 func sendSignal(signal string, ch Channel, t *testing.T) { 521 sig := exitSignalMsg{ 522 Signal: signal, 523 CoreDumped: false, 524 Errmsg: "Process terminated", 525 Lang: "en-GB-oed", 526 } 527 if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil { 528 t.Errorf("unable to send signal: %v", err) 529 } 530 } 531 532 func discardHandler(ch Channel, t *testing.T) { 533 defer ch.Close() 534 io.Copy(ioutil.Discard, ch) 535 } 536 537 func echoHandler(ch Channel, in <-chan *Request, t *testing.T) { 538 defer ch.Close() 539 if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil { 540 t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err) 541 } 542 } 543 544 // copyNRandomly copies n bytes from src to dst. It uses a variable, and random, 545 // buffer size to exercise more code paths. 546 func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) { 547 var ( 548 buf = make([]byte, 32*1024) 549 written int 550 remaining = n 551 ) 552 for remaining > 0 { 553 l := rand.Intn(1 << 15) 554 if remaining < l { 555 l = remaining 556 } 557 nr, er := src.Read(buf[:l]) 558 nw, ew := dst.Write(buf[:nr]) 559 remaining -= nw 560 written += nw 561 if ew != nil { 562 return written, ew 563 } 564 if nr != nw { 565 return written, io.ErrShortWrite 566 } 567 if er != nil && er != io.EOF { 568 return written, er 569 } 570 } 571 return written, nil 572 } 573 574 func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) { 575 defer ch.Close() 576 shell := newServerShell(ch, in, "> ") 577 readLine(shell, t) 578 if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil { 579 t.Errorf("unable to send channel keepalive request: %v", err) 580 } 581 sendStatus(0, ch, t) 582 } 583 584 func TestClientWriteEOF(t *testing.T) { 585 conn := dial(simpleEchoHandler, t) 586 defer conn.Close() 587 588 session, err := conn.NewSession() 589 if err != nil { 590 t.Fatal(err) 591 } 592 defer session.Close() 593 stdin, err := session.StdinPipe() 594 if err != nil { 595 t.Fatalf("StdinPipe failed: %v", err) 596 } 597 stdout, err := session.StdoutPipe() 598 if err != nil { 599 t.Fatalf("StdoutPipe failed: %v", err) 600 } 601 602 data := []byte(`0000`) 603 _, err = stdin.Write(data) 604 if err != nil { 605 t.Fatalf("Write failed: %v", err) 606 } 607 stdin.Close() 608 609 res, err := ioutil.ReadAll(stdout) 610 if err != nil { 611 t.Fatalf("Read failed: %v", err) 612 } 613 614 if !bytes.Equal(data, res) { 615 t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res) 616 } 617 } 618 619 func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) { 620 defer ch.Close() 621 data, err := ioutil.ReadAll(ch) 622 if err != nil { 623 t.Errorf("handler read error: %v", err) 624 } 625 _, err = ch.Write(data) 626 if err != nil { 627 t.Errorf("handler write error: %v", err) 628 } 629 } 630 631 func TestSessionID(t *testing.T) { 632 c1, c2, err := netPipe() 633 if err != nil { 634 t.Fatalf("netPipe: %v", err) 635 } 636 defer c1.Close() 637 defer c2.Close() 638 639 serverID := make(chan []byte, 1) 640 clientID := make(chan []byte, 1) 641 642 serverConf := &ServerConfig{ 643 NoClientAuth: true, 644 } 645 serverConf.AddHostKey(testSigners["ecdsa"]) 646 clientConf := &ClientConfig{ 647 HostKeyCallback: InsecureIgnoreHostKey(), 648 User: "user", 649 } 650 651 go func() { 652 conn, chans, reqs, err := NewServerConn(c1, serverConf) 653 if err != nil { 654 t.Fatalf("server handshake: %v", err) 655 } 656 serverID <- conn.SessionID() 657 go DiscardRequests(reqs) 658 for ch := range chans { 659 ch.Reject(Prohibited, "") 660 } 661 }() 662 663 go func() { 664 conn, chans, reqs, err := NewClientConn(c2, "", clientConf) 665 if err != nil { 666 t.Fatalf("client handshake: %v", err) 667 } 668 clientID <- conn.SessionID() 669 go DiscardRequests(reqs) 670 for ch := range chans { 671 ch.Reject(Prohibited, "") 672 } 673 }() 674 675 s := <-serverID 676 c := <-clientID 677 if bytes.Compare(s, c) != 0 { 678 t.Errorf("server session ID (%x) != client session ID (%x)", s, c) 679 } else if len(s) == 0 { 680 t.Errorf("client and server SessionID were empty.") 681 } 682 } 683 684 type noReadConn struct { 685 readSeen bool 686 net.Conn 687 } 688 689 func (c *noReadConn) Close() error { 690 return nil 691 } 692 693 func (c *noReadConn) Read(b []byte) (int, error) { 694 c.readSeen = true 695 return 0, errors.New("noReadConn error") 696 } 697 698 func TestInvalidServerConfiguration(t *testing.T) { 699 c1, c2, err := netPipe() 700 if err != nil { 701 t.Fatalf("netPipe: %v", err) 702 } 703 defer c1.Close() 704 defer c2.Close() 705 706 serveConn := noReadConn{Conn: c1} 707 serverConf := &ServerConfig{} 708 709 NewServerConn(&serveConn, serverConf) 710 if serveConn.readSeen { 711 t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing host key") 712 } 713 714 serverConf.AddHostKey(testSigners["ecdsa"]) 715 716 NewServerConn(&serveConn, serverConf) 717 if serveConn.readSeen { 718 t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method") 719 } 720 } 721 722 func TestHostKeyAlgorithms(t *testing.T) { 723 serverConf := &ServerConfig{ 724 NoClientAuth: true, 725 } 726 serverConf.AddHostKey(testSigners["rsa"]) 727 serverConf.AddHostKey(testSigners["ecdsa"]) 728 729 connect := func(clientConf *ClientConfig, want string) { 730 var alg string 731 clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error { 732 alg = key.Type() 733 return nil 734 } 735 c1, c2, err := netPipe() 736 if err != nil { 737 t.Fatalf("netPipe: %v", err) 738 } 739 defer c1.Close() 740 defer c2.Close() 741 742 go NewServerConn(c1, serverConf) 743 _, _, _, err = NewClientConn(c2, "", clientConf) 744 if err != nil { 745 t.Fatalf("NewClientConn: %v", err) 746 } 747 if alg != want { 748 t.Errorf("selected key algorithm %s, want %s", alg, want) 749 } 750 } 751 752 // By default, we get the preferred algorithm, which is ECDSA 256. 753 754 clientConf := &ClientConfig{ 755 HostKeyCallback: InsecureIgnoreHostKey(), 756 } 757 connect(clientConf, KeyAlgoECDSA256) 758 759 // Client asks for RSA explicitly. 760 clientConf.HostKeyAlgorithms = []string{SigAlgoRSA} 761 connect(clientConf, KeyAlgoRSA) 762 763 // Client asks for RSA-SHA2-512 explicitly. 764 clientConf.HostKeyAlgorithms = []string{SigAlgoRSASHA2512} 765 // We get back an "ssh-rsa" key but the verification happened 766 // with an RSA-SHA2-512 signature. 767 connect(clientConf, KeyAlgoRSA) 768 769 c1, c2, err := netPipe() 770 if err != nil { 771 t.Fatalf("netPipe: %v", err) 772 } 773 defer c1.Close() 774 defer c2.Close() 775 776 go NewServerConn(c1, serverConf) 777 clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"} 778 _, _, _, err = NewClientConn(c2, "", clientConf) 779 if err == nil { 780 t.Fatal("succeeded connecting with unknown hostkey algorithm") 781 } 782 }