client_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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. package agent
  5. import (
  6. "bytes"
  7. "crypto/rand"
  8. "errors"
  9. "io"
  10. "net"
  11. "os"
  12. "os/exec"
  13. "path/filepath"
  14. "runtime"
  15. "strconv"
  16. "strings"
  17. "testing"
  18. "time"
  19. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh"
  20. )
  21. // startOpenSSHAgent executes ssh-agent, and returns an Agent interface to it.
  22. func startOpenSSHAgent(t *testing.T) (client ExtendedAgent, socket string, cleanup func()) {
  23. if testing.Short() {
  24. // ssh-agent is not always available, and the key
  25. // types supported vary by platform.
  26. t.Skip("skipping test due to -short")
  27. }
  28. if runtime.GOOS == "windows" {
  29. t.Skip("skipping on windows, we don't support connecting to the ssh-agent via a named pipe")
  30. }
  31. bin, err := exec.LookPath("ssh-agent")
  32. if err != nil {
  33. t.Skip("could not find ssh-agent")
  34. }
  35. cmd := exec.Command(bin, "-s")
  36. cmd.Env = []string{} // Do not let the user's environment influence ssh-agent behavior.
  37. cmd.Stderr = new(bytes.Buffer)
  38. out, err := cmd.Output()
  39. if err != nil {
  40. t.Fatalf("%s failed: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
  41. }
  42. // Output looks like:
  43. //
  44. // SSH_AUTH_SOCK=/tmp/ssh-P65gpcqArqvH/agent.15541; export SSH_AUTH_SOCK;
  45. // SSH_AGENT_PID=15542; export SSH_AGENT_PID;
  46. // echo Agent pid 15542;
  47. fields := bytes.Split(out, []byte(";"))
  48. line := bytes.SplitN(fields[0], []byte("="), 2)
  49. line[0] = bytes.TrimLeft(line[0], "\n")
  50. if string(line[0]) != "SSH_AUTH_SOCK" {
  51. t.Fatalf("could not find key SSH_AUTH_SOCK in %q", fields[0])
  52. }
  53. socket = string(line[1])
  54. line = bytes.SplitN(fields[2], []byte("="), 2)
  55. line[0] = bytes.TrimLeft(line[0], "\n")
  56. if string(line[0]) != "SSH_AGENT_PID" {
  57. t.Fatalf("could not find key SSH_AGENT_PID in %q", fields[2])
  58. }
  59. pidStr := line[1]
  60. pid, err := strconv.Atoi(string(pidStr))
  61. if err != nil {
  62. t.Fatalf("Atoi(%q): %v", pidStr, err)
  63. }
  64. conn, err := net.Dial("unix", string(socket))
  65. if err != nil {
  66. t.Fatalf("net.Dial: %v", err)
  67. }
  68. ac := NewClient(conn)
  69. return ac, socket, func() {
  70. proc, _ := os.FindProcess(pid)
  71. if proc != nil {
  72. proc.Kill()
  73. }
  74. conn.Close()
  75. os.RemoveAll(filepath.Dir(socket))
  76. }
  77. }
  78. func startAgent(t *testing.T, agent Agent) (client ExtendedAgent, cleanup func()) {
  79. c1, c2, err := netPipe()
  80. if err != nil {
  81. t.Fatalf("netPipe: %v", err)
  82. }
  83. go ServeAgent(agent, c2)
  84. return NewClient(c1), func() {
  85. c1.Close()
  86. c2.Close()
  87. }
  88. }
  89. // startKeyringAgent uses Keyring to simulate a ssh-agent Server and returns a client.
  90. func startKeyringAgent(t *testing.T) (client ExtendedAgent, cleanup func()) {
  91. return startAgent(t, NewKeyring())
  92. }
  93. func testOpenSSHAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
  94. agent, _, cleanup := startOpenSSHAgent(t)
  95. defer cleanup()
  96. testAgentInterface(t, agent, key, cert, lifetimeSecs)
  97. }
  98. func testKeyringAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
  99. agent, cleanup := startKeyringAgent(t)
  100. defer cleanup()
  101. testAgentInterface(t, agent, key, cert, lifetimeSecs)
  102. }
  103. func testAgentInterface(t *testing.T, agent ExtendedAgent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
  104. signer, err := ssh.NewSignerFromKey(key)
  105. if err != nil {
  106. t.Fatalf("NewSignerFromKey(%T): %v", key, err)
  107. }
  108. // The agent should start up empty.
  109. if keys, err := agent.List(); err != nil {
  110. t.Fatalf("RequestIdentities: %v", err)
  111. } else if len(keys) > 0 {
  112. t.Fatalf("got %d keys, want 0: %v", len(keys), keys)
  113. }
  114. // Attempt to insert the key, with certificate if specified.
  115. var pubKey ssh.PublicKey
  116. if cert != nil {
  117. err = agent.Add(AddedKey{
  118. PrivateKey: key,
  119. Certificate: cert,
  120. Comment: "comment",
  121. LifetimeSecs: lifetimeSecs,
  122. })
  123. pubKey = cert
  124. } else {
  125. err = agent.Add(AddedKey{PrivateKey: key, Comment: "comment", LifetimeSecs: lifetimeSecs})
  126. pubKey = signer.PublicKey()
  127. }
  128. if err != nil {
  129. t.Fatalf("insert(%T): %v", key, err)
  130. }
  131. // Did the key get inserted successfully?
  132. if keys, err := agent.List(); err != nil {
  133. t.Fatalf("List: %v", err)
  134. } else if len(keys) != 1 {
  135. t.Fatalf("got %v, want 1 key", keys)
  136. } else if keys[0].Comment != "comment" {
  137. t.Fatalf("key comment: got %v, want %v", keys[0].Comment, "comment")
  138. } else if !bytes.Equal(keys[0].Blob, pubKey.Marshal()) {
  139. t.Fatalf("key mismatch")
  140. }
  141. // Can the agent make a valid signature?
  142. data := []byte("hello")
  143. sig, err := agent.Sign(pubKey, data)
  144. if err != nil {
  145. t.Logf("sign failed with key type %q", pubKey.Type())
  146. // In integration tests ssh-rsa (SHA1 signatures) may be disabled for
  147. // security reasons, we check SHA-2 variants later.
  148. if pubKey.Type() != ssh.KeyAlgoRSA && pubKey.Type() != ssh.CertAlgoRSAv01 {
  149. t.Fatalf("Sign(%s): %v", pubKey.Type(), err)
  150. }
  151. } else {
  152. if err := pubKey.Verify(data, sig); err != nil {
  153. t.Logf("verify failed with key type %q", pubKey.Type())
  154. if pubKey.Type() != ssh.KeyAlgoRSA {
  155. t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
  156. }
  157. }
  158. }
  159. // For tests on RSA keys, try signing with SHA-256 and SHA-512 flags
  160. if pubKey.Type() == ssh.KeyAlgoRSA {
  161. sshFlagTest := func(flag SignatureFlags, expectedSigFormat string) {
  162. sig, err = agent.SignWithFlags(pubKey, data, flag)
  163. if err != nil {
  164. t.Fatalf("SignWithFlags(%s): %v", pubKey.Type(), err)
  165. }
  166. if sig.Format != expectedSigFormat {
  167. t.Fatalf("Signature format didn't match expected value: %s != %s", sig.Format, expectedSigFormat)
  168. }
  169. if err := pubKey.Verify(data, sig); err != nil {
  170. t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
  171. }
  172. }
  173. sshFlagTest(SignatureFlagRsaSha256, ssh.KeyAlgoRSASHA256)
  174. sshFlagTest(SignatureFlagRsaSha512, ssh.KeyAlgoRSASHA512)
  175. }
  176. // If the key has a lifetime, is it removed when it should be?
  177. if lifetimeSecs > 0 {
  178. time.Sleep(time.Second*time.Duration(lifetimeSecs) + 100*time.Millisecond)
  179. keys, err := agent.List()
  180. if err != nil {
  181. t.Fatalf("List: %v", err)
  182. }
  183. if len(keys) > 0 {
  184. t.Fatalf("key not expired")
  185. }
  186. }
  187. }
  188. func TestMalformedRequests(t *testing.T) {
  189. keyringAgent := NewKeyring()
  190. testCase := func(t *testing.T, requestBytes []byte, wantServerErr bool) {
  191. c, s := net.Pipe()
  192. defer c.Close()
  193. defer s.Close()
  194. go func() {
  195. _, err := c.Write(requestBytes)
  196. if err != nil {
  197. t.Errorf("Unexpected error writing raw bytes on connection: %v", err)
  198. }
  199. c.Close()
  200. }()
  201. err := ServeAgent(keyringAgent, s)
  202. if err == nil {
  203. t.Error("ServeAgent should have returned an error to malformed input")
  204. } else {
  205. if (err != io.EOF) != wantServerErr {
  206. t.Errorf("ServeAgent returned expected error: %v", err)
  207. }
  208. }
  209. }
  210. var testCases = []struct {
  211. name string
  212. requestBytes []byte
  213. wantServerErr bool
  214. }{
  215. {"Empty request", []byte{}, false},
  216. {"Short header", []byte{0x00}, true},
  217. {"Empty body", []byte{0x00, 0x00, 0x00, 0x00}, true},
  218. {"Short body", []byte{0x00, 0x00, 0x00, 0x01}, false},
  219. }
  220. for _, tc := range testCases {
  221. t.Run(tc.name, func(t *testing.T) { testCase(t, tc.requestBytes, tc.wantServerErr) })
  222. }
  223. }
  224. func TestAgent(t *testing.T) {
  225. for _, keyType := range []string{"rsa", "ecdsa", "ed25519"} {
  226. testOpenSSHAgent(t, testPrivateKeys[keyType], nil, 0)
  227. testKeyringAgent(t, testPrivateKeys[keyType], nil, 0)
  228. }
  229. }
  230. func TestCert(t *testing.T) {
  231. cert := &ssh.Certificate{
  232. Key: testPublicKeys["rsa"],
  233. ValidBefore: ssh.CertTimeInfinity,
  234. CertType: ssh.UserCert,
  235. }
  236. cert.SignCert(rand.Reader, testSigners["ecdsa"])
  237. testOpenSSHAgent(t, testPrivateKeys["rsa"], cert, 0)
  238. testKeyringAgent(t, testPrivateKeys["rsa"], cert, 0)
  239. }
  240. // netListener creates a localhost network listener.
  241. func netListener() (net.Listener, error) {
  242. listener, err := net.Listen("tcp", "127.0.0.1:0")
  243. if err != nil {
  244. listener, err = net.Listen("tcp", "[::1]:0")
  245. if err != nil {
  246. return nil, err
  247. }
  248. }
  249. return listener, nil
  250. }
  251. // netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
  252. // therefore is buffered (net.Pipe deadlocks if both sides start with
  253. // a write.)
  254. func netPipe() (net.Conn, net.Conn, error) {
  255. listener, err := netListener()
  256. if err != nil {
  257. return nil, nil, err
  258. }
  259. defer listener.Close()
  260. c1, err := net.Dial("tcp", listener.Addr().String())
  261. if err != nil {
  262. return nil, nil, err
  263. }
  264. c2, err := listener.Accept()
  265. if err != nil {
  266. c1.Close()
  267. return nil, nil, err
  268. }
  269. return c1, c2, nil
  270. }
  271. func TestServerResponseTooLarge(t *testing.T) {
  272. a, b, err := netPipe()
  273. if err != nil {
  274. t.Fatalf("netPipe: %v", err)
  275. }
  276. done := make(chan struct{})
  277. defer func() { <-done }()
  278. defer a.Close()
  279. defer b.Close()
  280. var response identitiesAnswerAgentMsg
  281. response.NumKeys = 1
  282. response.Keys = make([]byte, maxAgentResponseBytes+1)
  283. agent := NewClient(a)
  284. go func() {
  285. defer close(done)
  286. n, err := b.Write(ssh.Marshal(response))
  287. if n < 4 {
  288. if runtime.GOOS == "plan9" {
  289. if e1, ok := err.(*net.OpError); ok {
  290. if e2, ok := e1.Err.(*os.PathError); ok {
  291. switch e2.Err.Error() {
  292. case "Hangup", "i/o on hungup channel":
  293. // syscall.Pwrite returns -1 in this case even when some data did get written.
  294. return
  295. }
  296. }
  297. }
  298. }
  299. t.Errorf("At least 4 bytes (the response size) should have been successfully written: %d < 4: %v", n, err)
  300. }
  301. }()
  302. _, err = agent.List()
  303. if err == nil {
  304. t.Fatal("Did not get error result")
  305. }
  306. if err.Error() != "agent: client error: response too large" {
  307. t.Fatal("Did not get expected error result")
  308. }
  309. }
  310. func TestAuth(t *testing.T) {
  311. agent, _, cleanup := startOpenSSHAgent(t)
  312. defer cleanup()
  313. a, b, err := netPipe()
  314. if err != nil {
  315. t.Fatalf("netPipe: %v", err)
  316. }
  317. defer a.Close()
  318. defer b.Close()
  319. if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
  320. t.Errorf("Add: %v", err)
  321. }
  322. serverConf := ssh.ServerConfig{}
  323. serverConf.AddHostKey(testSigners["rsa"])
  324. serverConf.PublicKeyCallback = func(c ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
  325. if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
  326. return nil, nil
  327. }
  328. return nil, errors.New("pubkey rejected")
  329. }
  330. go func() {
  331. conn, _, _, err := ssh.NewServerConn(a, &serverConf)
  332. if err != nil {
  333. t.Errorf("NewServerConn error: %v", err)
  334. return
  335. }
  336. conn.Close()
  337. }()
  338. conf := ssh.ClientConfig{
  339. HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  340. }
  341. conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers))
  342. conn, _, _, err := ssh.NewClientConn(b, "", &conf)
  343. if err != nil {
  344. t.Fatalf("NewClientConn: %v", err)
  345. }
  346. conn.Close()
  347. }
  348. func TestLockOpenSSHAgent(t *testing.T) {
  349. agent, _, cleanup := startOpenSSHAgent(t)
  350. defer cleanup()
  351. testLockAgent(agent, t)
  352. }
  353. func TestLockKeyringAgent(t *testing.T) {
  354. agent, cleanup := startKeyringAgent(t)
  355. defer cleanup()
  356. testLockAgent(agent, t)
  357. }
  358. func testLockAgent(agent Agent, t *testing.T) {
  359. if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment 1"}); err != nil {
  360. t.Errorf("Add: %v", err)
  361. }
  362. if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["ecdsa"], Comment: "comment ecdsa"}); err != nil {
  363. t.Errorf("Add: %v", err)
  364. }
  365. if keys, err := agent.List(); err != nil {
  366. t.Errorf("List: %v", err)
  367. } else if len(keys) != 2 {
  368. t.Errorf("Want 2 keys, got %v", keys)
  369. }
  370. passphrase := []byte("secret")
  371. if err := agent.Lock(passphrase); err != nil {
  372. t.Errorf("Lock: %v", err)
  373. }
  374. if keys, err := agent.List(); err != nil {
  375. t.Errorf("List: %v", err)
  376. } else if len(keys) != 0 {
  377. t.Errorf("Want 0 keys, got %v", keys)
  378. }
  379. signer, _ := ssh.NewSignerFromKey(testPrivateKeys["rsa"])
  380. if _, err := agent.Sign(signer.PublicKey(), []byte("hello")); err == nil {
  381. t.Fatalf("Sign did not fail")
  382. }
  383. if err := agent.Remove(signer.PublicKey()); err == nil {
  384. t.Fatalf("Remove did not fail")
  385. }
  386. if err := agent.RemoveAll(); err == nil {
  387. t.Fatalf("RemoveAll did not fail")
  388. }
  389. if err := agent.Unlock(nil); err == nil {
  390. t.Errorf("Unlock with wrong passphrase succeeded")
  391. }
  392. if err := agent.Unlock(passphrase); err != nil {
  393. t.Errorf("Unlock: %v", err)
  394. }
  395. if err := agent.Remove(signer.PublicKey()); err != nil {
  396. t.Fatalf("Remove: %v", err)
  397. }
  398. if keys, err := agent.List(); err != nil {
  399. t.Errorf("List: %v", err)
  400. } else if len(keys) != 1 {
  401. t.Errorf("Want 1 keys, got %v", keys)
  402. }
  403. }
  404. func testOpenSSHAgentLifetime(t *testing.T) {
  405. agent, _, cleanup := startOpenSSHAgent(t)
  406. defer cleanup()
  407. testAgentLifetime(t, agent)
  408. }
  409. func testKeyringAgentLifetime(t *testing.T) {
  410. agent, cleanup := startKeyringAgent(t)
  411. defer cleanup()
  412. testAgentLifetime(t, agent)
  413. }
  414. func testAgentLifetime(t *testing.T, agent Agent) {
  415. for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
  416. // Add private keys to the agent.
  417. err := agent.Add(AddedKey{
  418. PrivateKey: testPrivateKeys[keyType],
  419. Comment: "comment",
  420. LifetimeSecs: 1,
  421. })
  422. if err != nil {
  423. t.Fatalf("add: %v", err)
  424. }
  425. // Add certs to the agent.
  426. cert := &ssh.Certificate{
  427. Key: testPublicKeys[keyType],
  428. ValidBefore: ssh.CertTimeInfinity,
  429. CertType: ssh.UserCert,
  430. }
  431. cert.SignCert(rand.Reader, testSigners[keyType])
  432. err = agent.Add(AddedKey{
  433. PrivateKey: testPrivateKeys[keyType],
  434. Certificate: cert,
  435. Comment: "comment",
  436. LifetimeSecs: 1,
  437. })
  438. if err != nil {
  439. t.Fatalf("add: %v", err)
  440. }
  441. }
  442. time.Sleep(1100 * time.Millisecond)
  443. if keys, err := agent.List(); err != nil {
  444. t.Errorf("List: %v", err)
  445. } else if len(keys) != 0 {
  446. t.Errorf("Want 0 keys, got %v", len(keys))
  447. }
  448. }
  449. type keyringExtended struct {
  450. *keyring
  451. }
  452. func (r *keyringExtended) Extension(extensionType string, contents []byte) ([]byte, error) {
  453. if extensionType != "my-extension@example.com" {
  454. return []byte{agentExtensionFailure}, nil
  455. }
  456. return append([]byte{agentSuccess}, contents...), nil
  457. }
  458. func TestAgentExtensions(t *testing.T) {
  459. agent, _, cleanup := startOpenSSHAgent(t)
  460. defer cleanup()
  461. _, err := agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
  462. if err == nil {
  463. t.Fatal("should have gotten agent extension failure")
  464. }
  465. agent, cleanup = startAgent(t, &keyringExtended{})
  466. defer cleanup()
  467. result, err := agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
  468. if err != nil {
  469. t.Fatalf("agent extension failure: %v", err)
  470. }
  471. if len(result) != 4 || !bytes.Equal(result, []byte{agentSuccess, 0x00, 0x01, 0x02}) {
  472. t.Fatalf("agent extension result invalid: %v", result)
  473. }
  474. _, err = agent.Extension("bad-extension@example.com", []byte{0x00, 0x01, 0x02})
  475. if err == nil {
  476. t.Fatal("should have gotten agent extension failure")
  477. }
  478. }