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