client_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  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.Fatalf("Sign(%s): %v", pubKey.Type(), err)
  146. }
  147. if err := pubKey.Verify(data, sig); err != nil {
  148. t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
  149. }
  150. // For tests on RSA keys, try signing with SHA-256 and SHA-512 flags
  151. if pubKey.Type() == "ssh-rsa" {
  152. sshFlagTest := func(flag SignatureFlags, expectedSigFormat string) {
  153. sig, err = agent.SignWithFlags(pubKey, data, flag)
  154. if err != nil {
  155. t.Fatalf("SignWithFlags(%s): %v", pubKey.Type(), err)
  156. }
  157. if sig.Format != expectedSigFormat {
  158. t.Fatalf("Signature format didn't match expected value: %s != %s", sig.Format, expectedSigFormat)
  159. }
  160. if err := pubKey.Verify(data, sig); err != nil {
  161. t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
  162. }
  163. }
  164. sshFlagTest(0, ssh.KeyAlgoRSA)
  165. sshFlagTest(SignatureFlagRsaSha256, ssh.KeyAlgoRSASHA256)
  166. sshFlagTest(SignatureFlagRsaSha512, ssh.KeyAlgoRSASHA512)
  167. }
  168. // If the key has a lifetime, is it removed when it should be?
  169. if lifetimeSecs > 0 {
  170. time.Sleep(time.Second*time.Duration(lifetimeSecs) + 100*time.Millisecond)
  171. keys, err := agent.List()
  172. if err != nil {
  173. t.Fatalf("List: %v", err)
  174. }
  175. if len(keys) > 0 {
  176. t.Fatalf("key not expired")
  177. }
  178. }
  179. }
  180. func TestMalformedRequests(t *testing.T) {
  181. keyringAgent := NewKeyring()
  182. testCase := func(t *testing.T, requestBytes []byte, wantServerErr bool) {
  183. c, s := net.Pipe()
  184. defer c.Close()
  185. defer s.Close()
  186. go func() {
  187. _, err := c.Write(requestBytes)
  188. if err != nil {
  189. t.Errorf("Unexpected error writing raw bytes on connection: %v", err)
  190. }
  191. c.Close()
  192. }()
  193. err := ServeAgent(keyringAgent, s)
  194. if err == nil {
  195. t.Error("ServeAgent should have returned an error to malformed input")
  196. } else {
  197. if (err != io.EOF) != wantServerErr {
  198. t.Errorf("ServeAgent returned expected error: %v", err)
  199. }
  200. }
  201. }
  202. var testCases = []struct {
  203. name string
  204. requestBytes []byte
  205. wantServerErr bool
  206. }{
  207. {"Empty request", []byte{}, false},
  208. {"Short header", []byte{0x00}, true},
  209. {"Empty body", []byte{0x00, 0x00, 0x00, 0x00}, true},
  210. {"Short body", []byte{0x00, 0x00, 0x00, 0x01}, false},
  211. }
  212. for _, tc := range testCases {
  213. t.Run(tc.name, func(t *testing.T) { testCase(t, tc.requestBytes, tc.wantServerErr) })
  214. }
  215. }
  216. func TestAgent(t *testing.T) {
  217. for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
  218. testOpenSSHAgent(t, testPrivateKeys[keyType], nil, 0)
  219. testKeyringAgent(t, testPrivateKeys[keyType], nil, 0)
  220. }
  221. }
  222. func TestCert(t *testing.T) {
  223. cert := &ssh.Certificate{
  224. Key: testPublicKeys["rsa"],
  225. ValidBefore: ssh.CertTimeInfinity,
  226. CertType: ssh.UserCert,
  227. }
  228. cert.SignCert(rand.Reader, testSigners["ecdsa"])
  229. testOpenSSHAgent(t, testPrivateKeys["rsa"], cert, 0)
  230. testKeyringAgent(t, testPrivateKeys["rsa"], cert, 0)
  231. }
  232. // netListener creates a localhost network listener.
  233. func netListener() (net.Listener, error) {
  234. listener, err := net.Listen("tcp", "127.0.0.1:0")
  235. if err != nil {
  236. listener, err = net.Listen("tcp", "[::1]:0")
  237. if err != nil {
  238. return nil, err
  239. }
  240. }
  241. return listener, nil
  242. }
  243. // netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
  244. // therefore is buffered (net.Pipe deadlocks if both sides start with
  245. // a write.)
  246. func netPipe() (net.Conn, net.Conn, error) {
  247. listener, err := netListener()
  248. if err != nil {
  249. return nil, nil, err
  250. }
  251. defer listener.Close()
  252. c1, err := net.Dial("tcp", listener.Addr().String())
  253. if err != nil {
  254. return nil, nil, err
  255. }
  256. c2, err := listener.Accept()
  257. if err != nil {
  258. c1.Close()
  259. return nil, nil, err
  260. }
  261. return c1, c2, nil
  262. }
  263. func TestServerResponseTooLarge(t *testing.T) {
  264. a, b, err := netPipe()
  265. if err != nil {
  266. t.Fatalf("netPipe: %v", err)
  267. }
  268. done := make(chan struct{})
  269. defer func() { <-done }()
  270. defer a.Close()
  271. defer b.Close()
  272. var response identitiesAnswerAgentMsg
  273. response.NumKeys = 1
  274. response.Keys = make([]byte, maxAgentResponseBytes+1)
  275. agent := NewClient(a)
  276. go func() {
  277. defer close(done)
  278. n, err := b.Write(ssh.Marshal(response))
  279. if n < 4 {
  280. if runtime.GOOS == "plan9" {
  281. if e1, ok := err.(*net.OpError); ok {
  282. if e2, ok := e1.Err.(*os.PathError); ok {
  283. switch e2.Err.Error() {
  284. case "Hangup", "i/o on hungup channel":
  285. // syscall.Pwrite returns -1 in this case even when some data did get written.
  286. return
  287. }
  288. }
  289. }
  290. }
  291. t.Errorf("At least 4 bytes (the response size) should have been successfully written: %d < 4: %v", n, err)
  292. }
  293. }()
  294. _, err = agent.List()
  295. if err == nil {
  296. t.Fatal("Did not get error result")
  297. }
  298. if err.Error() != "agent: client error: response too large" {
  299. t.Fatal("Did not get expected error result")
  300. }
  301. }
  302. func TestAuth(t *testing.T) {
  303. agent, _, cleanup := startOpenSSHAgent(t)
  304. defer cleanup()
  305. a, b, err := netPipe()
  306. if err != nil {
  307. t.Fatalf("netPipe: %v", err)
  308. }
  309. defer a.Close()
  310. defer b.Close()
  311. if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
  312. t.Errorf("Add: %v", err)
  313. }
  314. serverConf := ssh.ServerConfig{}
  315. serverConf.AddHostKey(testSigners["rsa"])
  316. serverConf.PublicKeyCallback = func(c ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
  317. if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
  318. return nil, nil
  319. }
  320. return nil, errors.New("pubkey rejected")
  321. }
  322. go func() {
  323. conn, _, _, err := ssh.NewServerConn(a, &serverConf)
  324. if err != nil {
  325. t.Errorf("NewServerConn error: %v", err)
  326. return
  327. }
  328. conn.Close()
  329. }()
  330. conf := ssh.ClientConfig{
  331. HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  332. }
  333. conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers))
  334. conn, _, _, err := ssh.NewClientConn(b, "", &conf)
  335. if err != nil {
  336. t.Fatalf("NewClientConn: %v", err)
  337. }
  338. conn.Close()
  339. }
  340. func TestLockOpenSSHAgent(t *testing.T) {
  341. agent, _, cleanup := startOpenSSHAgent(t)
  342. defer cleanup()
  343. testLockAgent(agent, t)
  344. }
  345. func TestLockKeyringAgent(t *testing.T) {
  346. agent, cleanup := startKeyringAgent(t)
  347. defer cleanup()
  348. testLockAgent(agent, t)
  349. }
  350. func testLockAgent(agent Agent, t *testing.T) {
  351. if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment 1"}); err != nil {
  352. t.Errorf("Add: %v", err)
  353. }
  354. if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["dsa"], Comment: "comment dsa"}); err != nil {
  355. t.Errorf("Add: %v", err)
  356. }
  357. if keys, err := agent.List(); err != nil {
  358. t.Errorf("List: %v", err)
  359. } else if len(keys) != 2 {
  360. t.Errorf("Want 2 keys, got %v", keys)
  361. }
  362. passphrase := []byte("secret")
  363. if err := agent.Lock(passphrase); err != nil {
  364. t.Errorf("Lock: %v", err)
  365. }
  366. if keys, err := agent.List(); err != nil {
  367. t.Errorf("List: %v", err)
  368. } else if len(keys) != 0 {
  369. t.Errorf("Want 0 keys, got %v", keys)
  370. }
  371. signer, _ := ssh.NewSignerFromKey(testPrivateKeys["rsa"])
  372. if _, err := agent.Sign(signer.PublicKey(), []byte("hello")); err == nil {
  373. t.Fatalf("Sign did not fail")
  374. }
  375. if err := agent.Remove(signer.PublicKey()); err == nil {
  376. t.Fatalf("Remove did not fail")
  377. }
  378. if err := agent.RemoveAll(); err == nil {
  379. t.Fatalf("RemoveAll did not fail")
  380. }
  381. if err := agent.Unlock(nil); err == nil {
  382. t.Errorf("Unlock with wrong passphrase succeeded")
  383. }
  384. if err := agent.Unlock(passphrase); err != nil {
  385. t.Errorf("Unlock: %v", err)
  386. }
  387. if err := agent.Remove(signer.PublicKey()); err != nil {
  388. t.Fatalf("Remove: %v", err)
  389. }
  390. if keys, err := agent.List(); err != nil {
  391. t.Errorf("List: %v", err)
  392. } else if len(keys) != 1 {
  393. t.Errorf("Want 1 keys, got %v", keys)
  394. }
  395. }
  396. func testOpenSSHAgentLifetime(t *testing.T) {
  397. agent, _, cleanup := startOpenSSHAgent(t)
  398. defer cleanup()
  399. testAgentLifetime(t, agent)
  400. }
  401. func testKeyringAgentLifetime(t *testing.T) {
  402. agent, cleanup := startKeyringAgent(t)
  403. defer cleanup()
  404. testAgentLifetime(t, agent)
  405. }
  406. func testAgentLifetime(t *testing.T, agent Agent) {
  407. for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
  408. // Add private keys to the agent.
  409. err := agent.Add(AddedKey{
  410. PrivateKey: testPrivateKeys[keyType],
  411. Comment: "comment",
  412. LifetimeSecs: 1,
  413. })
  414. if err != nil {
  415. t.Fatalf("add: %v", err)
  416. }
  417. // Add certs to the agent.
  418. cert := &ssh.Certificate{
  419. Key: testPublicKeys[keyType],
  420. ValidBefore: ssh.CertTimeInfinity,
  421. CertType: ssh.UserCert,
  422. }
  423. cert.SignCert(rand.Reader, testSigners[keyType])
  424. err = agent.Add(AddedKey{
  425. PrivateKey: testPrivateKeys[keyType],
  426. Certificate: cert,
  427. Comment: "comment",
  428. LifetimeSecs: 1,
  429. })
  430. if err != nil {
  431. t.Fatalf("add: %v", err)
  432. }
  433. }
  434. time.Sleep(1100 * time.Millisecond)
  435. if keys, err := agent.List(); err != nil {
  436. t.Errorf("List: %v", err)
  437. } else if len(keys) != 0 {
  438. t.Errorf("Want 0 keys, got %v", len(keys))
  439. }
  440. }
  441. type keyringExtended struct {
  442. *keyring
  443. }
  444. func (r *keyringExtended) Extension(extensionType string, contents []byte) ([]byte, error) {
  445. if extensionType != "my-extension@example.com" {
  446. return []byte{agentExtensionFailure}, nil
  447. }
  448. return append([]byte{agentSuccess}, contents...), nil
  449. }
  450. func TestAgentExtensions(t *testing.T) {
  451. agent, _, cleanup := startOpenSSHAgent(t)
  452. defer cleanup()
  453. _, err := agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
  454. if err == nil {
  455. t.Fatal("should have gotten agent extension failure")
  456. }
  457. agent, cleanup = startAgent(t, &keyringExtended{})
  458. defer cleanup()
  459. result, err := agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
  460. if err != nil {
  461. t.Fatalf("agent extension failure: %v", err)
  462. }
  463. if len(result) != 4 || !bytes.Equal(result, []byte{agentSuccess, 0x00, 0x01, 0x02}) {
  464. t.Fatalf("agent extension result invalid: %v", result)
  465. }
  466. _, err = agent.Extension("bad-extension@example.com", []byte{0x00, 0x01, 0x02})
  467. if err == nil {
  468. t.Fatal("should have gotten agent extension failure")
  469. }
  470. }