session_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. // Copyright 2012 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. //go:build !windows && !js && !wasip1
  5. package test
  6. // Session functional tests.
  7. import (
  8. "bytes"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "path/filepath"
  13. "regexp"
  14. "runtime"
  15. "strings"
  16. "testing"
  17. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh"
  18. )
  19. func skipIfIssue64959(t *testing.T, err error) {
  20. if err != nil && runtime.GOOS == "darwin" && strings.Contains(err.Error(), "ssh: unexpected packet in response to channel open: <nil>") {
  21. t.Helper()
  22. t.Skipf("skipping test broken on some versions of macOS; see https://go.dev/issue/64959")
  23. }
  24. }
  25. func TestRunCommandSuccess(t *testing.T) {
  26. server := newServer(t)
  27. conn := server.Dial(clientConfig())
  28. defer conn.Close()
  29. session, err := conn.NewSession()
  30. if err != nil {
  31. skipIfIssue64959(t, err)
  32. t.Fatalf("session failed: %v", err)
  33. }
  34. defer session.Close()
  35. err = session.Run("true")
  36. if err != nil {
  37. t.Fatalf("session failed: %v", err)
  38. }
  39. }
  40. func TestHostKeyCheck(t *testing.T) {
  41. server := newServer(t)
  42. conf := clientConfig()
  43. hostDB := hostKeyDB()
  44. conf.HostKeyCallback = hostDB.Check
  45. // change the keys.
  46. hostDB.keys[ssh.KeyAlgoRSA][25]++
  47. hostDB.keys[ssh.KeyAlgoDSA][25]++
  48. hostDB.keys[ssh.KeyAlgoECDSA256][25]++
  49. conn, err := server.TryDial(conf)
  50. if err == nil {
  51. conn.Close()
  52. t.Fatalf("dial should have failed.")
  53. } else if !strings.Contains(err.Error(), "host key mismatch") {
  54. t.Fatalf("'host key mismatch' not found in %v", err)
  55. }
  56. }
  57. func TestRunCommandStdin(t *testing.T) {
  58. server := newServer(t)
  59. conn := server.Dial(clientConfig())
  60. defer conn.Close()
  61. session, err := conn.NewSession()
  62. if err != nil {
  63. skipIfIssue64959(t, err)
  64. t.Fatalf("session failed: %v", err)
  65. }
  66. defer session.Close()
  67. r, w := io.Pipe()
  68. defer r.Close()
  69. defer w.Close()
  70. session.Stdin = r
  71. err = session.Run("true")
  72. if err != nil {
  73. t.Fatalf("session failed: %v", err)
  74. }
  75. }
  76. func TestRunCommandStdinError(t *testing.T) {
  77. server := newServer(t)
  78. conn := server.Dial(clientConfig())
  79. defer conn.Close()
  80. session, err := conn.NewSession()
  81. if err != nil {
  82. skipIfIssue64959(t, err)
  83. t.Fatalf("session failed: %v", err)
  84. }
  85. defer session.Close()
  86. r, w := io.Pipe()
  87. defer r.Close()
  88. session.Stdin = r
  89. pipeErr := errors.New("closing write end of pipe")
  90. w.CloseWithError(pipeErr)
  91. err = session.Run("true")
  92. if err != pipeErr {
  93. t.Fatalf("expected %v, found %v", pipeErr, err)
  94. }
  95. }
  96. func TestRunCommandFailed(t *testing.T) {
  97. server := newServer(t)
  98. conn := server.Dial(clientConfig())
  99. defer conn.Close()
  100. session, err := conn.NewSession()
  101. if err != nil {
  102. skipIfIssue64959(t, err)
  103. t.Fatalf("session failed: %v", err)
  104. }
  105. defer session.Close()
  106. err = session.Run(`bash -c "kill -9 $$"`)
  107. if err == nil {
  108. t.Fatalf("session succeeded: %v", err)
  109. }
  110. }
  111. func TestRunCommandWeClosed(t *testing.T) {
  112. server := newServer(t)
  113. conn := server.Dial(clientConfig())
  114. defer conn.Close()
  115. session, err := conn.NewSession()
  116. if err != nil {
  117. skipIfIssue64959(t, err)
  118. t.Fatalf("session failed: %v", err)
  119. }
  120. err = session.Shell()
  121. if err != nil {
  122. t.Fatalf("shell failed: %v", err)
  123. }
  124. err = session.Close()
  125. if err != nil {
  126. t.Fatalf("shell failed: %v", err)
  127. }
  128. }
  129. func TestFuncLargeRead(t *testing.T) {
  130. server := newServer(t)
  131. conn := server.Dial(clientConfig())
  132. defer conn.Close()
  133. session, err := conn.NewSession()
  134. if err != nil {
  135. skipIfIssue64959(t, err)
  136. t.Fatalf("unable to create new session: %s", err)
  137. }
  138. stdout, err := session.StdoutPipe()
  139. if err != nil {
  140. t.Fatalf("unable to acquire stdout pipe: %s", err)
  141. }
  142. err = session.Start("dd if=/dev/urandom bs=2048 count=1024")
  143. if err != nil {
  144. t.Fatalf("unable to execute remote command: %s", err)
  145. }
  146. buf := new(bytes.Buffer)
  147. n, err := io.Copy(buf, stdout)
  148. if err != nil {
  149. t.Fatalf("error reading from remote stdout: %s", err)
  150. }
  151. if n != 2048*1024 {
  152. t.Fatalf("Expected %d bytes but read only %d from remote command", 2048, n)
  153. }
  154. }
  155. func TestKeyChange(t *testing.T) {
  156. server := newServer(t)
  157. conf := clientConfig()
  158. hostDB := hostKeyDB()
  159. conf.HostKeyCallback = hostDB.Check
  160. conf.RekeyThreshold = 1024
  161. conn := server.Dial(conf)
  162. defer conn.Close()
  163. for i := 0; i < 4; i++ {
  164. session, err := conn.NewSession()
  165. if err != nil {
  166. skipIfIssue64959(t, err)
  167. t.Fatalf("unable to create new session: %s", err)
  168. }
  169. stdout, err := session.StdoutPipe()
  170. if err != nil {
  171. t.Fatalf("unable to acquire stdout pipe: %s", err)
  172. }
  173. err = session.Start("dd if=/dev/urandom bs=1024 count=1")
  174. if err != nil {
  175. t.Fatalf("unable to execute remote command: %s", err)
  176. }
  177. buf := new(bytes.Buffer)
  178. n, err := io.Copy(buf, stdout)
  179. if err != nil {
  180. t.Fatalf("error reading from remote stdout: %s", err)
  181. }
  182. want := int64(1024)
  183. if n != want {
  184. t.Fatalf("Expected %d bytes but read only %d from remote command", want, n)
  185. }
  186. }
  187. if changes := hostDB.checkCount; changes < 4 {
  188. t.Errorf("got %d key changes, want 4", changes)
  189. }
  190. }
  191. func TestValidTerminalMode(t *testing.T) {
  192. if runtime.GOOS == "aix" {
  193. // On AIX, sshd cannot acquire /dev/pts/* if launched as
  194. // a non-root user.
  195. t.Skipf("skipping on %s", runtime.GOOS)
  196. }
  197. server := newServer(t)
  198. conn := server.Dial(clientConfig())
  199. defer conn.Close()
  200. session, err := conn.NewSession()
  201. if err != nil {
  202. skipIfIssue64959(t, err)
  203. t.Fatalf("session failed: %v", err)
  204. }
  205. defer session.Close()
  206. stdout, err := session.StdoutPipe()
  207. if err != nil {
  208. t.Fatalf("unable to acquire stdout pipe: %s", err)
  209. }
  210. stdin, err := session.StdinPipe()
  211. if err != nil {
  212. t.Fatalf("unable to acquire stdin pipe: %s", err)
  213. }
  214. tm := ssh.TerminalModes{ssh.ECHO: 0}
  215. if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
  216. t.Fatalf("req-pty failed: %s", err)
  217. }
  218. err = session.Shell()
  219. if err != nil {
  220. t.Fatalf("session failed: %s", err)
  221. }
  222. if _, err := io.WriteString(stdin, "echo && echo SHELL $SHELL && stty -a && exit\n"); err != nil {
  223. t.Fatal(err)
  224. }
  225. buf := new(strings.Builder)
  226. if _, err := io.Copy(buf, stdout); err != nil {
  227. t.Fatalf("reading failed: %s", err)
  228. }
  229. if testing.Verbose() {
  230. t.Logf("echo && echo SHELL $SHELL && stty -a && exit:\n%s", buf)
  231. }
  232. shellLine := regexp.MustCompile("(?m)^SHELL (.*)$").FindStringSubmatch(buf.String())
  233. if len(shellLine) != 2 {
  234. t.Fatalf("missing output from echo SHELL $SHELL")
  235. }
  236. switch shell := filepath.Base(strings.TrimSpace(shellLine[1])); shell {
  237. case "sh", "bash":
  238. default:
  239. t.Skipf("skipping test on non-Bourne shell %q", shell)
  240. }
  241. if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "-echo ") {
  242. t.Fatal("terminal mode failure: expected -echo in stty output")
  243. }
  244. }
  245. func TestWindowChange(t *testing.T) {
  246. if runtime.GOOS == "aix" {
  247. // On AIX, sshd cannot acquire /dev/pts/* if launched as
  248. // a non-root user.
  249. t.Skipf("skipping on %s", runtime.GOOS)
  250. }
  251. server := newServer(t)
  252. conn := server.Dial(clientConfig())
  253. defer conn.Close()
  254. session, err := conn.NewSession()
  255. if err != nil {
  256. skipIfIssue64959(t, err)
  257. t.Fatalf("session failed: %v", err)
  258. }
  259. defer session.Close()
  260. stdout, err := session.StdoutPipe()
  261. if err != nil {
  262. t.Fatalf("unable to acquire stdout pipe: %s", err)
  263. }
  264. stdin, err := session.StdinPipe()
  265. if err != nil {
  266. t.Fatalf("unable to acquire stdin pipe: %s", err)
  267. }
  268. tm := ssh.TerminalModes{ssh.ECHO: 0}
  269. if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
  270. t.Fatalf("req-pty failed: %s", err)
  271. }
  272. if err := session.WindowChange(100, 100); err != nil {
  273. t.Fatalf("window-change failed: %s", err)
  274. }
  275. err = session.Shell()
  276. if err != nil {
  277. t.Fatalf("session failed: %s", err)
  278. }
  279. stdin.Write([]byte("stty size && exit\n"))
  280. var buf bytes.Buffer
  281. if _, err := io.Copy(&buf, stdout); err != nil {
  282. t.Fatalf("reading failed: %s", err)
  283. }
  284. if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "100 100") {
  285. t.Fatalf("terminal WindowChange failure: expected \"100 100\" stty output, got %s", sttyOutput)
  286. }
  287. }
  288. func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
  289. server := newServer(t)
  290. conf := clientConfig()
  291. conf.Ciphers = []string{cipher}
  292. // Don't fail if sshd doesn't have the cipher.
  293. conf.Ciphers = append(conf.Ciphers, cipherOrder...)
  294. conn, err := server.TryDial(conf)
  295. if err != nil {
  296. t.Fatalf("TryDial: %v", err)
  297. }
  298. defer conn.Close()
  299. numBytes := 4096
  300. // Exercise receiving data from the server
  301. session, err := conn.NewSession()
  302. if err != nil {
  303. skipIfIssue64959(t, err)
  304. t.Fatalf("NewSession: %v", err)
  305. }
  306. out, err := session.Output(fmt.Sprintf("dd if=/dev/zero bs=%d count=1", numBytes))
  307. if err != nil {
  308. t.Fatalf("Output: %v", err)
  309. }
  310. if len(out) != numBytes {
  311. t.Fatalf("got %d bytes, want %d bytes", len(out), numBytes)
  312. }
  313. // Exercise sending data to the server
  314. if _, _, err := conn.Conn.SendRequest("drop-me", false, make([]byte, numBytes)); err != nil {
  315. t.Fatalf("SendRequest: %v", err)
  316. }
  317. }
  318. var deprecatedCiphers = []string{
  319. "aes128-cbc", "3des-cbc",
  320. "arcfour128", "arcfour256",
  321. }
  322. func TestCiphers(t *testing.T) {
  323. var config ssh.Config
  324. config.SetDefaults()
  325. cipherOrder := append(config.Ciphers, deprecatedCiphers...)
  326. for _, ciph := range cipherOrder {
  327. t.Run(ciph, func(t *testing.T) {
  328. testOneCipher(t, ciph, cipherOrder)
  329. })
  330. }
  331. }
  332. func TestMACs(t *testing.T) {
  333. var config ssh.Config
  334. config.SetDefaults()
  335. macOrder := config.MACs
  336. for _, mac := range macOrder {
  337. t.Run(mac, func(t *testing.T) {
  338. server := newServer(t)
  339. conf := clientConfig()
  340. conf.MACs = []string{mac}
  341. // Don't fail if sshd doesn't have the MAC.
  342. conf.MACs = append(conf.MACs, macOrder...)
  343. if conn, err := server.TryDial(conf); err == nil {
  344. conn.Close()
  345. } else {
  346. t.Fatalf("failed for MAC %q", mac)
  347. }
  348. })
  349. }
  350. }
  351. func TestKeyExchanges(t *testing.T) {
  352. var config ssh.Config
  353. config.SetDefaults()
  354. kexOrder := config.KeyExchanges
  355. // Based on the discussion in #17230, the key exchange algorithms
  356. // diffie-hellman-group-exchange-sha1 and diffie-hellman-group-exchange-sha256
  357. // are not included in the default list of supported kex so we have to add them
  358. // here manually.
  359. kexOrder = append(kexOrder, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256")
  360. // The key exchange algorithms diffie-hellman-group16-sha512 is disabled by
  361. // default so we add it here manually.
  362. kexOrder = append(kexOrder, "diffie-hellman-group16-sha512")
  363. for _, kex := range kexOrder {
  364. t.Run(kex, func(t *testing.T) {
  365. server := newServer(t)
  366. conf := clientConfig()
  367. // Don't fail if sshd doesn't have the kex.
  368. conf.KeyExchanges = append([]string{kex}, kexOrder...)
  369. conn, err := server.TryDial(conf)
  370. if err == nil {
  371. conn.Close()
  372. } else {
  373. t.Errorf("failed for kex %q", kex)
  374. }
  375. })
  376. }
  377. }
  378. func TestClientAuthAlgorithms(t *testing.T) {
  379. for _, key := range []string{
  380. "rsa",
  381. "ecdsa",
  382. "ed25519",
  383. } {
  384. t.Run(key, func(t *testing.T) {
  385. server := newServer(t)
  386. conf := clientConfig()
  387. conf.SetDefaults()
  388. conf.Auth = []ssh.AuthMethod{
  389. ssh.PublicKeys(testSigners[key]),
  390. }
  391. conn, err := server.TryDial(conf)
  392. if err == nil {
  393. conn.Close()
  394. } else {
  395. t.Errorf("failed for key %q", key)
  396. }
  397. })
  398. }
  399. }
  400. func TestClientAuthDisconnect(t *testing.T) {
  401. // Use a static key that is not accepted by server.
  402. // This key has been generated with following ssh-keygen command and
  403. // used exclusively in this unit test:
  404. // $ ssh-keygen -t RSA -b 2048 -f /tmp/static_key \
  405. // -C "Static RSA key for golang.org/x/crypto/ssh unit test"
  406. const privKeyData = `-----BEGIN OPENSSH PRIVATE KEY-----
  407. b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
  408. NhAAAAAwEAAQAAAQEAwV1Zg3MqX27nIQQNWd8V09P4q4F1fx7H2xNJdL3Yg3y91GFLJ92+
  409. 0IiGV8n1VMGL/71PPhzyqBpUYSTpWjiU2JZSfA+iTg1GJBcOaEOA6vrXsTtXTHZ//mOT4d
  410. mlvuP4+9NqfCBLGXN7ZJpT+amkD8AVW9YW9QN3ipY61ZWxPaAocVpDd8rVgJTk54KvaPa7
  411. t4ddOSQDQq61aubIDR1Z3P+XjkB4piWOsbck3HJL+veTALy12C09tAhwUnZUAXS+DjhxOL
  412. xpDVclF/yXYhAvBvsjwyk/OC3+nK9F799hpQZsjxmbP7oN+tGwz06BUcAKi7u7QstENvvk
  413. 85SDZy1q1QAAA/A7ylbJO8pWyQAAAAdzc2gtcnNhAAABAQDBXVmDcypfbuchBA1Z3xXT0/
  414. irgXV/HsfbE0l0vdiDfL3UYUsn3b7QiIZXyfVUwYv/vU8+HPKoGlRhJOlaOJTYllJ8D6JO
  415. DUYkFw5oQ4Dq+texO1dMdn/+Y5Ph2aW+4/j702p8IEsZc3tkmlP5qaQPwBVb1hb1A3eKlj
  416. rVlbE9oChxWkN3ytWAlOTngq9o9ru3h105JANCrrVq5sgNHVnc/5eOQHimJY6xtyTcckv6
  417. 95MAvLXYLT20CHBSdlQBdL4OOHE4vGkNVyUX/JdiEC8G+yPDKT84Lf6cr0Xv32GlBmyPGZ
  418. s/ug360bDPToFRwAqLu7tCy0Q2++TzlINnLWrVAAAAAwEAAQAAAQAIvPDHMiyIxgCksGPF
  419. uyv9F9U4XjVip8/abE9zkAMJWW5++wuT/bRlBOUPRrWIXZEM9ETbtsqswo3Wxah+7CjRIH
  420. qR7SdFlYTP1jPk4yIKXF4OvggBUPySkMpAGJ9hwOMW8Ymcb4gn77JJ4aMoWIcXssje+XiC
  421. 8iO+4UWU3SV2i6K7flK1UDCI5JVCyBr3DVf3QhMOgvwJl9TgD7FzWy1hkjuZq/Pzdv+fA2
  422. OfrUFiSukLNolidNoI9+KWa1yxixE+B2oN4Xan3ZbqGbL6Wc1dB+K9h/bNcu+SKf7fXWRi
  423. /vVG44A61xGDZzen1+eQlqFp7narkKXoaU71+45VXDThAAAAgBPWUdQykEEm0yOS6hPIW+
  424. hS8z1LXWGTEcag9fMwJXKE7cQFO3LEk+dXMbClHdhD/ydswOZYGSNepxwvmo/a5LiO2ulp
  425. W+5tnsNhcK3skdaf71t+boUEXBNZ6u3WNTkU7tDN8h9tebI+xlNceDGSGjOlNoHQVMKZdA
  426. W9TA4ZqXUPAAAAgQDWU0UZVOSCAOODPz4PYsbFKdCfXNP8O4+t9txyc9E3hsLAsVs+CpVX
  427. Gr219MGLrublzAxojipyzuQb6Tp1l9nsu7VkcBrPL8I1tokz0AyTnmNF3A9KszBal7gGNS
  428. a2qYuf6JO4cub1KzonxUJQHZPZq9YhCxOtDwTd+uyHZiPy9QAAAIEA5vayd+nfVJgCKTdf
  429. z5MFsxBSUj/cAYg7JYPS/0bZ5bEkLosL22wl5Tm/ZftJa8apkyBPhguAWt6jEWLoDiK+kn
  430. Fv0SaEq1HUdXgWmISVnWzv2pxdAtq/apmbxTg3iIJyrAwEDo13iImR3k6rNPx1m3i/jX56
  431. HLcvWM4Y6bFzbGEAAAA0U3RhdGljIFJTQSBrZXkgZm9yIGdvbGFuZy5vcmcveC9jcnlwdG
  432. 8vc3NoIHVuaXQgdGVzdAECAwQFBgc=
  433. -----END OPENSSH PRIVATE KEY-----`
  434. signer, err := ssh.ParsePrivateKey([]byte(privKeyData))
  435. if err != nil {
  436. t.Fatalf("failed to create signer from key: %v", err)
  437. }
  438. // Start server with MaxAuthTries 1 and publickey and password auth
  439. // enabled
  440. server := newServerForConfig(t, "MaxAuthTries", map[string]string{})
  441. // Connect to server, expect failure, that PublicKeysCallback is called
  442. // and that PasswordCallback is not called.
  443. publicKeysCallbackCalled := false
  444. config := clientConfig()
  445. config.Auth = []ssh.AuthMethod{
  446. ssh.PublicKeysCallback(func() ([]ssh.Signer, error) {
  447. publicKeysCallbackCalled = true
  448. return []ssh.Signer{signer}, nil
  449. }),
  450. ssh.PasswordCallback(func() (string, error) {
  451. t.Errorf("unexpected call to PasswordCallback()")
  452. return "notaverygoodpassword", nil
  453. }),
  454. }
  455. client, err := server.TryDial(config)
  456. if err == nil {
  457. t.Errorf("expected TryDial() to fail")
  458. _ = client.Close()
  459. }
  460. if !publicKeysCallbackCalled {
  461. t.Errorf("expected PublicKeysCallback() to be called")
  462. }
  463. }