test_unix_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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 dragonfly freebsd linux netbsd openbsd plan9
  5. package test
  6. // functional test harness for unix.
  7. import (
  8. "bytes"
  9. "fmt"
  10. "io/ioutil"
  11. "log"
  12. "net"
  13. "os"
  14. "os/exec"
  15. "os/user"
  16. "path/filepath"
  17. "testing"
  18. "text/template"
  19. "golang.org/x/crypto/ssh"
  20. "golang.org/x/crypto/ssh/testdata"
  21. )
  22. const sshd_config = `
  23. Protocol 2
  24. HostKey {{.Dir}}/id_rsa
  25. HostKey {{.Dir}}/id_dsa
  26. HostKey {{.Dir}}/id_ecdsa
  27. HostCertificate {{.Dir}}/id_rsa-cert.pub
  28. Pidfile {{.Dir}}/sshd.pid
  29. #UsePrivilegeSeparation no
  30. KeyRegenerationInterval 3600
  31. ServerKeyBits 768
  32. SyslogFacility AUTH
  33. LogLevel DEBUG2
  34. LoginGraceTime 120
  35. PermitRootLogin no
  36. StrictModes no
  37. RSAAuthentication yes
  38. PubkeyAuthentication yes
  39. AuthorizedKeysFile {{.Dir}}/authorized_keys
  40. TrustedUserCAKeys {{.Dir}}/id_ecdsa.pub
  41. IgnoreRhosts yes
  42. RhostsRSAAuthentication no
  43. HostbasedAuthentication no
  44. PubkeyAcceptedKeyTypes=*
  45. `
  46. var configTmpl = template.Must(template.New("").Parse(sshd_config))
  47. type server struct {
  48. t *testing.T
  49. cleanup func() // executed during Shutdown
  50. configfile string
  51. cmd *exec.Cmd
  52. output bytes.Buffer // holds stderr from sshd process
  53. // Client half of the network connection.
  54. clientConn net.Conn
  55. }
  56. func username() string {
  57. var username string
  58. if user, err := user.Current(); err == nil {
  59. username = user.Username
  60. } else {
  61. // user.Current() currently requires cgo. If an error is
  62. // returned attempt to get the username from the environment.
  63. log.Printf("user.Current: %v; falling back on $USER", err)
  64. username = os.Getenv("USER")
  65. }
  66. if username == "" {
  67. panic("Unable to get username")
  68. }
  69. return username
  70. }
  71. type storedHostKey struct {
  72. // keys map from an algorithm string to binary key data.
  73. keys map[string][]byte
  74. // checkCount counts the Check calls. Used for testing
  75. // rekeying.
  76. checkCount int
  77. }
  78. func (k *storedHostKey) Add(key ssh.PublicKey) {
  79. if k.keys == nil {
  80. k.keys = map[string][]byte{}
  81. }
  82. k.keys[key.Type()] = key.Marshal()
  83. }
  84. func (k *storedHostKey) Check(addr string, remote net.Addr, key ssh.PublicKey) error {
  85. k.checkCount++
  86. algo := key.Type()
  87. if k.keys == nil || bytes.Compare(key.Marshal(), k.keys[algo]) != 0 {
  88. return fmt.Errorf("host key mismatch. Got %q, want %q", key, k.keys[algo])
  89. }
  90. return nil
  91. }
  92. func hostKeyDB() *storedHostKey {
  93. keyChecker := &storedHostKey{}
  94. keyChecker.Add(testPublicKeys["ecdsa"])
  95. keyChecker.Add(testPublicKeys["rsa"])
  96. keyChecker.Add(testPublicKeys["dsa"])
  97. return keyChecker
  98. }
  99. func clientConfig() *ssh.ClientConfig {
  100. config := &ssh.ClientConfig{
  101. User: username(),
  102. Auth: []ssh.AuthMethod{
  103. ssh.PublicKeys(testSigners["user"]),
  104. },
  105. HostKeyCallback: hostKeyDB().Check,
  106. HostKeyAlgorithms: []string{ // by default, don't allow certs as this affects the hostKeyDB checker
  107. ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
  108. ssh.KeyAlgoRSA, ssh.KeyAlgoDSA,
  109. ssh.KeyAlgoED25519,
  110. },
  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.Client, error) {
  141. return s.TryDialWithAddr(config, "")
  142. }
  143. // addr is the user specified host:port. While we don't actually dial it,
  144. // we need to know this for host key matching
  145. func (s *server) TryDialWithAddr(config *ssh.ClientConfig, addr string) (*ssh.Client, error) {
  146. sshd, err := exec.LookPath("sshd")
  147. if err != nil {
  148. s.t.Skipf("skipping test: %v", err)
  149. }
  150. c1, c2, err := unixConnection()
  151. if err != nil {
  152. s.t.Fatalf("unixConnection: %v", err)
  153. }
  154. s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e")
  155. f, err := c2.File()
  156. if err != nil {
  157. s.t.Fatalf("UnixConn.File: %v", err)
  158. }
  159. defer f.Close()
  160. s.cmd.Stdin = f
  161. s.cmd.Stdout = f
  162. s.cmd.Stderr = &s.output
  163. if err := s.cmd.Start(); err != nil {
  164. s.t.Fail()
  165. s.Shutdown()
  166. s.t.Fatalf("s.cmd.Start: %v", err)
  167. }
  168. s.clientConn = c1
  169. conn, chans, reqs, err := ssh.NewClientConn(c1, addr, config)
  170. if err != nil {
  171. return nil, err
  172. }
  173. return ssh.NewClient(conn, chans, reqs), nil
  174. }
  175. func (s *server) Dial(config *ssh.ClientConfig) *ssh.Client {
  176. conn, err := s.TryDial(config)
  177. if err != nil {
  178. s.t.Fail()
  179. s.Shutdown()
  180. s.t.Fatalf("ssh.Client: %v", err)
  181. }
  182. return conn
  183. }
  184. func (s *server) Shutdown() {
  185. if s.cmd != nil && s.cmd.Process != nil {
  186. // Don't check for errors; if it fails it's most
  187. // likely "os: process already finished", and we don't
  188. // care about that. Use os.Interrupt, so child
  189. // processes are killed too.
  190. s.cmd.Process.Signal(os.Interrupt)
  191. s.cmd.Wait()
  192. }
  193. if s.t.Failed() {
  194. // log any output from sshd process
  195. s.t.Logf("sshd: %s", s.output.String())
  196. }
  197. s.cleanup()
  198. }
  199. func writeFile(path string, contents []byte) {
  200. f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
  201. if err != nil {
  202. panic(err)
  203. }
  204. defer f.Close()
  205. if _, err := f.Write(contents); err != nil {
  206. panic(err)
  207. }
  208. }
  209. // newServer returns a new mock ssh server.
  210. func newServer(t *testing.T) *server {
  211. if testing.Short() {
  212. t.Skip("skipping test due to -short")
  213. }
  214. dir, err := ioutil.TempDir("", "sshtest")
  215. if err != nil {
  216. t.Fatal(err)
  217. }
  218. f, err := os.Create(filepath.Join(dir, "sshd_config"))
  219. if err != nil {
  220. t.Fatal(err)
  221. }
  222. err = configTmpl.Execute(f, map[string]string{
  223. "Dir": dir,
  224. })
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. f.Close()
  229. for k, v := range testdata.PEMBytes {
  230. filename := "id_" + k
  231. writeFile(filepath.Join(dir, filename), v)
  232. writeFile(filepath.Join(dir, filename+".pub"), ssh.MarshalAuthorizedKey(testPublicKeys[k]))
  233. }
  234. for k, v := range testdata.SSHCertificates {
  235. filename := "id_" + k + "-cert.pub"
  236. writeFile(filepath.Join(dir, filename), v)
  237. }
  238. var authkeys bytes.Buffer
  239. for k, _ := range testdata.PEMBytes {
  240. authkeys.Write(ssh.MarshalAuthorizedKey(testPublicKeys[k]))
  241. }
  242. writeFile(filepath.Join(dir, "authorized_keys"), authkeys.Bytes())
  243. return &server{
  244. t: t,
  245. configfile: f.Name(),
  246. cleanup: func() {
  247. if err := os.RemoveAll(dir); err != nil {
  248. t.Error(err)
  249. }
  250. },
  251. }
  252. }
  253. func newTempSocket(t *testing.T) (string, func()) {
  254. dir, err := ioutil.TempDir("", "socket")
  255. if err != nil {
  256. t.Fatal(err)
  257. }
  258. deferFunc := func() { os.RemoveAll(dir) }
  259. addr := filepath.Join(dir, "sock")
  260. return addr, deferFunc
  261. }