test_unix_test.go 6.9 KB

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