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.

repo_sign.go 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Copyright 2019 The Gitea 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. "strings"
  7. "code.gitea.io/gitea/modules/git"
  8. "code.gitea.io/gitea/modules/log"
  9. "code.gitea.io/gitea/modules/process"
  10. "code.gitea.io/gitea/modules/setting"
  11. )
  12. type signingMode string
  13. const (
  14. never signingMode = "never"
  15. always signingMode = "always"
  16. pubkey signingMode = "pubkey"
  17. twofa signingMode = "twofa"
  18. parentSigned signingMode = "parentsigned"
  19. baseSigned signingMode = "basesigned"
  20. headSigned signingMode = "headsigned"
  21. commitsSigned signingMode = "commitssigned"
  22. approved signingMode = "approved"
  23. noKey signingMode = "nokey"
  24. )
  25. func signingModeFromStrings(modeStrings []string) []signingMode {
  26. returnable := make([]signingMode, 0, len(modeStrings))
  27. for _, mode := range modeStrings {
  28. signMode := signingMode(strings.ToLower(mode))
  29. switch signMode {
  30. case never:
  31. return []signingMode{never}
  32. case always:
  33. return []signingMode{always}
  34. case pubkey:
  35. fallthrough
  36. case twofa:
  37. fallthrough
  38. case parentSigned:
  39. fallthrough
  40. case baseSigned:
  41. fallthrough
  42. case headSigned:
  43. fallthrough
  44. case approved:
  45. fallthrough
  46. case commitsSigned:
  47. returnable = append(returnable, signMode)
  48. }
  49. }
  50. if len(returnable) == 0 {
  51. return []signingMode{never}
  52. }
  53. return returnable
  54. }
  55. func signingKey(repoPath string) string {
  56. if setting.Repository.Signing.SigningKey == "none" {
  57. return ""
  58. }
  59. if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" {
  60. // Can ignore the error here as it means that commit.gpgsign is not set
  61. value, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunInDir(repoPath)
  62. sign, valid := git.ParseBool(strings.TrimSpace(value))
  63. if !sign || !valid {
  64. return ""
  65. }
  66. signingKey, _ := git.NewCommand("config", "--get", "user.signingkey").RunInDir(repoPath)
  67. return strings.TrimSpace(signingKey)
  68. }
  69. return setting.Repository.Signing.SigningKey
  70. }
  71. // PublicSigningKey gets the public signing key within a provided repository directory
  72. func PublicSigningKey(repoPath string) (string, error) {
  73. signingKey := signingKey(repoPath)
  74. if signingKey == "" {
  75. return "", nil
  76. }
  77. content, stderr, err := process.GetManager().ExecDir(-1, repoPath,
  78. "gpg --export -a", "gpg", "--export", "-a", signingKey)
  79. if err != nil {
  80. log.Error("Unable to get default signing key in %s: %s, %s, %v", repoPath, signingKey, stderr, err)
  81. return "", err
  82. }
  83. return content, nil
  84. }
  85. // SignInitialCommit determines if we should sign the initial commit to this repository
  86. func SignInitialCommit(repoPath string, u *User) (bool, string, error) {
  87. rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit)
  88. signingKey := signingKey(repoPath)
  89. if signingKey == "" {
  90. return false, "", &ErrWontSign{noKey}
  91. }
  92. Loop:
  93. for _, rule := range rules {
  94. switch rule {
  95. case never:
  96. return false, "", &ErrWontSign{never}
  97. case always:
  98. break Loop
  99. case pubkey:
  100. keys, err := ListGPGKeys(u.ID, ListOptions{})
  101. if err != nil {
  102. return false, "", err
  103. }
  104. if len(keys) == 0 {
  105. return false, "", &ErrWontSign{pubkey}
  106. }
  107. case twofa:
  108. twofaModel, err := GetTwoFactorByUID(u.ID)
  109. if err != nil && !IsErrTwoFactorNotEnrolled(err) {
  110. return false, "", err
  111. }
  112. if twofaModel == nil {
  113. return false, "", &ErrWontSign{twofa}
  114. }
  115. }
  116. }
  117. return true, signingKey, nil
  118. }
  119. // SignWikiCommit determines if we should sign the commits to this repository wiki
  120. func (repo *Repository) SignWikiCommit(u *User) (bool, string, error) {
  121. rules := signingModeFromStrings(setting.Repository.Signing.Wiki)
  122. signingKey := signingKey(repo.WikiPath())
  123. if signingKey == "" {
  124. return false, "", &ErrWontSign{noKey}
  125. }
  126. Loop:
  127. for _, rule := range rules {
  128. switch rule {
  129. case never:
  130. return false, "", &ErrWontSign{never}
  131. case always:
  132. break Loop
  133. case pubkey:
  134. keys, err := ListGPGKeys(u.ID, ListOptions{})
  135. if err != nil {
  136. return false, "", err
  137. }
  138. if len(keys) == 0 {
  139. return false, "", &ErrWontSign{pubkey}
  140. }
  141. case twofa:
  142. twofaModel, err := GetTwoFactorByUID(u.ID)
  143. if err != nil && !IsErrTwoFactorNotEnrolled(err) {
  144. return false, "", err
  145. }
  146. if twofaModel == nil {
  147. return false, "", &ErrWontSign{twofa}
  148. }
  149. case parentSigned:
  150. gitRepo, err := git.OpenRepository(repo.WikiPath())
  151. if err != nil {
  152. return false, "", err
  153. }
  154. defer gitRepo.Close()
  155. commit, err := gitRepo.GetCommit("HEAD")
  156. if err != nil {
  157. return false, "", err
  158. }
  159. if commit.Signature == nil {
  160. return false, "", &ErrWontSign{parentSigned}
  161. }
  162. verification := ParseCommitWithSignature(commit)
  163. if !verification.Verified {
  164. return false, "", &ErrWontSign{parentSigned}
  165. }
  166. }
  167. }
  168. return true, signingKey, nil
  169. }
  170. // SignCRUDAction determines if we should sign a CRUD commit to this repository
  171. func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string, error) {
  172. rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions)
  173. signingKey := signingKey(repo.RepoPath())
  174. if signingKey == "" {
  175. return false, "", &ErrWontSign{noKey}
  176. }
  177. Loop:
  178. for _, rule := range rules {
  179. switch rule {
  180. case never:
  181. return false, "", &ErrWontSign{never}
  182. case always:
  183. break Loop
  184. case pubkey:
  185. keys, err := ListGPGKeys(u.ID, ListOptions{})
  186. if err != nil {
  187. return false, "", err
  188. }
  189. if len(keys) == 0 {
  190. return false, "", &ErrWontSign{pubkey}
  191. }
  192. case twofa:
  193. twofaModel, err := GetTwoFactorByUID(u.ID)
  194. if err != nil && !IsErrTwoFactorNotEnrolled(err) {
  195. return false, "", err
  196. }
  197. if twofaModel == nil {
  198. return false, "", &ErrWontSign{twofa}
  199. }
  200. case parentSigned:
  201. gitRepo, err := git.OpenRepository(tmpBasePath)
  202. if err != nil {
  203. return false, "", err
  204. }
  205. defer gitRepo.Close()
  206. commit, err := gitRepo.GetCommit(parentCommit)
  207. if err != nil {
  208. return false, "", err
  209. }
  210. if commit.Signature == nil {
  211. return false, "", &ErrWontSign{parentSigned}
  212. }
  213. verification := ParseCommitWithSignature(commit)
  214. if !verification.Verified {
  215. return false, "", &ErrWontSign{parentSigned}
  216. }
  217. }
  218. }
  219. return true, signingKey, nil
  220. }