You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

216 lines
4.4 KiB

  1. // Copyright 2014 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. package agent
  5. import (
  6. "bytes"
  7. "crypto/rand"
  8. "crypto/subtle"
  9. "errors"
  10. "fmt"
  11. "sync"
  12. "time"
  13. "golang.org/x/crypto/ssh"
  14. )
  15. type privKey struct {
  16. signer ssh.Signer
  17. comment string
  18. expire *time.Time
  19. }
  20. type keyring struct {
  21. mu sync.Mutex
  22. keys []privKey
  23. locked bool
  24. passphrase []byte
  25. }
  26. var errLocked = errors.New("agent: locked")
  27. // NewKeyring returns an Agent that holds keys in memory. It is safe
  28. // for concurrent use by multiple goroutines.
  29. func NewKeyring() Agent {
  30. return &keyring{}
  31. }
  32. // RemoveAll removes all identities.
  33. func (r *keyring) RemoveAll() error {
  34. r.mu.Lock()
  35. defer r.mu.Unlock()
  36. if r.locked {
  37. return errLocked
  38. }
  39. r.keys = nil
  40. return nil
  41. }
  42. // removeLocked does the actual key removal. The caller must already be holding the
  43. // keyring mutex.
  44. func (r *keyring) removeLocked(want []byte) error {
  45. found := false
  46. for i := 0; i < len(r.keys); {
  47. if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) {
  48. found = true
  49. r.keys[i] = r.keys[len(r.keys)-1]
  50. r.keys = r.keys[:len(r.keys)-1]
  51. continue
  52. } else {
  53. i++
  54. }
  55. }
  56. if !found {
  57. return errors.New("agent: key not found")
  58. }
  59. return nil
  60. }
  61. // Remove removes all identities with the given public key.
  62. func (r *keyring) Remove(key ssh.PublicKey) error {
  63. r.mu.Lock()
  64. defer r.mu.Unlock()
  65. if r.locked {
  66. return errLocked
  67. }
  68. return r.removeLocked(key.Marshal())
  69. }
  70. // Lock locks the agent. Sign and Remove will fail, and List will return an empty list.
  71. func (r *keyring) Lock(passphrase []byte) error {
  72. r.mu.Lock()
  73. defer r.mu.Unlock()
  74. if r.locked {
  75. return errLocked
  76. }
  77. r.locked = true
  78. r.passphrase = passphrase
  79. return nil
  80. }
  81. // Unlock undoes the effect of Lock
  82. func (r *keyring) Unlock(passphrase []byte) error {
  83. r.mu.Lock()
  84. defer r.mu.Unlock()
  85. if !r.locked {
  86. return errors.New("agent: not locked")
  87. }
  88. if 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) {
  89. return fmt.Errorf("agent: incorrect passphrase")
  90. }
  91. r.locked = false
  92. r.passphrase = nil
  93. return nil
  94. }
  95. // expireKeysLocked removes expired keys from the keyring. If a key was added
  96. // with a lifetimesecs contraint and seconds >= lifetimesecs seconds have
  97. // ellapsed, it is removed. The caller *must* be holding the keyring mutex.
  98. func (r *keyring) expireKeysLocked() {
  99. for _, k := range r.keys {
  100. if k.expire != nil && time.Now().After(*k.expire) {
  101. r.removeLocked(k.signer.PublicKey().Marshal())
  102. }
  103. }
  104. }
  105. // List returns the identities known to the agent.
  106. func (r *keyring) List() ([]*Key, error) {
  107. r.mu.Lock()
  108. defer r.mu.Unlock()
  109. if r.locked {
  110. // section 2.7: locked agents return empty.
  111. return nil, nil
  112. }
  113. r.expireKeysLocked()
  114. var ids []*Key
  115. for _, k := range r.keys {
  116. pub := k.signer.PublicKey()
  117. ids = append(ids, &Key{
  118. Format: pub.Type(),
  119. Blob: pub.Marshal(),
  120. Comment: k.comment})
  121. }
  122. return ids, nil
  123. }
  124. // Insert adds a private key to the keyring. If a certificate
  125. // is given, that certificate is added as public key. Note that
  126. // any constraints given are ignored.
  127. func (r *keyring) Add(key AddedKey) error {
  128. r.mu.Lock()
  129. defer r.mu.Unlock()
  130. if r.locked {
  131. return errLocked
  132. }
  133. signer, err := ssh.NewSignerFromKey(key.PrivateKey)
  134. if err != nil {
  135. return err
  136. }
  137. if cert := key.Certificate; cert != nil {
  138. signer, err = ssh.NewCertSigner(cert, signer)
  139. if err != nil {
  140. return err
  141. }
  142. }
  143. p := privKey{
  144. signer: signer,
  145. comment: key.Comment,
  146. }
  147. if key.LifetimeSecs > 0 {
  148. t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second)
  149. p.expire = &t
  150. }
  151. r.keys = append(r.keys, p)
  152. return nil
  153. }
  154. // Sign returns a signature for the data.
  155. func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
  156. r.mu.Lock()
  157. defer r.mu.Unlock()
  158. if r.locked {
  159. return nil, errLocked
  160. }
  161. r.expireKeysLocked()
  162. wanted := key.Marshal()
  163. for _, k := range r.keys {
  164. if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
  165. return k.signer.Sign(rand.Reader, data)
  166. }
  167. }
  168. return nil, errors.New("not found")
  169. }
  170. // Signers returns signers for all the known keys.
  171. func (r *keyring) Signers() ([]ssh.Signer, error) {
  172. r.mu.Lock()
  173. defer r.mu.Unlock()
  174. if r.locked {
  175. return nil, errLocked
  176. }
  177. r.expireKeysLocked()
  178. s := make([]ssh.Signer, 0, len(r.keys))
  179. for _, k := range r.keys {
  180. s = append(s, k.signer)
  181. }
  182. return s, nil
  183. }