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