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