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