test_unix_test.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. "crypto"
  10. "crypto/dsa"
  11. "crypto/rsa"
  12. "crypto/x509"
  13. "encoding/pem"
  14. "errors"
  15. "io"
  16. "io/ioutil"
  17. "log"
  18. "net"
  19. "os"
  20. "os/exec"
  21. "os/user"
  22. "path/filepath"
  23. "sync"
  24. "testing"
  25. "text/template"
  26. "time"
  27. "code.google.com/p/go.crypto/ssh"
  28. )
  29. const sshd_config = `
  30. Protocol 2
  31. HostKey {{.Dir}}/ssh_host_rsa_key
  32. HostKey {{.Dir}}/ssh_host_dsa_key
  33. HostKey {{.Dir}}/ssh_host_ecdsa_key
  34. Pidfile {{.Dir}}/sshd.pid
  35. #UsePrivilegeSeparation no
  36. KeyRegenerationInterval 3600
  37. ServerKeyBits 768
  38. SyslogFacility AUTH
  39. LogLevel DEBUG2
  40. LoginGraceTime 120
  41. PermitRootLogin no
  42. StrictModes no
  43. RSAAuthentication yes
  44. PubkeyAuthentication yes
  45. AuthorizedKeysFile {{.Dir}}/authorized_keys
  46. IgnoreRhosts yes
  47. RhostsRSAAuthentication no
  48. HostbasedAuthentication no
  49. `
  50. var (
  51. configTmpl template.Template
  52. rsakey *rsa.PrivateKey
  53. )
  54. func init() {
  55. template.Must(configTmpl.Parse(sshd_config))
  56. block, _ := pem.Decode([]byte(testClientPrivateKey))
  57. rsakey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
  58. }
  59. type server struct {
  60. t *testing.T
  61. cleanup func() // executed during Shutdown
  62. configfile string
  63. cmd *exec.Cmd
  64. output bytes.Buffer // holds stderr from sshd process
  65. }
  66. func username() string {
  67. var username string
  68. if user, err := user.Current(); err == nil {
  69. username = user.Username
  70. } else {
  71. // user.Current() currently requires cgo. If an error is
  72. // returned attempt to get the username from the environment.
  73. log.Printf("user.Current: %v; falling back on $USER", err)
  74. username = os.Getenv("USER")
  75. }
  76. if username == "" {
  77. panic("Unable to get username")
  78. }
  79. return username
  80. }
  81. func clientConfig() *ssh.ClientConfig {
  82. kc := new(keychain)
  83. kc.keys = append(kc.keys, rsakey)
  84. config := &ssh.ClientConfig{
  85. User: username(),
  86. Auth: []ssh.ClientAuth{
  87. ssh.ClientAuthKeyring(kc),
  88. },
  89. }
  90. return config
  91. }
  92. func (s *server) Dial(config *ssh.ClientConfig) *ssh.ClientConn {
  93. sshd, err := exec.LookPath("sshd")
  94. if err != nil {
  95. s.t.Skipf("skipping test: %v", err)
  96. }
  97. s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e")
  98. r1, w1, err := os.Pipe()
  99. if err != nil {
  100. s.t.Fatal(err)
  101. }
  102. s.cmd.Stdout = w1
  103. r2, w2, err := os.Pipe()
  104. if err != nil {
  105. s.t.Fatal(err)
  106. }
  107. s.cmd.Stdin = r2
  108. s.cmd.Stderr = os.Stderr
  109. if err := s.cmd.Start(); err != nil {
  110. s.t.Fail()
  111. s.Shutdown()
  112. s.t.Fatalf("s.cmd.Start: %v", err)
  113. }
  114. conn, err := ssh.Client(&client{wc: w2, r: r1}, config)
  115. if err != nil {
  116. s.t.Fail()
  117. s.Shutdown()
  118. s.t.Fatalf("ssh.Client: %v", err)
  119. }
  120. return conn
  121. }
  122. func (s *server) Shutdown() {
  123. if s.cmd != nil && s.cmd.Process != nil {
  124. // don't check for Kill error; if it fails it's most likely
  125. // "os: process already finished", and we don't care about that.
  126. s.cmd.Process.Kill()
  127. s.cmd.Wait()
  128. }
  129. if s.t.Failed() {
  130. // log any output from sshd process
  131. s.t.Logf("sshd: %q", s.output.String())
  132. }
  133. s.cleanup()
  134. }
  135. // client wraps a pair of Reader/WriteClosers to implement the
  136. // net.Conn interface. Importantly, client also mocks the
  137. // ability of net.Conn to support concurrent calls to Read/Write
  138. // and Close. See golang.org/issue/5138 for more details.
  139. type client struct {
  140. wc io.WriteCloser
  141. r io.Reader
  142. sync.Mutex // protects refcount and closing
  143. refcount int
  144. closing bool
  145. }
  146. var errClosing = errors.New("use of closed network connection")
  147. func (c *client) LocalAddr() net.Addr { return nil }
  148. func (c *client) RemoteAddr() net.Addr { return nil }
  149. func (c *client) SetDeadline(time.Time) error { return nil }
  150. func (c *client) SetReadDeadline(time.Time) error { return nil }
  151. func (c *client) SetWriteDeadline(time.Time) error { return nil }
  152. // incref, decref are copied from the net package (see net/fd_unix.go) to
  153. // implement the concurrent Close contract that net.Conn implementations
  154. // from that that package provide.
  155. func (c *client) incRef(closing bool) error {
  156. c.Lock()
  157. defer c.Unlock()
  158. if c.closing {
  159. return errClosing
  160. }
  161. c.refcount++
  162. if closing {
  163. c.closing = true
  164. }
  165. return nil
  166. }
  167. func (c *client) decRef() {
  168. c.Lock()
  169. defer c.Unlock()
  170. c.refcount--
  171. if c.closing && c.refcount == 0 {
  172. c.wc.Close()
  173. }
  174. }
  175. func (c *client) Close() error {
  176. if err := c.incRef(true); err != nil {
  177. return err
  178. }
  179. c.decRef()
  180. return nil
  181. }
  182. func (c *client) Read(b []byte) (int, error) {
  183. if err := c.incRef(false); err != nil {
  184. return 0, err
  185. }
  186. defer c.decRef()
  187. return c.r.Read(b)
  188. }
  189. func (c *client) Write(b []byte) (int, error) {
  190. if err := c.incRef(false); err != nil {
  191. return 0, err
  192. }
  193. defer c.decRef()
  194. return c.wc.Write(b)
  195. }
  196. // newServer returns a new mock ssh server.
  197. func newServer(t *testing.T) *server {
  198. dir, err := ioutil.TempDir("", "sshtest")
  199. if err != nil {
  200. t.Fatal(err)
  201. }
  202. f, err := os.Create(filepath.Join(dir, "sshd_config"))
  203. if err != nil {
  204. t.Fatal(err)
  205. }
  206. err = configTmpl.Execute(f, map[string]string{
  207. "Dir": dir,
  208. })
  209. if err != nil {
  210. t.Fatal(err)
  211. }
  212. f.Close()
  213. for k, v := range keys {
  214. f, err := os.OpenFile(filepath.Join(dir, k), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
  215. if err != nil {
  216. t.Fatal(err)
  217. }
  218. if _, err := f.Write([]byte(v)); err != nil {
  219. t.Fatal(err)
  220. }
  221. f.Close()
  222. }
  223. return &server{
  224. t: t,
  225. configfile: f.Name(),
  226. cleanup: func() {
  227. if err := os.RemoveAll(dir); err != nil {
  228. t.Error(err)
  229. }
  230. },
  231. }
  232. }
  233. // keychain implements the ClientKeyring interface
  234. type keychain struct {
  235. keys []interface{}
  236. }
  237. func (k *keychain) Key(i int) (interface{}, error) {
  238. if i < 0 || i >= len(k.keys) {
  239. return nil, nil
  240. }
  241. switch key := k.keys[i].(type) {
  242. case *rsa.PrivateKey:
  243. return &key.PublicKey, nil
  244. case *dsa.PrivateKey:
  245. return &key.PublicKey, nil
  246. }
  247. panic("unknown key type")
  248. }
  249. func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
  250. hashFunc := crypto.SHA1
  251. h := hashFunc.New()
  252. h.Write(data)
  253. digest := h.Sum(nil)
  254. switch key := k.keys[i].(type) {
  255. case *rsa.PrivateKey:
  256. return rsa.SignPKCS1v15(rand, key, hashFunc, digest)
  257. }
  258. return nil, errors.New("ssh: unknown key type")
  259. }
  260. func (k *keychain) loadPEM(file string) error {
  261. buf, err := ioutil.ReadFile(file)
  262. if err != nil {
  263. return err
  264. }
  265. block, _ := pem.Decode(buf)
  266. if block == nil {
  267. return errors.New("ssh: no key found")
  268. }
  269. r, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  270. if err != nil {
  271. return err
  272. }
  273. k.keys = append(k.keys, r)
  274. return nil
  275. }