| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package agent |
| |
| import ( |
| "crypto" |
| "crypto/rand" |
| "fmt" |
| pseudorand "math/rand" |
| "reflect" |
| "strings" |
| "testing" |
| |
| "golang.org/x/crypto/ssh" |
| ) |
| |
| func TestServer(t *testing.T) { |
| c1, c2, err := netPipe() |
| if err != nil { |
| t.Fatalf("netPipe: %v", err) |
| } |
| defer c1.Close() |
| defer c2.Close() |
| client := NewClient(c1) |
| |
| go ServeAgent(NewKeyring(), c2) |
| |
| testAgentInterface(t, client, testPrivateKeys["rsa"], nil, 0) |
| } |
| |
| func TestLockServer(t *testing.T) { |
| testLockAgent(NewKeyring(), t) |
| } |
| |
| func TestSetupForwardAgent(t *testing.T) { |
| a, b, err := netPipe() |
| if err != nil { |
| t.Fatalf("netPipe: %v", err) |
| } |
| |
| defer a.Close() |
| defer b.Close() |
| |
| _, socket, cleanup := startOpenSSHAgent(t) |
| defer cleanup() |
| |
| serverConf := ssh.ServerConfig{ |
| NoClientAuth: true, |
| } |
| serverConf.AddHostKey(testSigners["rsa"]) |
| incoming := make(chan *ssh.ServerConn, 1) |
| go func() { |
| conn, _, _, err := ssh.NewServerConn(a, &serverConf) |
| if err != nil { |
| t.Fatalf("Server: %v", err) |
| } |
| incoming <- conn |
| }() |
| |
| conf := ssh.ClientConfig{ |
| HostKeyCallback: ssh.InsecureIgnoreHostKey(), |
| } |
| conn, chans, reqs, err := ssh.NewClientConn(b, "", &conf) |
| if err != nil { |
| t.Fatalf("NewClientConn: %v", err) |
| } |
| client := ssh.NewClient(conn, chans, reqs) |
| |
| if err := ForwardToRemote(client, socket); err != nil { |
| t.Fatalf("SetupForwardAgent: %v", err) |
| } |
| |
| server := <-incoming |
| ch, reqs, err := server.OpenChannel(channelType, nil) |
| if err != nil { |
| t.Fatalf("OpenChannel(%q): %v", channelType, err) |
| } |
| go ssh.DiscardRequests(reqs) |
| |
| agentClient := NewClient(ch) |
| testAgentInterface(t, agentClient, testPrivateKeys["rsa"], nil, 0) |
| conn.Close() |
| } |
| |
| func TestV1ProtocolMessages(t *testing.T) { |
| c1, c2, err := netPipe() |
| if err != nil { |
| t.Fatalf("netPipe: %v", err) |
| } |
| defer c1.Close() |
| defer c2.Close() |
| c := NewClient(c1) |
| |
| go ServeAgent(NewKeyring(), c2) |
| |
| testV1ProtocolMessages(t, c.(*client)) |
| } |
| |
| func testV1ProtocolMessages(t *testing.T, c *client) { |
| reply, err := c.call([]byte{agentRequestV1Identities}) |
| if err != nil { |
| t.Fatalf("v1 request all failed: %v", err) |
| } |
| if msg, ok := reply.(*agentV1IdentityMsg); !ok || msg.Numkeys != 0 { |
| t.Fatalf("invalid request all response: %#v", reply) |
| } |
| |
| reply, err = c.call([]byte{agentRemoveAllV1Identities}) |
| if err != nil { |
| t.Fatalf("v1 remove all failed: %v", err) |
| } |
| if _, ok := reply.(*successAgentMsg); !ok { |
| t.Fatalf("invalid remove all response: %#v", reply) |
| } |
| } |
| |
| func verifyKey(sshAgent Agent) error { |
| keys, err := sshAgent.List() |
| if err != nil { |
| return fmt.Errorf("listing keys: %v", err) |
| } |
| |
| if len(keys) != 1 { |
| return fmt.Errorf("bad number of keys found. expected 1, got %d", len(keys)) |
| } |
| |
| buf := make([]byte, 128) |
| if _, err := rand.Read(buf); err != nil { |
| return fmt.Errorf("rand: %v", err) |
| } |
| |
| sig, err := sshAgent.Sign(keys[0], buf) |
| if err != nil { |
| return fmt.Errorf("sign: %v", err) |
| } |
| |
| if err := keys[0].Verify(buf, sig); err != nil { |
| return fmt.Errorf("verify: %v", err) |
| } |
| return nil |
| } |
| |
| func addKeyToAgent(key crypto.PrivateKey) error { |
| sshAgent := NewKeyring() |
| if err := sshAgent.Add(AddedKey{PrivateKey: key}); err != nil { |
| return fmt.Errorf("add: %v", err) |
| } |
| return verifyKey(sshAgent) |
| } |
| |
| func TestKeyTypes(t *testing.T) { |
| for k, v := range testPrivateKeys { |
| if err := addKeyToAgent(v); err != nil { |
| t.Errorf("error adding key type %s, %v", k, err) |
| } |
| if err := addCertToAgentSock(v, nil); err != nil { |
| t.Errorf("error adding key type %s, %v", k, err) |
| } |
| } |
| } |
| |
| func addCertToAgentSock(key crypto.PrivateKey, cert *ssh.Certificate) error { |
| a, b, err := netPipe() |
| if err != nil { |
| return err |
| } |
| agentServer := NewKeyring() |
| go ServeAgent(agentServer, a) |
| |
| agentClient := NewClient(b) |
| if err := agentClient.Add(AddedKey{PrivateKey: key, Certificate: cert}); err != nil { |
| return fmt.Errorf("add: %v", err) |
| } |
| return verifyKey(agentClient) |
| } |
| |
| func addCertToAgent(key crypto.PrivateKey, cert *ssh.Certificate) error { |
| sshAgent := NewKeyring() |
| if err := sshAgent.Add(AddedKey{PrivateKey: key, Certificate: cert}); err != nil { |
| return fmt.Errorf("add: %v", err) |
| } |
| return verifyKey(sshAgent) |
| } |
| |
| func TestCertTypes(t *testing.T) { |
| for keyType, key := range testPublicKeys { |
| cert := &ssh.Certificate{ |
| ValidPrincipals: []string{"gopher1"}, |
| ValidAfter: 0, |
| ValidBefore: ssh.CertTimeInfinity, |
| Key: key, |
| Serial: 1, |
| CertType: ssh.UserCert, |
| SignatureKey: testPublicKeys["rsa"], |
| Permissions: ssh.Permissions{ |
| CriticalOptions: map[string]string{}, |
| Extensions: map[string]string{}, |
| }, |
| } |
| if err := cert.SignCert(rand.Reader, testSigners["rsa"]); err != nil { |
| t.Fatalf("signcert: %v", err) |
| } |
| if err := addCertToAgent(testPrivateKeys[keyType], cert); err != nil { |
| t.Fatalf("%v", err) |
| } |
| if err := addCertToAgentSock(testPrivateKeys[keyType], cert); err != nil { |
| t.Fatalf("%v", err) |
| } |
| } |
| } |
| |
| func TestParseConstraints(t *testing.T) { |
| // Test LifetimeSecs |
| var msg = constrainLifetimeAgentMsg{pseudorand.Uint32()} |
| lifetimeSecs, _, _, err := parseConstraints(ssh.Marshal(msg)) |
| if err != nil { |
| t.Fatalf("parseConstraints: %v", err) |
| } |
| if lifetimeSecs != msg.LifetimeSecs { |
| t.Errorf("got lifetime %v, want %v", lifetimeSecs, msg.LifetimeSecs) |
| } |
| |
| // Test ConfirmBeforeUse |
| _, confirmBeforeUse, _, err := parseConstraints([]byte{agentConstrainConfirm}) |
| if err != nil { |
| t.Fatalf("%v", err) |
| } |
| if !confirmBeforeUse { |
| t.Error("got comfirmBeforeUse == false") |
| } |
| |
| // Test ConstraintExtensions |
| var data []byte |
| var expect []ConstraintExtension |
| for i := 0; i < 10; i++ { |
| var ext = ConstraintExtension{ |
| ExtensionName: fmt.Sprintf("name%d", i), |
| ExtensionDetails: []byte(fmt.Sprintf("details: %d", i)), |
| } |
| expect = append(expect, ext) |
| data = append(data, agentConstrainExtension) |
| data = append(data, ssh.Marshal(ext)...) |
| } |
| _, _, extensions, err := parseConstraints(data) |
| if err != nil { |
| t.Fatalf("%v", err) |
| } |
| if !reflect.DeepEqual(expect, extensions) { |
| t.Errorf("got extension %v, want %v", extensions, expect) |
| } |
| |
| // Test Unknown Constraint |
| _, _, _, err = parseConstraints([]byte{128}) |
| if err == nil || !strings.Contains(err.Error(), "unknown constraint") { |
| t.Errorf("unexpected error: %v", err) |
| } |
| } |