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.

publickey.go 4.8KB

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