Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

publickey.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "bufio"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "os"
  12. "os/exec"
  13. "path"
  14. "path/filepath"
  15. "strings"
  16. "sync"
  17. "time"
  18. "github.com/Unknwon/com"
  19. "github.com/gogits/gogs/modules/log"
  20. )
  21. const (
  22. // "### autogenerated by gitgos, DO NOT EDIT\n"
  23. TPL_PUBLICK_KEY = `command="%s serv key-%d",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s`
  24. )
  25. var (
  26. ErrKeyAlreadyExist = errors.New("Public key already exist")
  27. )
  28. var sshOpLocker = sync.Mutex{}
  29. var (
  30. sshPath string
  31. appPath string
  32. )
  33. // exePath returns the executable path.
  34. func exePath() (string, error) {
  35. file, err := exec.LookPath(os.Args[0])
  36. if err != nil {
  37. return "", err
  38. }
  39. return filepath.Abs(file)
  40. }
  41. // homeDir returns the home directory of current user.
  42. func homeDir() string {
  43. home, err := com.HomeDir()
  44. if err != nil {
  45. return "/"
  46. }
  47. return home
  48. }
  49. func init() {
  50. var err error
  51. appPath, err = exePath()
  52. if err != nil {
  53. fmt.Printf("publickey.init(fail to get app path): %v\n", err)
  54. os.Exit(2)
  55. }
  56. // Determine and create .ssh path.
  57. sshPath = filepath.Join(homeDir(), ".ssh")
  58. if err = os.MkdirAll(sshPath, os.ModePerm); err != nil {
  59. fmt.Printf("publickey.init(fail to create sshPath(%s)): %v\n", sshPath, err)
  60. os.Exit(2)
  61. }
  62. }
  63. // PublicKey represents a SSH key of user.
  64. type PublicKey struct {
  65. Id int64
  66. OwnerId int64 `xorm:"index"`
  67. Name string `xorm:"unique not null"`
  68. Fingerprint string
  69. Content string `xorm:"TEXT not null"`
  70. Created time.Time `xorm:"created"`
  71. Updated time.Time `xorm:"updated"`
  72. }
  73. // GenAuthorizedKey returns formatted public key string.
  74. func GenAuthorizedKey(keyId int64, key string) string {
  75. return fmt.Sprintf(TPL_PUBLICK_KEY+"\n", appPath, keyId, key)
  76. }
  77. // AddPublicKey adds new public key to database and SSH key file.
  78. func AddPublicKey(key *PublicKey) (err error) {
  79. // Check if public key name has been used.
  80. has, err := orm.Get(key)
  81. if err != nil {
  82. return err
  83. } else if has {
  84. return ErrKeyAlreadyExist
  85. }
  86. // Calculate fingerprint.
  87. tmpPath := strings.Replace(filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
  88. "id_rsa.pub"), "\\", "/", -1)
  89. os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
  90. if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil {
  91. return err
  92. }
  93. stdout, _, err := com.ExecCmd("ssh-keygen", "-l", "-f", tmpPath)
  94. if err != nil {
  95. return err
  96. } else if len(stdout) < 2 {
  97. return errors.New("Not enough output for calculating fingerprint")
  98. }
  99. key.Fingerprint = strings.Split(stdout, " ")[1]
  100. // Save SSH key.
  101. if _, err = orm.Insert(key); err != nil {
  102. return err
  103. }
  104. if err = SaveAuthorizedKeyFile(key); err != nil {
  105. if _, err2 := orm.Delete(key); err2 != nil {
  106. return err2
  107. }
  108. return err
  109. }
  110. return nil
  111. }
  112. func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error {
  113. // Delete SSH key in SSH key file.
  114. sshOpLocker.Lock()
  115. defer sshOpLocker.Unlock()
  116. fr, err := os.Open(p)
  117. if err != nil {
  118. return err
  119. }
  120. defer fr.Close()
  121. fw, err := os.Create(tmpP)
  122. if err != nil {
  123. return err
  124. }
  125. defer fw.Close()
  126. buf := bufio.NewReader(fr)
  127. for {
  128. line, errRead := buf.ReadString('\n')
  129. line = strings.TrimSpace(line)
  130. if errRead != nil {
  131. if errRead != io.EOF {
  132. return errRead
  133. }
  134. // Reached end of file, if nothing to read then break,
  135. // otherwise handle the last line.
  136. if len(line) == 0 {
  137. break
  138. }
  139. }
  140. // Found the line and copy rest of file.
  141. if strings.Contains(line, fmt.Sprintf("key-%d", key.Id)) && strings.Contains(line, key.Content) {
  142. continue
  143. }
  144. // Still finding the line, copy the line that currently read.
  145. if _, err = fw.WriteString(line + "\n"); err != nil {
  146. return err
  147. }
  148. if errRead == io.EOF {
  149. break
  150. }
  151. }
  152. return nil
  153. }
  154. // DeletePublicKey deletes SSH key information both in database and authorized_keys file.
  155. func DeletePublicKey(key *PublicKey) (err error) {
  156. // Delete SSH key in database.
  157. has, err := orm.Id(key.Id).Get(key)
  158. if err != nil {
  159. return err
  160. } else if !has {
  161. return errors.New("Public key does not exist")
  162. }
  163. if _, err = orm.Delete(key); err != nil {
  164. return err
  165. }
  166. p := filepath.Join(sshPath, "authorized_keys")
  167. tmpP := filepath.Join(sshPath, "authorized_keys.tmp")
  168. log.Trace("ssh.DeletePublicKey(authorized_keys): %s", p)
  169. if err = rewriteAuthorizedKeys(key, p, tmpP); err != nil {
  170. return err
  171. } else if err = os.Remove(p); err != nil {
  172. return err
  173. }
  174. return os.Rename(tmpP, p)
  175. }
  176. // ListPublicKey returns a list of public keys that user has.
  177. func ListPublicKey(userId int64) ([]PublicKey, error) {
  178. keys := make([]PublicKey, 0)
  179. err := orm.Find(&keys, &PublicKey{OwnerId: userId})
  180. return keys, err
  181. }
  182. // SaveAuthorizedKeyFile writes SSH key content to SSH key file.
  183. func SaveAuthorizedKeyFile(key *PublicKey) error {
  184. sshOpLocker.Lock()
  185. defer sshOpLocker.Unlock()
  186. p := filepath.Join(sshPath, "authorized_keys")
  187. f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  188. if err != nil {
  189. return err
  190. }
  191. defer f.Close()
  192. _, err = f.WriteString(GenAuthorizedKey(key.Id, key.Content))
  193. return err
  194. }