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.
5 // +build darwin dragonfly freebsd linux netbsd openbsd plan9
9 // functional test harness for unix.
24 "golang.org/x/crypto/ssh"
25 "golang.org/x/crypto/ssh/testdata"
30 HostKey {{.Dir}}/id_rsa
31 HostKey {{.Dir}}/id_dsa
32 HostKey {{.Dir}}/id_ecdsa
33 HostCertificate {{.Dir}}/id_rsa-cert.pub
34 Pidfile {{.Dir}}/sshd.pid
35 #UsePrivilegeSeparation no
36 KeyRegenerationInterval 3600
44 PubkeyAuthentication yes
45 AuthorizedKeysFile {{.Dir}}/authorized_keys
46 TrustedUserCAKeys {{.Dir}}/id_ecdsa.pub
48 RhostsRSAAuthentication no
49 HostbasedAuthentication no
50 PubkeyAcceptedKeyTypes=*
53 var configTmpl = template.Must(template.New("").Parse(sshd_config))
57 cleanup func() // executed during Shutdown
60 output bytes.Buffer // holds stderr from sshd process
62 // Client half of the network connection.
66 func username() string {
68 if user, err := user.Current(); err == nil {
69 username = user.Username
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")
77 panic("Unable to get username")
82 type storedHostKey struct {
83 // keys map from an algorithm string to binary key data.
84 keys map[string][]byte
86 // checkCount counts the Check calls. Used for testing
91 func (k *storedHostKey) Add(key ssh.PublicKey) {
93 k.keys = map[string][]byte{}
95 k.keys[key.Type()] = key.Marshal()
98 func (k *storedHostKey) Check(addr string, remote net.Addr, key ssh.PublicKey) error {
102 if k.keys == nil || bytes.Compare(key.Marshal(), k.keys[algo]) != 0 {
103 return fmt.Errorf("host key mismatch. Got %q, want %q", key, k.keys[algo])
108 func hostKeyDB() *storedHostKey {
109 keyChecker := &storedHostKey{}
110 keyChecker.Add(testPublicKeys["ecdsa"])
111 keyChecker.Add(testPublicKeys["rsa"])
112 keyChecker.Add(testPublicKeys["dsa"])
116 func clientConfig() *ssh.ClientConfig {
117 config := &ssh.ClientConfig{
119 Auth: []ssh.AuthMethod{
120 ssh.PublicKeys(testSigners["user"]),
122 HostKeyCallback: hostKeyDB().Check,
123 HostKeyAlgorithms: []string{ // by default, don't allow certs as this affects the hostKeyDB checker
124 ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
125 ssh.KeyAlgoRSA, ssh.KeyAlgoDSA,
132 // unixConnection creates two halves of a connected net.UnixConn. It
133 // is used for connecting the Go SSH client with sshd without opening
135 func unixConnection() (*net.UnixConn, *net.UnixConn, error) {
136 dir, err := ioutil.TempDir("", "unixConnection")
142 addr := filepath.Join(dir, "ssh")
143 listener, err := net.Listen("unix", addr)
147 defer listener.Close()
148 c1, err := net.Dial("unix", addr)
153 c2, err := listener.Accept()
159 return c1.(*net.UnixConn), c2.(*net.UnixConn), nil
162 func (s *server) TryDial(config *ssh.ClientConfig) (*ssh.Client, error) {
163 return s.TryDialWithAddr(config, "")
166 // addr is the user specified host:port. While we don't actually dial it,
167 // we need to know this for host key matching
168 func (s *server) TryDialWithAddr(config *ssh.ClientConfig, addr string) (*ssh.Client, error) {
169 sshd, err := exec.LookPath("sshd")
171 s.t.Skipf("skipping test: %v", err)
174 c1, c2, err := unixConnection()
176 s.t.Fatalf("unixConnection: %v", err)
179 s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e")
182 s.t.Fatalf("UnixConn.File: %v", err)
187 s.cmd.Stderr = &s.output
188 if err := s.cmd.Start(); err != nil {
191 s.t.Fatalf("s.cmd.Start: %v", err)
194 conn, chans, reqs, err := ssh.NewClientConn(c1, addr, config)
198 return ssh.NewClient(conn, chans, reqs), nil
201 func (s *server) Dial(config *ssh.ClientConfig) *ssh.Client {
202 conn, err := s.TryDial(config)
206 s.t.Fatalf("ssh.Client: %v", err)
211 func (s *server) Shutdown() {
212 if s.cmd != nil && s.cmd.Process != nil {
213 // Don't check for errors; if it fails it's most
214 // likely "os: process already finished", and we don't
215 // care about that. Use os.Interrupt, so child
216 // processes are killed too.
217 s.cmd.Process.Signal(os.Interrupt)
221 // log any output from sshd process
222 s.t.Logf("sshd: %s", s.output.String())
227 func writeFile(path string, contents []byte) {
228 f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
233 if _, err := f.Write(contents); err != nil {
238 // newServer returns a new mock ssh server.
239 func newServer(t *testing.T) *server {
241 t.Skip("skipping test due to -short")
243 dir, err := ioutil.TempDir("", "sshtest")
247 f, err := os.Create(filepath.Join(dir, "sshd_config"))
251 err = configTmpl.Execute(f, map[string]string{
259 for k, v := range testdata.PEMBytes {
260 filename := "id_" + k
261 writeFile(filepath.Join(dir, filename), v)
262 writeFile(filepath.Join(dir, filename+".pub"), ssh.MarshalAuthorizedKey(testPublicKeys[k]))
265 for k, v := range testdata.SSHCertificates {
266 filename := "id_" + k + "-cert.pub"
267 writeFile(filepath.Join(dir, filename), v)
270 var authkeys bytes.Buffer
271 for k, _ := range testdata.PEMBytes {
272 authkeys.Write(ssh.MarshalAuthorizedKey(testPublicKeys[k]))
274 writeFile(filepath.Join(dir, "authorized_keys"), authkeys.Bytes())
278 configfile: f.Name(),
280 if err := os.RemoveAll(dir); err != nil {
287 func newTempSocket(t *testing.T) (string, func()) {
288 dir, err := ioutil.TempDir("", "socket")
292 deferFunc := func() { os.RemoveAll(dir) }
293 addr := filepath.Join(dir, "sock")
294 return addr, deferFunc