test_unix_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. // +build darwin freebsd linux netbsd openbsd plan9
  5. package test
  6. // functional test harness for unix.
  7. import (
  8. "bytes"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "log"
  13. "net"
  14. "os"
  15. "os/exec"
  16. "os/user"
  17. "path/filepath"
  18. "testing"
  19. "text/template"
  20. "code.google.com/p/go.crypto/ssh"
  21. )
  22. const sshd_config = `
  23. Protocol 2
  24. HostKey {{.Dir}}/ssh_host_rsa_key
  25. HostKey {{.Dir}}/ssh_host_dsa_key
  26. HostKey {{.Dir}}/ssh_host_ecdsa_key
  27. Pidfile {{.Dir}}/sshd.pid
  28. #UsePrivilegeSeparation no
  29. KeyRegenerationInterval 3600
  30. ServerKeyBits 768
  31. SyslogFacility AUTH
  32. LogLevel DEBUG2
  33. LoginGraceTime 120
  34. PermitRootLogin no
  35. StrictModes no
  36. RSAAuthentication yes
  37. PubkeyAuthentication yes
  38. AuthorizedKeysFile {{.Dir}}/authorized_keys
  39. IgnoreRhosts yes
  40. RhostsRSAAuthentication no
  41. HostbasedAuthentication no
  42. `
  43. var (
  44. configTmpl template.Template
  45. privateKey ssh.Signer
  46. hostKeyRSA ssh.Signer
  47. hostKeyECDSA ssh.Signer
  48. hostKeyDSA ssh.Signer
  49. )
  50. func init() {
  51. template.Must(configTmpl.Parse(sshd_config))
  52. for n, k := range map[string]*ssh.Signer{
  53. "ssh_host_ecdsa_key": &hostKeyECDSA,
  54. "ssh_host_rsa_key": &hostKeyRSA,
  55. "ssh_host_dsa_key": &hostKeyDSA,
  56. } {
  57. var err error
  58. *k, err = ssh.ParsePrivateKey([]byte(keys[n]))
  59. if err != nil {
  60. panic(fmt.Sprintf("ParsePrivateKey(%q): %v", n, err))
  61. }
  62. }
  63. var err error
  64. privateKey, err = ssh.ParsePrivateKey([]byte(testClientPrivateKey))
  65. if err != nil {
  66. panic(fmt.Sprintf("ParsePrivateKey: %v", err))
  67. }
  68. }
  69. type server struct {
  70. t *testing.T
  71. cleanup func() // executed during Shutdown
  72. configfile string
  73. cmd *exec.Cmd
  74. output bytes.Buffer // holds stderr from sshd process
  75. // Client half of the network connection.
  76. clientConn net.Conn
  77. }
  78. func username() string {
  79. var username string
  80. if user, err := user.Current(); err == nil {
  81. username = user.Username
  82. } else {
  83. // user.Current() currently requires cgo. If an error is
  84. // returned attempt to get the username from the environment.
  85. log.Printf("user.Current: %v; falling back on $USER", err)
  86. username = os.Getenv("USER")
  87. }
  88. if username == "" {
  89. panic("Unable to get username")
  90. }
  91. return username
  92. }
  93. type storedHostKey struct {
  94. // keys map from an algorithm string to binary key data.
  95. keys map[string][]byte
  96. }
  97. func (k *storedHostKey) Add(key ssh.PublicKey) {
  98. if k.keys == nil {
  99. k.keys = map[string][]byte{}
  100. }
  101. k.keys[key.PublicKeyAlgo()] = ssh.MarshalPublicKey(key)
  102. }
  103. func (k *storedHostKey) Check(addr string, remote net.Addr, algo string, key []byte) error {
  104. if k.keys == nil || bytes.Compare(key, k.keys[algo]) != 0 {
  105. return fmt.Errorf("host key mismatch. Got %q, want %q", key, k.keys[algo])
  106. }
  107. return nil
  108. }
  109. func clientConfig() *ssh.ClientConfig {
  110. keyChecker := storedHostKey{}
  111. keyChecker.Add(hostKeyECDSA.PublicKey())
  112. keyChecker.Add(hostKeyRSA.PublicKey())
  113. keyChecker.Add(hostKeyDSA.PublicKey())
  114. kc := new(keychain)
  115. kc.keys = append(kc.keys, privateKey)
  116. config := &ssh.ClientConfig{
  117. User: username(),
  118. Auth: []ssh.ClientAuth{
  119. ssh.ClientAuthKeyring(kc),
  120. },
  121. HostKeyChecker: &keyChecker,
  122. }
  123. return config
  124. }
  125. // unixConnection creates two halves of a connected net.UnixConn. It
  126. // is used for connecting the Go SSH client with sshd without opening
  127. // ports.
  128. func unixConnection() (*net.UnixConn, *net.UnixConn, error) {
  129. dir, err := ioutil.TempDir("", "unixConnection")
  130. if err != nil {
  131. return nil, nil, err
  132. }
  133. defer os.Remove(dir)
  134. addr := filepath.Join(dir, "ssh")
  135. listener, err := net.Listen("unix", addr)
  136. if err != nil {
  137. return nil, nil, err
  138. }
  139. defer listener.Close()
  140. c1, err := net.Dial("unix", addr)
  141. if err != nil {
  142. return nil, nil, err
  143. }
  144. c2, err := listener.Accept()
  145. if err != nil {
  146. c1.Close()
  147. return nil, nil, err
  148. }
  149. return c1.(*net.UnixConn), c2.(*net.UnixConn), nil
  150. }
  151. func (s *server) TryDial(config *ssh.ClientConfig) (*ssh.ClientConn, error) {
  152. sshd, err := exec.LookPath("sshd")
  153. if err != nil {
  154. s.t.Skipf("skipping test: %v", err)
  155. }
  156. c1, c2, err := unixConnection()
  157. if err != nil {
  158. s.t.Fatalf("unixConnection: %v", err)
  159. }
  160. s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e")
  161. f, err := c2.File()
  162. if err != nil {
  163. s.t.Fatalf("UnixConn.File: %v", err)
  164. }
  165. defer f.Close()
  166. s.cmd.Stdin = f
  167. s.cmd.Stdout = f
  168. s.cmd.Stderr = &s.output
  169. if err := s.cmd.Start(); err != nil {
  170. s.t.Fail()
  171. s.Shutdown()
  172. s.t.Fatalf("s.cmd.Start: %v", err)
  173. }
  174. s.clientConn = c1
  175. return ssh.Client(c1, config)
  176. }
  177. func (s *server) Dial(config *ssh.ClientConfig) *ssh.ClientConn {
  178. conn, err := s.TryDial(config)
  179. if err != nil {
  180. s.t.Fail()
  181. s.Shutdown()
  182. s.t.Fatalf("ssh.Client: %v", err)
  183. }
  184. return conn
  185. }
  186. func (s *server) Shutdown() {
  187. if s.cmd != nil && s.cmd.Process != nil {
  188. // Don't check for errors; if it fails it's most
  189. // likely "os: process already finished", and we don't
  190. // care about that. Use os.Interrupt, so child
  191. // processes are killed too.
  192. s.cmd.Process.Signal(os.Interrupt)
  193. s.cmd.Wait()
  194. }
  195. if s.t.Failed() {
  196. // log any output from sshd process
  197. s.t.Logf("sshd: %s", s.output.String())
  198. }
  199. s.cleanup()
  200. }
  201. // newServer returns a new mock ssh server.
  202. func newServer(t *testing.T) *server {
  203. dir, err := ioutil.TempDir("", "sshtest")
  204. if err != nil {
  205. t.Fatal(err)
  206. }
  207. f, err := os.Create(filepath.Join(dir, "sshd_config"))
  208. if err != nil {
  209. t.Fatal(err)
  210. }
  211. err = configTmpl.Execute(f, map[string]string{
  212. "Dir": dir,
  213. })
  214. if err != nil {
  215. t.Fatal(err)
  216. }
  217. f.Close()
  218. for k, v := range keys {
  219. f, err := os.OpenFile(filepath.Join(dir, k), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
  220. if err != nil {
  221. t.Fatal(err)
  222. }
  223. if _, err := f.Write([]byte(v)); err != nil {
  224. t.Fatal(err)
  225. }
  226. f.Close()
  227. }
  228. return &server{
  229. t: t,
  230. configfile: f.Name(),
  231. cleanup: func() {
  232. if err := os.RemoveAll(dir); err != nil {
  233. t.Error(err)
  234. }
  235. },
  236. }
  237. }
  238. // keychain implements the ClientKeyring interface.
  239. type keychain struct {
  240. keys []ssh.Signer
  241. }
  242. func (k *keychain) Key(i int) (ssh.PublicKey, error) {
  243. if i < 0 || i >= len(k.keys) {
  244. return nil, nil
  245. }
  246. return k.keys[i].PublicKey(), nil
  247. }
  248. func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
  249. return k.keys[i].Sign(rand, data)
  250. }
  251. func (k *keychain) loadPEM(file string) error {
  252. buf, err := ioutil.ReadFile(file)
  253. if err != nil {
  254. return err
  255. }
  256. key, err := ssh.ParsePrivateKey(buf)
  257. if err != nil {
  258. return err
  259. }
  260. k.keys = append(k.keys, key)
  261. return nil
  262. }