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 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. )
  23. func signingModeFromStrings(modeStrings []string) []signingMode {
  24. returnable := make([]signingMode, 0, len(modeStrings))
  25. for _, mode := range modeStrings {
  26. signMode := signingMode(strings.ToLower(mode))
  27. switch signMode {
  28. case never:
  29. return []signingMode{never}
  30. case always:
  31. return []signingMode{always}
  32. case pubkey:
  33. fallthrough
  34. case twofa:
  35. fallthrough
  36. case parentSigned:
  37. fallthrough
  38. case baseSigned:
  39. fallthrough
  40. case headSigned:
  41. fallthrough
  42. case commitsSigned:
  43. returnable = append(returnable, signMode)
  44. }
  45. }
  46. if len(returnable) == 0 {
  47. return []signingMode{never}
  48. }
  49. return returnable
  50. }
  51. func signingKey(repoPath string) string {
  52. if setting.Repository.Signing.SigningKey == "none" {
  53. return ""
  54. }
  55. if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" {
  56. // Can ignore the error here as it means that commit.gpgsign is not set
  57. value, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunInDir(repoPath)
  58. sign, valid := git.ParseBool(strings.TrimSpace(value))
  59. if !sign || !valid {
  60. return ""
  61. }
  62. signingKey, _ := git.NewCommand("config", "--get", "user.signingkey").RunInDir(repoPath)
  63. return strings.TrimSpace(signingKey)
  64. }
  65. return setting.Repository.Signing.SigningKey
  66. }
  67. // PublicSigningKey gets the public signing key within a provided repository directory
  68. func PublicSigningKey(repoPath string) (string, error) {
  69. signingKey := signingKey(repoPath)
  70. if signingKey == "" {
  71. return "", nil
  72. }
  73. content, stderr, err := process.GetManager().ExecDir(-1, repoPath,
  74. "gpg --export -a", "gpg", "--export", "-a", signingKey)
  75. if err != nil {
  76. log.Error("Unable to get default signing key in %s: %s, %s, %v", repoPath, signingKey, stderr, err)
  77. return "", err
  78. }
  79. return content, nil
  80. }
  81. // SignInitialCommit determines if we should sign the initial commit to this repository
  82. func SignInitialCommit(repoPath string, u *User) (bool, string) {
  83. rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit)
  84. signingKey := signingKey(repoPath)
  85. if signingKey == "" {
  86. return false, ""
  87. }
  88. for _, rule := range rules {
  89. switch rule {
  90. case never:
  91. return false, ""
  92. case always:
  93. break
  94. case pubkey:
  95. keys, err := ListGPGKeys(u.ID)
  96. if err != nil || len(keys) == 0 {
  97. return false, ""
  98. }
  99. case twofa:
  100. twofa, err := GetTwoFactorByUID(u.ID)
  101. if err != nil || twofa == nil {
  102. return false, ""
  103. }
  104. }
  105. }
  106. return true, signingKey
  107. }
  108. // SignWikiCommit determines if we should sign the commits to this repository wiki
  109. func (repo *Repository) SignWikiCommit(u *User) (bool, string) {
  110. rules := signingModeFromStrings(setting.Repository.Signing.Wiki)
  111. signingKey := signingKey(repo.WikiPath())
  112. if signingKey == "" {
  113. return false, ""
  114. }
  115. for _, rule := range rules {
  116. switch rule {
  117. case never:
  118. return false, ""
  119. case always:
  120. break
  121. case pubkey:
  122. keys, err := ListGPGKeys(u.ID)
  123. if err != nil || len(keys) == 0 {
  124. return false, ""
  125. }
  126. case twofa:
  127. twofa, err := GetTwoFactorByUID(u.ID)
  128. if err != nil || twofa == nil {
  129. return false, ""
  130. }
  131. case parentSigned:
  132. gitRepo, err := git.OpenRepository(repo.WikiPath())
  133. if err != nil {
  134. return false, ""
  135. }
  136. defer gitRepo.Close()
  137. commit, err := gitRepo.GetCommit("HEAD")
  138. if err != nil {
  139. return false, ""
  140. }
  141. if commit.Signature == nil {
  142. return false, ""
  143. }
  144. verification := ParseCommitWithSignature(commit)
  145. if !verification.Verified {
  146. return false, ""
  147. }
  148. }
  149. }
  150. return true, signingKey
  151. }
  152. // SignCRUDAction determines if we should sign a CRUD commit to this repository
  153. func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string) {
  154. rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions)
  155. signingKey := signingKey(repo.RepoPath())
  156. if signingKey == "" {
  157. return false, ""
  158. }
  159. for _, rule := range rules {
  160. switch rule {
  161. case never:
  162. return false, ""
  163. case always:
  164. break
  165. case pubkey:
  166. keys, err := ListGPGKeys(u.ID)
  167. if err != nil || len(keys) == 0 {
  168. return false, ""
  169. }
  170. case twofa:
  171. twofa, err := GetTwoFactorByUID(u.ID)
  172. if err != nil || twofa == nil {
  173. return false, ""
  174. }
  175. case parentSigned:
  176. gitRepo, err := git.OpenRepository(tmpBasePath)
  177. if err != nil {
  178. return false, ""
  179. }
  180. defer gitRepo.Close()
  181. commit, err := gitRepo.GetCommit(parentCommit)
  182. if err != nil {
  183. return false, ""
  184. }
  185. if commit.Signature == nil {
  186. return false, ""
  187. }
  188. verification := ParseCommitWithSignature(commit)
  189. if !verification.Verified {
  190. return false, ""
  191. }
  192. }
  193. }
  194. return true, signingKey
  195. }
  196. // SignMerge determines if we should sign a merge commit to this repository
  197. func (repo *Repository) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string) {
  198. rules := signingModeFromStrings(setting.Repository.Signing.Merges)
  199. signingKey := signingKey(repo.RepoPath())
  200. if signingKey == "" {
  201. return false, ""
  202. }
  203. var gitRepo *git.Repository
  204. var err error
  205. for _, rule := range rules {
  206. switch rule {
  207. case never:
  208. return false, ""
  209. case always:
  210. break
  211. case pubkey:
  212. keys, err := ListGPGKeys(u.ID)
  213. if err != nil || len(keys) == 0 {
  214. return false, ""
  215. }
  216. case twofa:
  217. twofa, err := GetTwoFactorByUID(u.ID)
  218. if err != nil || twofa == nil {
  219. return false, ""
  220. }
  221. case baseSigned:
  222. if gitRepo == nil {
  223. gitRepo, err = git.OpenRepository(tmpBasePath)
  224. if err != nil {
  225. return false, ""
  226. }
  227. defer gitRepo.Close()
  228. }
  229. commit, err := gitRepo.GetCommit(baseCommit)
  230. if err != nil {
  231. return false, ""
  232. }
  233. verification := ParseCommitWithSignature(commit)
  234. if !verification.Verified {
  235. return false, ""
  236. }
  237. case headSigned:
  238. if gitRepo == nil {
  239. gitRepo, err = git.OpenRepository(tmpBasePath)
  240. if err != nil {
  241. return false, ""
  242. }
  243. defer gitRepo.Close()
  244. }
  245. commit, err := gitRepo.GetCommit(headCommit)
  246. if err != nil {
  247. return false, ""
  248. }
  249. verification := ParseCommitWithSignature(commit)
  250. if !verification.Verified {
  251. return false, ""
  252. }
  253. case commitsSigned:
  254. if gitRepo == nil {
  255. gitRepo, err = git.OpenRepository(tmpBasePath)
  256. if err != nil {
  257. return false, ""
  258. }
  259. defer gitRepo.Close()
  260. }
  261. commit, err := gitRepo.GetCommit(headCommit)
  262. if err != nil {
  263. return false, ""
  264. }
  265. verification := ParseCommitWithSignature(commit)
  266. if !verification.Verified {
  267. return false, ""
  268. }
  269. // need to work out merge-base
  270. mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit)
  271. if err != nil {
  272. return false, ""
  273. }
  274. commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit)
  275. if err != nil {
  276. return false, ""
  277. }
  278. for e := commitList.Front(); e != nil; e = e.Next() {
  279. commit = e.Value.(*git.Commit)
  280. verification := ParseCommitWithSignature(commit)
  281. if !verification.Verified {
  282. return false, ""
  283. }
  284. }
  285. }
  286. }
  287. return true, signingKey
  288. }