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  }