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.

209 lines
4.2 KiB

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