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