github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/golang.org/x/crypto/ssh/agent/client.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 /* 6 Package agent implements a client to an ssh-agent daemon. 7 8 References: 9 [PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD 10 */ 11 package agent 12 13 import ( 14 "bytes" 15 "crypto/dsa" 16 "crypto/ecdsa" 17 "crypto/elliptic" 18 "crypto/rsa" 19 "encoding/base64" 20 "encoding/binary" 21 "errors" 22 "fmt" 23 "io" 24 "math/big" 25 "sync" 26 27 "golang.org/x/crypto/ssh" 28 ) 29 30 // Agent represents the capabilities of an ssh-agent. 31 type Agent interface { 32 // List returns the identities known to the agent. 33 List() ([]*Key, error) 34 35 // Sign has the agent sign the data using a protocol 2 key as defined 36 // in [PROTOCOL.agent] section 2.6.2. 37 Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) 38 39 // Insert adds a private key to the agent. If a certificate 40 // is given, that certificate is added as public key. 41 Add(s interface{}, cert *ssh.Certificate, comment string) error 42 43 // Remove removes all identities with the given public key. 44 Remove(key ssh.PublicKey) error 45 46 // RemoveAll removes all identities. 47 RemoveAll() error 48 49 // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. 50 Lock(passphrase []byte) error 51 52 // Unlock undoes the effect of Lock 53 Unlock(passphrase []byte) error 54 55 // Signers returns signers for all the known keys. 56 Signers() ([]ssh.Signer, error) 57 } 58 59 // See [PROTOCOL.agent], section 3. 60 const ( 61 agentRequestV1Identities = 1 62 63 // 3.2 Requests from client to agent for protocol 2 key operations 64 agentAddIdentity = 17 65 agentRemoveIdentity = 18 66 agentRemoveAllIdentities = 19 67 agentAddIdConstrained = 25 68 69 // 3.3 Key-type independent requests from client to agent 70 agentAddSmartcardKey = 20 71 agentRemoveSmartcardKey = 21 72 agentLock = 22 73 agentUnlock = 23 74 agentAddSmartcardKeyConstrained = 26 75 76 // 3.7 Key constraint identifiers 77 agentConstrainLifetime = 1 78 agentConstrainConfirm = 2 79 ) 80 81 // maxAgentResponseBytes is the maximum agent reply size that is accepted. This 82 // is a sanity check, not a limit in the spec. 83 const maxAgentResponseBytes = 16 << 20 84 85 // Agent messages: 86 // These structures mirror the wire format of the corresponding ssh agent 87 // messages found in [PROTOCOL.agent]. 88 89 // 3.4 Generic replies from agent to client 90 const agentFailure = 5 91 92 type failureAgentMsg struct{} 93 94 const agentSuccess = 6 95 96 type successAgentMsg struct{} 97 98 // See [PROTOCOL.agent], section 2.5.2. 99 const agentRequestIdentities = 11 100 101 type requestIdentitiesAgentMsg struct{} 102 103 // See [PROTOCOL.agent], section 2.5.2. 104 const agentIdentitiesAnswer = 12 105 106 type identitiesAnswerAgentMsg struct { 107 NumKeys uint32 `sshtype:"12"` 108 Keys []byte `ssh:"rest"` 109 } 110 111 // See [PROTOCOL.agent], section 2.6.2. 112 const agentSignRequest = 13 113 114 type signRequestAgentMsg struct { 115 KeyBlob []byte `sshtype:"13"` 116 Data []byte 117 Flags uint32 118 } 119 120 // See [PROTOCOL.agent], section 2.6.2. 121 122 // 3.6 Replies from agent to client for protocol 2 key operations 123 const agentSignResponse = 14 124 125 type signResponseAgentMsg struct { 126 SigBlob []byte `sshtype:"14"` 127 } 128 129 type publicKey struct { 130 Format string 131 Rest []byte `ssh:"rest"` 132 } 133 134 // Key represents a protocol 2 public key as defined in 135 // [PROTOCOL.agent], section 2.5.2. 136 type Key struct { 137 Format string 138 Blob []byte 139 Comment string 140 } 141 142 func clientErr(err error) error { 143 return fmt.Errorf("agent: client error: %v", err) 144 } 145 146 // String returns the storage form of an agent key with the format, base64 147 // encoded serialized key, and the comment if it is not empty. 148 func (k *Key) String() string { 149 s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob) 150 151 if k.Comment != "" { 152 s += " " + k.Comment 153 } 154 155 return s 156 } 157 158 // Type returns the public key type. 159 func (k *Key) Type() string { 160 return k.Format 161 } 162 163 // Marshal returns key blob to satisfy the ssh.PublicKey interface. 164 func (k *Key) Marshal() []byte { 165 return k.Blob 166 } 167 168 // Verify satisfies the ssh.PublicKey interface, but is not 169 // implemented for agent keys. 170 func (k *Key) Verify(data []byte, sig *ssh.Signature) error { 171 return errors.New("agent: agent key does not know how to verify") 172 } 173 174 type wireKey struct { 175 Format string 176 Rest []byte `ssh:"rest"` 177 } 178 179 func parseKey(in []byte) (out *Key, rest []byte, err error) { 180 var record struct { 181 Blob []byte 182 Comment string 183 Rest []byte `ssh:"rest"` 184 } 185 186 if err := ssh.Unmarshal(in, &record); err != nil { 187 return nil, nil, err 188 } 189 190 var wk wireKey 191 if err := ssh.Unmarshal(record.Blob, &wk); err != nil { 192 return nil, nil, err 193 } 194 195 return &Key{ 196 Format: wk.Format, 197 Blob: record.Blob, 198 Comment: record.Comment, 199 }, record.Rest, nil 200 } 201 202 // client is a client for an ssh-agent process. 203 type client struct { 204 // conn is typically a *net.UnixConn 205 conn io.ReadWriter 206 // mu is used to prevent concurrent access to the agent 207 mu sync.Mutex 208 } 209 210 // NewClient returns an Agent that talks to an ssh-agent process over 211 // the given connection. 212 func NewClient(rw io.ReadWriter) Agent { 213 return &client{conn: rw} 214 } 215 216 // call sends an RPC to the agent. On success, the reply is 217 // unmarshaled into reply and replyType is set to the first byte of 218 // the reply, which contains the type of the message. 219 func (c *client) call(req []byte) (reply interface{}, err error) { 220 c.mu.Lock() 221 defer c.mu.Unlock() 222 223 msg := make([]byte, 4+len(req)) 224 binary.BigEndian.PutUint32(msg, uint32(len(req))) 225 copy(msg[4:], req) 226 if _, err = c.conn.Write(msg); err != nil { 227 return nil, clientErr(err) 228 } 229 230 var respSizeBuf [4]byte 231 if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil { 232 return nil, clientErr(err) 233 } 234 respSize := binary.BigEndian.Uint32(respSizeBuf[:]) 235 if respSize > maxAgentResponseBytes { 236 return nil, clientErr(err) 237 } 238 239 buf := make([]byte, respSize) 240 if _, err = io.ReadFull(c.conn, buf); err != nil { 241 return nil, clientErr(err) 242 } 243 reply, err = unmarshal(buf) 244 if err != nil { 245 return nil, clientErr(err) 246 } 247 return reply, err 248 } 249 250 func (c *client) simpleCall(req []byte) error { 251 resp, err := c.call(req) 252 if err != nil { 253 return err 254 } 255 if _, ok := resp.(*successAgentMsg); ok { 256 return nil 257 } 258 return errors.New("agent: failure") 259 } 260 261 func (c *client) RemoveAll() error { 262 return c.simpleCall([]byte{agentRemoveAllIdentities}) 263 } 264 265 func (c *client) Remove(key ssh.PublicKey) error { 266 req := ssh.Marshal(&agentRemoveIdentityMsg{ 267 KeyBlob: key.Marshal(), 268 }) 269 return c.simpleCall(req) 270 } 271 272 func (c *client) Lock(passphrase []byte) error { 273 req := ssh.Marshal(&agentLockMsg{ 274 Passphrase: passphrase, 275 }) 276 return c.simpleCall(req) 277 } 278 279 func (c *client) Unlock(passphrase []byte) error { 280 req := ssh.Marshal(&agentUnlockMsg{ 281 Passphrase: passphrase, 282 }) 283 return c.simpleCall(req) 284 } 285 286 // List returns the identities known to the agent. 287 func (c *client) List() ([]*Key, error) { 288 // see [PROTOCOL.agent] section 2.5.2. 289 req := []byte{agentRequestIdentities} 290 291 msg, err := c.call(req) 292 if err != nil { 293 return nil, err 294 } 295 296 switch msg := msg.(type) { 297 case *identitiesAnswerAgentMsg: 298 if msg.NumKeys > maxAgentResponseBytes/8 { 299 return nil, errors.New("agent: too many keys in agent reply") 300 } 301 keys := make([]*Key, msg.NumKeys) 302 data := msg.Keys 303 for i := uint32(0); i < msg.NumKeys; i++ { 304 var key *Key 305 var err error 306 if key, data, err = parseKey(data); err != nil { 307 return nil, err 308 } 309 keys[i] = key 310 } 311 return keys, nil 312 case *failureAgentMsg: 313 return nil, errors.New("agent: failed to list keys") 314 } 315 panic("unreachable") 316 } 317 318 // Sign has the agent sign the data using a protocol 2 key as defined 319 // in [PROTOCOL.agent] section 2.6.2. 320 func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { 321 req := ssh.Marshal(signRequestAgentMsg{ 322 KeyBlob: key.Marshal(), 323 Data: data, 324 }) 325 326 msg, err := c.call(req) 327 if err != nil { 328 return nil, err 329 } 330 331 switch msg := msg.(type) { 332 case *signResponseAgentMsg: 333 var sig ssh.Signature 334 if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { 335 return nil, err 336 } 337 338 return &sig, nil 339 case *failureAgentMsg: 340 return nil, errors.New("agent: failed to sign challenge") 341 } 342 panic("unreachable") 343 } 344 345 // unmarshal parses an agent message in packet, returning the parsed 346 // form and the message type of packet. 347 func unmarshal(packet []byte) (interface{}, error) { 348 if len(packet) < 1 { 349 return nil, errors.New("agent: empty packet") 350 } 351 var msg interface{} 352 switch packet[0] { 353 case agentFailure: 354 return new(failureAgentMsg), nil 355 case agentSuccess: 356 return new(successAgentMsg), nil 357 case agentIdentitiesAnswer: 358 msg = new(identitiesAnswerAgentMsg) 359 case agentSignResponse: 360 msg = new(signResponseAgentMsg) 361 default: 362 return nil, fmt.Errorf("agent: unknown type tag %d", packet[0]) 363 } 364 if err := ssh.Unmarshal(packet, msg); err != nil { 365 return nil, err 366 } 367 return msg, nil 368 } 369 370 type rsaKeyMsg struct { 371 Type string `sshtype:"17"` 372 N *big.Int 373 E *big.Int 374 D *big.Int 375 Iqmp *big.Int // IQMP = Inverse Q Mod P 376 P *big.Int 377 Q *big.Int 378 Comments string 379 } 380 381 type dsaKeyMsg struct { 382 Type string `sshtype:"17"` 383 P *big.Int 384 Q *big.Int 385 G *big.Int 386 Y *big.Int 387 X *big.Int 388 Comments string 389 } 390 391 type ecdsaKeyMsg struct { 392 Type string `sshtype:"17"` 393 Curve string 394 KeyBytes []byte 395 D *big.Int 396 Comments string 397 } 398 399 // Insert adds a private key to the agent. 400 func (c *client) insertKey(s interface{}, comment string) error { 401 var req []byte 402 switch k := s.(type) { 403 case *rsa.PrivateKey: 404 if len(k.Primes) != 2 { 405 return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) 406 } 407 k.Precompute() 408 req = ssh.Marshal(rsaKeyMsg{ 409 Type: ssh.KeyAlgoRSA, 410 N: k.N, 411 E: big.NewInt(int64(k.E)), 412 D: k.D, 413 Iqmp: k.Precomputed.Qinv, 414 P: k.Primes[0], 415 Q: k.Primes[1], 416 Comments: comment, 417 }) 418 case *dsa.PrivateKey: 419 req = ssh.Marshal(dsaKeyMsg{ 420 Type: ssh.KeyAlgoDSA, 421 P: k.P, 422 Q: k.Q, 423 G: k.G, 424 Y: k.Y, 425 X: k.X, 426 Comments: comment, 427 }) 428 case *ecdsa.PrivateKey: 429 nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) 430 req = ssh.Marshal(ecdsaKeyMsg{ 431 Type: "ecdsa-sha2-" + nistID, 432 Curve: nistID, 433 KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y), 434 D: k.D, 435 Comments: comment, 436 }) 437 default: 438 return fmt.Errorf("agent: unsupported key type %T", s) 439 } 440 resp, err := c.call(req) 441 if err != nil { 442 return err 443 } 444 if _, ok := resp.(*successAgentMsg); ok { 445 return nil 446 } 447 return errors.New("agent: failure") 448 } 449 450 type rsaCertMsg struct { 451 Type string `sshtype:"17"` 452 CertBytes []byte 453 D *big.Int 454 Iqmp *big.Int // IQMP = Inverse Q Mod P 455 P *big.Int 456 Q *big.Int 457 Comments string 458 } 459 460 type dsaCertMsg struct { 461 Type string `sshtype:"17"` 462 CertBytes []byte 463 X *big.Int 464 Comments string 465 } 466 467 type ecdsaCertMsg struct { 468 Type string `sshtype:"17"` 469 CertBytes []byte 470 D *big.Int 471 Comments string 472 } 473 474 // Insert adds a private key to the agent. If a certificate is given, 475 // that certificate is added instead as public key. 476 func (c *client) Add(s interface{}, cert *ssh.Certificate, comment string) error { 477 if cert == nil { 478 return c.insertKey(s, comment) 479 } else { 480 return c.insertCert(s, cert, comment) 481 } 482 } 483 484 func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string) error { 485 var req []byte 486 switch k := s.(type) { 487 case *rsa.PrivateKey: 488 if len(k.Primes) != 2 { 489 return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) 490 } 491 k.Precompute() 492 req = ssh.Marshal(rsaCertMsg{ 493 Type: cert.Type(), 494 CertBytes: cert.Marshal(), 495 D: k.D, 496 Iqmp: k.Precomputed.Qinv, 497 P: k.Primes[0], 498 Q: k.Primes[1], 499 Comments: comment, 500 }) 501 case *dsa.PrivateKey: 502 req = ssh.Marshal(dsaCertMsg{ 503 Type: cert.Type(), 504 CertBytes: cert.Marshal(), 505 X: k.X, 506 Comments: comment, 507 }) 508 case *ecdsa.PrivateKey: 509 req = ssh.Marshal(ecdsaCertMsg{ 510 Type: cert.Type(), 511 CertBytes: cert.Marshal(), 512 D: k.D, 513 Comments: comment, 514 }) 515 default: 516 return fmt.Errorf("agent: unsupported key type %T", s) 517 } 518 519 signer, err := ssh.NewSignerFromKey(s) 520 if err != nil { 521 return err 522 } 523 if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { 524 return errors.New("agent: signer and cert have different public key") 525 } 526 527 resp, err := c.call(req) 528 if err != nil { 529 return err 530 } 531 if _, ok := resp.(*successAgentMsg); ok { 532 return nil 533 } 534 return errors.New("agent: failure") 535 } 536 537 // Signers provides a callback for client authentication. 538 func (c *client) Signers() ([]ssh.Signer, error) { 539 keys, err := c.List() 540 if err != nil { 541 return nil, err 542 } 543 544 var result []ssh.Signer 545 for _, k := range keys { 546 result = append(result, &agentKeyringSigner{c, k}) 547 } 548 return result, nil 549 } 550 551 type agentKeyringSigner struct { 552 agent *client 553 pub ssh.PublicKey 554 } 555 556 func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { 557 return s.pub 558 } 559 560 func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) { 561 // The agent has its own entropy source, so the rand argument is ignored. 562 return s.agent.Sign(s.pub, data) 563 }