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  }