github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/golang.org/x/crypto/ssh/agent/client_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  package agent
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/rand"
    10  	"errors"
    11  	"net"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strconv"
    16  	"testing"
    17  	"time"
    18  
    19  	"golang.org/x/crypto/ssh"
    20  )
    21  
    22  // startAgent executes ssh-agent, and returns a Agent interface to it.
    23  func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
    24  	if testing.Short() {
    25  		// ssh-agent is not always available, and the key
    26  		// types supported vary by platform.
    27  		t.Skip("skipping test due to -short")
    28  	}
    29  
    30  	bin, err := exec.LookPath("ssh-agent")
    31  	if err != nil {
    32  		t.Skip("could not find ssh-agent")
    33  	}
    34  
    35  	cmd := exec.Command(bin, "-s")
    36  	out, err := cmd.Output()
    37  	if err != nil {
    38  		t.Fatalf("cmd.Output: %v", err)
    39  	}
    40  
    41  	/* Output looks like:
    42  
    43  		   SSH_AUTH_SOCK=/tmp/ssh-P65gpcqArqvH/agent.15541; export SSH_AUTH_SOCK;
    44  	           SSH_AGENT_PID=15542; export SSH_AGENT_PID;
    45  	           echo Agent pid 15542;
    46  	*/
    47  	fields := bytes.Split(out, []byte(";"))
    48  	line := bytes.SplitN(fields[0], []byte("="), 2)
    49  	line[0] = bytes.TrimLeft(line[0], "\n")
    50  	if string(line[0]) != "SSH_AUTH_SOCK" {
    51  		t.Fatalf("could not find key SSH_AUTH_SOCK in %q", fields[0])
    52  	}
    53  	socket = string(line[1])
    54  
    55  	line = bytes.SplitN(fields[2], []byte("="), 2)
    56  	line[0] = bytes.TrimLeft(line[0], "\n")
    57  	if string(line[0]) != "SSH_AGENT_PID" {
    58  		t.Fatalf("could not find key SSH_AGENT_PID in %q", fields[2])
    59  	}
    60  	pidStr := line[1]
    61  	pid, err := strconv.Atoi(string(pidStr))
    62  	if err != nil {
    63  		t.Fatalf("Atoi(%q): %v", pidStr, err)
    64  	}
    65  
    66  	conn, err := net.Dial("unix", string(socket))
    67  	if err != nil {
    68  		t.Fatalf("net.Dial: %v", err)
    69  	}
    70  
    71  	ac := NewClient(conn)
    72  	return ac, socket, func() {
    73  		proc, _ := os.FindProcess(pid)
    74  		if proc != nil {
    75  			proc.Kill()
    76  		}
    77  		conn.Close()
    78  		os.RemoveAll(filepath.Dir(socket))
    79  	}
    80  }
    81  
    82  func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
    83  	agent, _, cleanup := startAgent(t)
    84  	defer cleanup()
    85  
    86  	testAgentInterface(t, agent, key, cert, lifetimeSecs)
    87  }
    88  
    89  func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
    90  	signer, err := ssh.NewSignerFromKey(key)
    91  	if err != nil {
    92  		t.Fatalf("NewSignerFromKey(%T): %v", key, err)
    93  	}
    94  	// The agent should start up empty.
    95  	if keys, err := agent.List(); err != nil {
    96  		t.Fatalf("RequestIdentities: %v", err)
    97  	} else if len(keys) > 0 {
    98  		t.Fatalf("got %d keys, want 0: %v", len(keys), keys)
    99  	}
   100  
   101  	// Attempt to insert the key, with certificate if specified.
   102  	var pubKey ssh.PublicKey
   103  	if cert != nil {
   104  		err = agent.Add(AddedKey{
   105  			PrivateKey:   key,
   106  			Certificate:  cert,
   107  			Comment:      "comment",
   108  			LifetimeSecs: lifetimeSecs,
   109  		})
   110  		pubKey = cert
   111  	} else {
   112  		err = agent.Add(AddedKey{PrivateKey: key, Comment: "comment", LifetimeSecs: lifetimeSecs})
   113  		pubKey = signer.PublicKey()
   114  	}
   115  	if err != nil {
   116  		t.Fatalf("insert(%T): %v", key, err)
   117  	}
   118  
   119  	// Did the key get inserted successfully?
   120  	if keys, err := agent.List(); err != nil {
   121  		t.Fatalf("List: %v", err)
   122  	} else if len(keys) != 1 {
   123  		t.Fatalf("got %v, want 1 key", keys)
   124  	} else if keys[0].Comment != "comment" {
   125  		t.Fatalf("key comment: got %v, want %v", keys[0].Comment, "comment")
   126  	} else if !bytes.Equal(keys[0].Blob, pubKey.Marshal()) {
   127  		t.Fatalf("key mismatch")
   128  	}
   129  
   130  	// Can the agent make a valid signature?
   131  	data := []byte("hello")
   132  	sig, err := agent.Sign(pubKey, data)
   133  	if err != nil {
   134  		t.Fatalf("Sign(%s): %v", pubKey.Type(), err)
   135  	}
   136  
   137  	if err := pubKey.Verify(data, sig); err != nil {
   138  		t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
   139  	}
   140  }
   141  
   142  func TestAgent(t *testing.T) {
   143  	for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
   144  		testAgent(t, testPrivateKeys[keyType], nil, 0)
   145  	}
   146  }
   147  
   148  func TestCert(t *testing.T) {
   149  	cert := &ssh.Certificate{
   150  		Key:         testPublicKeys["rsa"],
   151  		ValidBefore: ssh.CertTimeInfinity,
   152  		CertType:    ssh.UserCert,
   153  	}
   154  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   155  
   156  	testAgent(t, testPrivateKeys["rsa"], cert, 0)
   157  }
   158  
   159  func TestConstraints(t *testing.T) {
   160  	testAgent(t, testPrivateKeys["rsa"], nil, 3600 /* lifetime in seconds */)
   161  }
   162  
   163  // netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
   164  // therefore is buffered (net.Pipe deadlocks if both sides start with
   165  // a write.)
   166  func netPipe() (net.Conn, net.Conn, error) {
   167  	listener, err := net.Listen("tcp", "127.0.0.1:0")
   168  	if err != nil {
   169  		return nil, nil, err
   170  	}
   171  	defer listener.Close()
   172  	c1, err := net.Dial("tcp", listener.Addr().String())
   173  	if err != nil {
   174  		return nil, nil, err
   175  	}
   176  
   177  	c2, err := listener.Accept()
   178  	if err != nil {
   179  		c1.Close()
   180  		return nil, nil, err
   181  	}
   182  
   183  	return c1, c2, nil
   184  }
   185  
   186  func TestAuth(t *testing.T) {
   187  	a, b, err := netPipe()
   188  	if err != nil {
   189  		t.Fatalf("netPipe: %v", err)
   190  	}
   191  
   192  	defer a.Close()
   193  	defer b.Close()
   194  
   195  	agent, _, cleanup := startAgent(t)
   196  	defer cleanup()
   197  
   198  	if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
   199  		t.Errorf("Add: %v", err)
   200  	}
   201  
   202  	serverConf := ssh.ServerConfig{}
   203  	serverConf.AddHostKey(testSigners["rsa"])
   204  	serverConf.PublicKeyCallback = func(c ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
   205  		if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
   206  			return nil, nil
   207  		}
   208  
   209  		return nil, errors.New("pubkey rejected")
   210  	}
   211  
   212  	go func() {
   213  		conn, _, _, err := ssh.NewServerConn(a, &serverConf)
   214  		if err != nil {
   215  			t.Fatalf("Server: %v", err)
   216  		}
   217  		conn.Close()
   218  	}()
   219  
   220  	conf := ssh.ClientConfig{}
   221  	conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers))
   222  	conn, _, _, err := ssh.NewClientConn(b, "", &conf)
   223  	if err != nil {
   224  		t.Fatalf("NewClientConn: %v", err)
   225  	}
   226  	conn.Close()
   227  }
   228  
   229  func TestLockClient(t *testing.T) {
   230  	agent, _, cleanup := startAgent(t)
   231  	defer cleanup()
   232  	testLockAgent(agent, t)
   233  }
   234  
   235  func testLockAgent(agent Agent, t *testing.T) {
   236  	if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment 1"}); err != nil {
   237  		t.Errorf("Add: %v", err)
   238  	}
   239  	if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["dsa"], Comment: "comment dsa"}); err != nil {
   240  		t.Errorf("Add: %v", err)
   241  	}
   242  	if keys, err := agent.List(); err != nil {
   243  		t.Errorf("List: %v", err)
   244  	} else if len(keys) != 2 {
   245  		t.Errorf("Want 2 keys, got %v", keys)
   246  	}
   247  
   248  	passphrase := []byte("secret")
   249  	if err := agent.Lock(passphrase); err != nil {
   250  		t.Errorf("Lock: %v", err)
   251  	}
   252  
   253  	if keys, err := agent.List(); err != nil {
   254  		t.Errorf("List: %v", err)
   255  	} else if len(keys) != 0 {
   256  		t.Errorf("Want 0 keys, got %v", keys)
   257  	}
   258  
   259  	signer, _ := ssh.NewSignerFromKey(testPrivateKeys["rsa"])
   260  	if _, err := agent.Sign(signer.PublicKey(), []byte("hello")); err == nil {
   261  		t.Fatalf("Sign did not fail")
   262  	}
   263  
   264  	if err := agent.Remove(signer.PublicKey()); err == nil {
   265  		t.Fatalf("Remove did not fail")
   266  	}
   267  
   268  	if err := agent.RemoveAll(); err == nil {
   269  		t.Fatalf("RemoveAll did not fail")
   270  	}
   271  
   272  	if err := agent.Unlock(nil); err == nil {
   273  		t.Errorf("Unlock with wrong passphrase succeeded")
   274  	}
   275  	if err := agent.Unlock(passphrase); err != nil {
   276  		t.Errorf("Unlock: %v", err)
   277  	}
   278  
   279  	if err := agent.Remove(signer.PublicKey()); err != nil {
   280  		t.Fatalf("Remove: %v", err)
   281  	}
   282  
   283  	if keys, err := agent.List(); err != nil {
   284  		t.Errorf("List: %v", err)
   285  	} else if len(keys) != 1 {
   286  		t.Errorf("Want 1 keys, got %v", keys)
   287  	}
   288  }
   289  
   290  func TestAgentLifetime(t *testing.T) {
   291  	agent, _, cleanup := startAgent(t)
   292  	defer cleanup()
   293  
   294  	for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
   295  		// Add private keys to the agent.
   296  		err := agent.Add(AddedKey{
   297  			PrivateKey:   testPrivateKeys[keyType],
   298  			Comment:      "comment",
   299  			LifetimeSecs: 1,
   300  		})
   301  		if err != nil {
   302  			t.Fatalf("add: %v", err)
   303  		}
   304  		// Add certs to the agent.
   305  		cert := &ssh.Certificate{
   306  			Key:         testPublicKeys[keyType],
   307  			ValidBefore: ssh.CertTimeInfinity,
   308  			CertType:    ssh.UserCert,
   309  		}
   310  		cert.SignCert(rand.Reader, testSigners[keyType])
   311  		err = agent.Add(AddedKey{
   312  			PrivateKey:   testPrivateKeys[keyType],
   313  			Certificate:  cert,
   314  			Comment:      "comment",
   315  			LifetimeSecs: 1,
   316  		})
   317  		if err != nil {
   318  			t.Fatalf("add: %v", err)
   319  		}
   320  	}
   321  	time.Sleep(1100 * time.Millisecond)
   322  	if keys, err := agent.List(); err != nil {
   323  		t.Errorf("List: %v", err)
   324  	} else if len(keys) != 0 {
   325  		t.Errorf("Want 0 keys, got %v", len(keys))
   326  	}
   327  }