You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

774 lines
19 KiB

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