test_unix_test.go 6.1 KB

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