test_unix_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. serializedHostKey []byte
  54. )
  55. func init() {
  56. template.Must(configTmpl.Parse(sshd_config))
  57. block, _ := pem.Decode([]byte(testClientPrivateKey))
  58. rsakey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
  59. block, _ = pem.Decode([]byte(keys["ssh_host_rsa_key"]))
  60. if block == nil {
  61. panic("pem.Decode ssh_host_rsa_key")
  62. }
  63. priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  64. if err != nil {
  65. panic("ParsePKCS1PrivateKey: " + err.Error())
  66. }
  67. serializedHostKey = ssh.MarshalPublicKey(&priv.PublicKey)
  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. }
  76. func username() string {
  77. var username string
  78. if user, err := user.Current(); err == nil {
  79. username = user.Username
  80. } else {
  81. // user.Current() currently requires cgo. If an error is
  82. // returned attempt to get the username from the environment.
  83. log.Printf("user.Current: %v; falling back on $USER", err)
  84. username = os.Getenv("USER")
  85. }
  86. if username == "" {
  87. panic("Unable to get username")
  88. }
  89. return username
  90. }
  91. type storedHostKey struct {
  92. // keys map from an algorithm string to binary key data.
  93. keys map[string][]byte
  94. }
  95. func (k *storedHostKey) Add(algo string, public []byte) {
  96. if k.keys == nil {
  97. k.keys = map[string][]byte{}
  98. }
  99. k.keys[algo] = append([]byte(nil), public...)
  100. }
  101. func (k *storedHostKey) Check(addr string, remote net.Addr, algo string, key []byte) error {
  102. if k.keys == nil || bytes.Compare(key, k.keys[algo]) != 0 {
  103. return errors.New("host key mismatch")
  104. }
  105. return nil
  106. }
  107. func clientConfig() *ssh.ClientConfig {
  108. keyChecker := storedHostKey{}
  109. keyChecker.Add("ssh-rsa", serializedHostKey)
  110. kc := new(keychain)
  111. kc.keys = append(kc.keys, rsakey)
  112. config := &ssh.ClientConfig{
  113. User: username(),
  114. Auth: []ssh.ClientAuth{
  115. ssh.ClientAuthKeyring(kc),
  116. },
  117. HostKeyChecker: &keyChecker,
  118. }
  119. return config
  120. }
  121. func (s *server) TryDial(config *ssh.ClientConfig) (*ssh.ClientConn, error) {
  122. sshd, err := exec.LookPath("sshd")
  123. if err != nil {
  124. s.t.Skipf("skipping test: %v", err)
  125. }
  126. s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e")
  127. r1, w1, err := os.Pipe()
  128. if err != nil {
  129. s.t.Fatal(err)
  130. }
  131. s.cmd.Stdout = w1
  132. r2, w2, err := os.Pipe()
  133. if err != nil {
  134. s.t.Fatal(err)
  135. }
  136. s.cmd.Stdin = r2
  137. s.cmd.Stderr = os.Stderr
  138. if err := s.cmd.Start(); err != nil {
  139. s.t.Fail()
  140. s.Shutdown()
  141. s.t.Fatalf("s.cmd.Start: %v", err)
  142. }
  143. return ssh.Client(&client{wc: w2, r: r1}, config)
  144. }
  145. func (s *server) Dial(config *ssh.ClientConfig) *ssh.ClientConn {
  146. conn, err := s.TryDial(config)
  147. if err != nil {
  148. s.t.Fail()
  149. s.Shutdown()
  150. s.t.Fatalf("ssh.Client: %v", err)
  151. }
  152. return conn
  153. }
  154. func (s *server) Shutdown() {
  155. if s.cmd != nil && s.cmd.Process != nil {
  156. // don't check for Kill error; if it fails it's most likely
  157. // "os: process already finished", and we don't care about that.
  158. s.cmd.Process.Kill()
  159. s.cmd.Wait()
  160. }
  161. if s.t.Failed() {
  162. // log any output from sshd process
  163. s.t.Logf("sshd: %q", s.output.String())
  164. }
  165. s.cleanup()
  166. }
  167. // client wraps a pair of Reader/WriteClosers to implement the
  168. // net.Conn interface. Importantly, client also mocks the
  169. // ability of net.Conn to support concurrent calls to Read/Write
  170. // and Close. See golang.org/issue/5138 for more details.
  171. type client struct {
  172. wc io.WriteCloser
  173. r io.Reader
  174. sync.Mutex // protects refcount and closing
  175. refcount int
  176. closing bool
  177. }
  178. var errClosing = errors.New("use of closed network connection")
  179. func (c *client) LocalAddr() net.Addr { return nil }
  180. func (c *client) RemoteAddr() net.Addr { return nil }
  181. func (c *client) SetDeadline(time.Time) error { return nil }
  182. func (c *client) SetReadDeadline(time.Time) error { return nil }
  183. func (c *client) SetWriteDeadline(time.Time) error { return nil }
  184. // incref, decref are copied from the net package (see net/fd_unix.go) to
  185. // implement the concurrent Close contract that net.Conn implementations
  186. // from that that package provide.
  187. func (c *client) incRef(closing bool) error {
  188. c.Lock()
  189. defer c.Unlock()
  190. if c.closing {
  191. return errClosing
  192. }
  193. c.refcount++
  194. if closing {
  195. c.closing = true
  196. }
  197. return nil
  198. }
  199. func (c *client) decRef() {
  200. c.Lock()
  201. defer c.Unlock()
  202. c.refcount--
  203. if c.closing && c.refcount == 0 {
  204. c.wc.Close()
  205. }
  206. }
  207. func (c *client) Close() error {
  208. if err := c.incRef(true); err != nil {
  209. return err
  210. }
  211. c.decRef()
  212. return nil
  213. }
  214. func (c *client) Read(b []byte) (int, error) {
  215. if err := c.incRef(false); err != nil {
  216. return 0, err
  217. }
  218. defer c.decRef()
  219. return c.r.Read(b)
  220. }
  221. func (c *client) Write(b []byte) (int, error) {
  222. if err := c.incRef(false); err != nil {
  223. return 0, err
  224. }
  225. defer c.decRef()
  226. return c.wc.Write(b)
  227. }
  228. // newServer returns a new mock ssh server.
  229. func newServer(t *testing.T) *server {
  230. dir, err := ioutil.TempDir("", "sshtest")
  231. if err != nil {
  232. t.Fatal(err)
  233. }
  234. f, err := os.Create(filepath.Join(dir, "sshd_config"))
  235. if err != nil {
  236. t.Fatal(err)
  237. }
  238. err = configTmpl.Execute(f, map[string]string{
  239. "Dir": dir,
  240. })
  241. if err != nil {
  242. t.Fatal(err)
  243. }
  244. f.Close()
  245. for k, v := range keys {
  246. f, err := os.OpenFile(filepath.Join(dir, k), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
  247. if err != nil {
  248. t.Fatal(err)
  249. }
  250. if _, err := f.Write([]byte(v)); err != nil {
  251. t.Fatal(err)
  252. }
  253. f.Close()
  254. }
  255. return &server{
  256. t: t,
  257. configfile: f.Name(),
  258. cleanup: func() {
  259. if err := os.RemoveAll(dir); err != nil {
  260. t.Error(err)
  261. }
  262. },
  263. }
  264. }
  265. // keychain implements the ClientKeyring interface
  266. type keychain struct {
  267. keys []interface{}
  268. }
  269. func (k *keychain) Key(i int) (interface{}, error) {
  270. if i < 0 || i >= len(k.keys) {
  271. return nil, nil
  272. }
  273. switch key := k.keys[i].(type) {
  274. case *rsa.PrivateKey:
  275. return &key.PublicKey, nil
  276. case *dsa.PrivateKey:
  277. return &key.PublicKey, nil
  278. }
  279. panic("unknown key type")
  280. }
  281. func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
  282. hashFunc := crypto.SHA1
  283. h := hashFunc.New()
  284. h.Write(data)
  285. digest := h.Sum(nil)
  286. switch key := k.keys[i].(type) {
  287. case *rsa.PrivateKey:
  288. return rsa.SignPKCS1v15(rand, key, hashFunc, digest)
  289. }
  290. return nil, errors.New("ssh: unknown key type")
  291. }
  292. func (k *keychain) loadPEM(file string) error {
  293. buf, err := ioutil.ReadFile(file)
  294. if err != nil {
  295. return err
  296. }
  297. block, _ := pem.Decode(buf)
  298. if block == nil {
  299. return errors.New("ssh: no key found")
  300. }
  301. r, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  302. if err != nil {
  303. return err
  304. }
  305. k.keys = append(k.keys, r)
  306. return nil
  307. }