github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/crypto/ssh/test/session_test.go (about) 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 5 //go:build !windows && !solaris && !js 6 // +build !windows,!solaris,!js 7 8 package test 9 10 // Session functional tests. 11 12 import ( 13 "bytes" 14 "errors" 15 "fmt" 16 "io" 17 "runtime" 18 "strings" 19 "testing" 20 21 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh" 22 ) 23 24 func TestRunCommandSuccess(t *testing.T) { 25 server := newServer(t) 26 defer server.Shutdown() 27 conn := server.Dial(clientConfig()) 28 defer conn.Close() 29 30 session, err := conn.NewSession() 31 if err != nil { 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 41 func TestHostKeyCheck(t *testing.T) { 42 server := newServer(t) 43 defer server.Shutdown() 44 45 conf := clientConfig() 46 hostDB := hostKeyDB() 47 conf.HostKeyCallback = hostDB.Check 48 49 // change the keys. 50 hostDB.keys[ssh.KeyAlgoRSA][25]++ 51 hostDB.keys[ssh.KeyAlgoDSA][25]++ 52 hostDB.keys[ssh.KeyAlgoECDSA256][25]++ 53 54 conn, err := server.TryDial(conf) 55 if err == nil { 56 conn.Close() 57 t.Fatalf("dial should have failed.") 58 } else if !strings.Contains(err.Error(), "host key mismatch") { 59 t.Fatalf("'host key mismatch' not found in %v", err) 60 } 61 } 62 63 func TestRunCommandStdin(t *testing.T) { 64 server := newServer(t) 65 defer server.Shutdown() 66 conn := server.Dial(clientConfig()) 67 defer conn.Close() 68 69 session, err := conn.NewSession() 70 if err != nil { 71 t.Fatalf("session failed: %v", err) 72 } 73 defer session.Close() 74 75 r, w := io.Pipe() 76 defer r.Close() 77 defer w.Close() 78 session.Stdin = r 79 80 err = session.Run("true") 81 if err != nil { 82 t.Fatalf("session failed: %v", err) 83 } 84 } 85 86 func TestRunCommandStdinError(t *testing.T) { 87 server := newServer(t) 88 defer server.Shutdown() 89 conn := server.Dial(clientConfig()) 90 defer conn.Close() 91 92 session, err := conn.NewSession() 93 if err != nil { 94 t.Fatalf("session failed: %v", err) 95 } 96 defer session.Close() 97 98 r, w := io.Pipe() 99 defer r.Close() 100 session.Stdin = r 101 pipeErr := errors.New("closing write end of pipe") 102 w.CloseWithError(pipeErr) 103 104 err = session.Run("true") 105 if err != pipeErr { 106 t.Fatalf("expected %v, found %v", pipeErr, err) 107 } 108 } 109 110 func TestRunCommandFailed(t *testing.T) { 111 server := newServer(t) 112 defer server.Shutdown() 113 conn := server.Dial(clientConfig()) 114 defer conn.Close() 115 116 session, err := conn.NewSession() 117 if err != nil { 118 t.Fatalf("session failed: %v", err) 119 } 120 defer session.Close() 121 err = session.Run(`bash -c "kill -9 $$"`) 122 if err == nil { 123 t.Fatalf("session succeeded: %v", err) 124 } 125 } 126 127 func TestRunCommandWeClosed(t *testing.T) { 128 server := newServer(t) 129 defer server.Shutdown() 130 conn := server.Dial(clientConfig()) 131 defer conn.Close() 132 133 session, err := conn.NewSession() 134 if err != nil { 135 t.Fatalf("session failed: %v", err) 136 } 137 err = session.Shell() 138 if err != nil { 139 t.Fatalf("shell failed: %v", err) 140 } 141 err = session.Close() 142 if err != nil { 143 t.Fatalf("shell failed: %v", err) 144 } 145 } 146 147 func TestFuncLargeRead(t *testing.T) { 148 server := newServer(t) 149 defer server.Shutdown() 150 conn := server.Dial(clientConfig()) 151 defer conn.Close() 152 153 session, err := conn.NewSession() 154 if err != nil { 155 t.Fatalf("unable to create new session: %s", err) 156 } 157 158 stdout, err := session.StdoutPipe() 159 if err != nil { 160 t.Fatalf("unable to acquire stdout pipe: %s", err) 161 } 162 163 err = session.Start("dd if=/dev/urandom bs=2048 count=1024") 164 if err != nil { 165 t.Fatalf("unable to execute remote command: %s", err) 166 } 167 168 buf := new(bytes.Buffer) 169 n, err := io.Copy(buf, stdout) 170 if err != nil { 171 t.Fatalf("error reading from remote stdout: %s", err) 172 } 173 174 if n != 2048*1024 { 175 t.Fatalf("Expected %d bytes but read only %d from remote command", 2048, n) 176 } 177 } 178 179 func TestKeyChange(t *testing.T) { 180 server := newServer(t) 181 defer server.Shutdown() 182 conf := clientConfig() 183 hostDB := hostKeyDB() 184 conf.HostKeyCallback = hostDB.Check 185 conf.RekeyThreshold = 1024 186 conn := server.Dial(conf) 187 defer conn.Close() 188 189 for i := 0; i < 4; i++ { 190 session, err := conn.NewSession() 191 if err != nil { 192 t.Fatalf("unable to create new session: %s", err) 193 } 194 195 stdout, err := session.StdoutPipe() 196 if err != nil { 197 t.Fatalf("unable to acquire stdout pipe: %s", err) 198 } 199 200 err = session.Start("dd if=/dev/urandom bs=1024 count=1") 201 if err != nil { 202 t.Fatalf("unable to execute remote command: %s", err) 203 } 204 buf := new(bytes.Buffer) 205 n, err := io.Copy(buf, stdout) 206 if err != nil { 207 t.Fatalf("error reading from remote stdout: %s", err) 208 } 209 210 want := int64(1024) 211 if n != want { 212 t.Fatalf("Expected %d bytes but read only %d from remote command", want, n) 213 } 214 } 215 216 if changes := hostDB.checkCount; changes < 4 { 217 t.Errorf("got %d key changes, want 4", changes) 218 } 219 } 220 221 func TestValidTerminalMode(t *testing.T) { 222 if runtime.GOOS == "aix" { 223 // On AIX, sshd cannot acquire /dev/pts/* if launched as 224 // a non-root user. 225 t.Skipf("skipping on %s", runtime.GOOS) 226 } 227 server := newServer(t) 228 defer server.Shutdown() 229 conn := server.Dial(clientConfig()) 230 defer conn.Close() 231 232 session, err := conn.NewSession() 233 if err != nil { 234 t.Fatalf("session failed: %v", err) 235 } 236 defer session.Close() 237 238 stdout, err := session.StdoutPipe() 239 if err != nil { 240 t.Fatalf("unable to acquire stdout pipe: %s", err) 241 } 242 243 stdin, err := session.StdinPipe() 244 if err != nil { 245 t.Fatalf("unable to acquire stdin pipe: %s", err) 246 } 247 248 tm := ssh.TerminalModes{ssh.ECHO: 0} 249 if err = session.RequestPty("xterm", 80, 40, tm); err != nil { 250 t.Fatalf("req-pty failed: %s", err) 251 } 252 253 err = session.Shell() 254 if err != nil { 255 t.Fatalf("session failed: %s", err) 256 } 257 258 stdin.Write([]byte("stty -a && exit\n")) 259 260 var buf bytes.Buffer 261 if _, err := io.Copy(&buf, stdout); err != nil { 262 t.Fatalf("reading failed: %s", err) 263 } 264 265 if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "-echo ") { 266 t.Fatalf("terminal mode failure: expected -echo in stty output, got %s", sttyOutput) 267 } 268 } 269 270 func TestWindowChange(t *testing.T) { 271 if runtime.GOOS == "aix" { 272 // On AIX, sshd cannot acquire /dev/pts/* if launched as 273 // a non-root user. 274 t.Skipf("skipping on %s", runtime.GOOS) 275 } 276 server := newServer(t) 277 defer server.Shutdown() 278 conn := server.Dial(clientConfig()) 279 defer conn.Close() 280 281 session, err := conn.NewSession() 282 if err != nil { 283 t.Fatalf("session failed: %v", err) 284 } 285 defer session.Close() 286 287 stdout, err := session.StdoutPipe() 288 if err != nil { 289 t.Fatalf("unable to acquire stdout pipe: %s", err) 290 } 291 292 stdin, err := session.StdinPipe() 293 if err != nil { 294 t.Fatalf("unable to acquire stdin pipe: %s", err) 295 } 296 297 tm := ssh.TerminalModes{ssh.ECHO: 0} 298 if err = session.RequestPty("xterm", 80, 40, tm); err != nil { 299 t.Fatalf("req-pty failed: %s", err) 300 } 301 302 if err := session.WindowChange(100, 100); err != nil { 303 t.Fatalf("window-change failed: %s", err) 304 } 305 306 err = session.Shell() 307 if err != nil { 308 t.Fatalf("session failed: %s", err) 309 } 310 311 stdin.Write([]byte("stty size && exit\n")) 312 313 var buf bytes.Buffer 314 if _, err := io.Copy(&buf, stdout); err != nil { 315 t.Fatalf("reading failed: %s", err) 316 } 317 318 if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "100 100") { 319 t.Fatalf("terminal WindowChange failure: expected \"100 100\" stty output, got %s", sttyOutput) 320 } 321 } 322 323 func testOneCipher(t *testing.T, cipher string, cipherOrder []string) { 324 server := newServer(t) 325 defer server.Shutdown() 326 conf := clientConfig() 327 conf.Ciphers = []string{cipher} 328 // Don't fail if sshd doesn't have the cipher. 329 conf.Ciphers = append(conf.Ciphers, cipherOrder...) 330 conn, err := server.TryDial(conf) 331 if err != nil { 332 t.Fatalf("TryDial: %v", err) 333 } 334 defer conn.Close() 335 336 numBytes := 4096 337 338 // Exercise sending data to the server 339 if _, _, err := conn.Conn.SendRequest("drop-me", false, make([]byte, numBytes)); err != nil { 340 t.Fatalf("SendRequest: %v", err) 341 } 342 343 // Exercise receiving data from the server 344 session, err := conn.NewSession() 345 if err != nil { 346 t.Fatalf("NewSession: %v", err) 347 } 348 349 out, err := session.Output(fmt.Sprintf("dd if=/dev/zero bs=%d count=1", numBytes)) 350 if err != nil { 351 t.Fatalf("Output: %v", err) 352 } 353 354 if len(out) != numBytes { 355 t.Fatalf("got %d bytes, want %d bytes", len(out), numBytes) 356 } 357 } 358 359 var deprecatedCiphers = []string{ 360 "aes128-cbc", "3des-cbc", 361 "arcfour128", "arcfour256", 362 } 363 364 func TestCiphers(t *testing.T) { 365 var config ssh.Config 366 config.SetDefaults() 367 cipherOrder := append(config.Ciphers, deprecatedCiphers...) 368 369 for _, ciph := range cipherOrder { 370 t.Run(ciph, func(t *testing.T) { 371 testOneCipher(t, ciph, cipherOrder) 372 }) 373 } 374 } 375 376 func TestMACs(t *testing.T) { 377 var config ssh.Config 378 config.SetDefaults() 379 macOrder := config.MACs 380 381 for _, mac := range macOrder { 382 t.Run(mac, func(t *testing.T) { 383 server := newServer(t) 384 defer server.Shutdown() 385 conf := clientConfig() 386 conf.MACs = []string{mac} 387 // Don't fail if sshd doesn't have the MAC. 388 conf.MACs = append(conf.MACs, macOrder...) 389 if conn, err := server.TryDial(conf); err == nil { 390 conn.Close() 391 } else { 392 t.Fatalf("failed for MAC %q", mac) 393 } 394 }) 395 } 396 } 397 398 func TestKeyExchanges(t *testing.T) { 399 var config ssh.Config 400 config.SetDefaults() 401 kexOrder := config.KeyExchanges 402 // Based on the discussion in #17230, the key exchange algorithms 403 // diffie-hellman-group-exchange-sha1 and diffie-hellman-group-exchange-sha256 404 // are not included in the default list of supported kex so we have to add them 405 // here manually. 406 kexOrder = append(kexOrder, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256") 407 for _, kex := range kexOrder { 408 t.Run(kex, func(t *testing.T) { 409 server := newServer(t) 410 defer server.Shutdown() 411 conf := clientConfig() 412 // Don't fail if sshd doesn't have the kex. 413 conf.KeyExchanges = append([]string{kex}, kexOrder...) 414 conn, err := server.TryDial(conf) 415 if err == nil { 416 conn.Close() 417 } else { 418 t.Errorf("failed for kex %q", kex) 419 } 420 }) 421 } 422 } 423 424 func TestClientAuthAlgorithms(t *testing.T) { 425 for _, key := range []string{ 426 "rsa", 427 "dsa", 428 "ecdsa", 429 "ed25519", 430 } { 431 t.Run(key, func(t *testing.T) { 432 server := newServer(t) 433 conf := clientConfig() 434 conf.SetDefaults() 435 conf.Auth = []ssh.AuthMethod{ 436 ssh.PublicKeys(testSigners[key]), 437 } 438 439 conn, err := server.TryDial(conf) 440 if err == nil { 441 conn.Close() 442 } else { 443 t.Errorf("failed for key %q", key) 444 } 445 446 server.Shutdown() 447 }) 448 } 449 }