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