2019-10-16 16:42:42 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"strings"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
)
type signingMode string
const (
never signingMode = "never"
always signingMode = "always"
pubkey signingMode = "pubkey"
twofa signingMode = "twofa"
parentSigned signingMode = "parentsigned"
baseSigned signingMode = "basesigned"
headSigned signingMode = "headsigned"
commitsSigned signingMode = "commitssigned"
2019-12-15 14:06:31 +03:00
approved signingMode = "approved"
2020-01-15 11:32:57 +03:00
noKey signingMode = "nokey"
2019-10-16 16:42:42 +03:00
)
func signingModeFromStrings ( modeStrings [ ] string ) [ ] signingMode {
returnable := make ( [ ] signingMode , 0 , len ( modeStrings ) )
for _ , mode := range modeStrings {
2020-09-19 19:44:55 +03:00
signMode := signingMode ( strings . ToLower ( strings . TrimSpace ( mode ) ) )
2019-10-16 16:42:42 +03:00
switch signMode {
case never :
return [ ] signingMode { never }
case always :
return [ ] signingMode { always }
case pubkey :
fallthrough
case twofa :
fallthrough
case parentSigned :
fallthrough
case baseSigned :
fallthrough
case headSigned :
fallthrough
2019-12-15 14:06:31 +03:00
case approved :
fallthrough
2019-10-16 16:42:42 +03:00
case commitsSigned :
returnable = append ( returnable , signMode )
}
}
if len ( returnable ) == 0 {
return [ ] signingMode { never }
}
return returnable
}
2020-09-19 19:44:55 +03:00
// SigningKey returns the KeyID and git Signature for the repo
func SigningKey ( repoPath string ) ( string , * git . Signature ) {
2019-10-16 16:42:42 +03:00
if setting . Repository . Signing . SigningKey == "none" {
2020-09-19 19:44:55 +03:00
return "" , nil
2019-10-16 16:42:42 +03:00
}
if setting . Repository . Signing . SigningKey == "default" || setting . Repository . Signing . SigningKey == "" {
// Can ignore the error here as it means that commit.gpgsign is not set
value , _ := git . NewCommand ( "config" , "--get" , "commit.gpgsign" ) . RunInDir ( repoPath )
sign , valid := git . ParseBool ( strings . TrimSpace ( value ) )
if ! sign || ! valid {
2020-09-19 19:44:55 +03:00
return "" , nil
2019-10-16 16:42:42 +03:00
}
signingKey , _ := git . NewCommand ( "config" , "--get" , "user.signingkey" ) . RunInDir ( repoPath )
2020-09-19 19:44:55 +03:00
signingName , _ := git . NewCommand ( "config" , "--get" , "user.name" ) . RunInDir ( repoPath )
signingEmail , _ := git . NewCommand ( "config" , "--get" , "user.email" ) . RunInDir ( repoPath )
return strings . TrimSpace ( signingKey ) , & git . Signature {
Name : strings . TrimSpace ( signingName ) ,
Email : strings . TrimSpace ( signingEmail ) ,
}
2019-10-16 16:42:42 +03:00
}
2020-09-19 19:44:55 +03:00
return setting . Repository . Signing . SigningKey , & git . Signature {
Name : setting . Repository . Signing . SigningName ,
Email : setting . Repository . Signing . SigningEmail ,
}
2019-10-16 16:42:42 +03:00
}
// PublicSigningKey gets the public signing key within a provided repository directory
func PublicSigningKey ( repoPath string ) ( string , error ) {
2020-09-19 19:44:55 +03:00
signingKey , _ := SigningKey ( repoPath )
2019-10-16 16:42:42 +03:00
if signingKey == "" {
return "" , nil
}
content , stderr , err := process . GetManager ( ) . ExecDir ( - 1 , repoPath ,
"gpg --export -a" , "gpg" , "--export" , "-a" , signingKey )
if err != nil {
log . Error ( "Unable to get default signing key in %s: %s, %s, %v" , repoPath , signingKey , stderr , err )
return "" , err
}
return content , nil
}
// SignInitialCommit determines if we should sign the initial commit to this repository
2020-09-19 19:44:55 +03:00
func SignInitialCommit ( repoPath string , u * User ) ( bool , string , * git . Signature , error ) {
2019-10-16 16:42:42 +03:00
rules := signingModeFromStrings ( setting . Repository . Signing . InitialCommit )
2020-09-19 19:44:55 +03:00
signingKey , sig := SigningKey ( repoPath )
2019-10-16 16:42:42 +03:00
if signingKey == "" {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { noKey }
2019-10-16 16:42:42 +03:00
}
2020-07-06 15:07:07 +03:00
Loop :
2019-10-16 16:42:42 +03:00
for _ , rule := range rules {
switch rule {
case never :
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { never }
2019-10-16 16:42:42 +03:00
case always :
2020-07-06 15:07:07 +03:00
break Loop
2019-10-16 16:42:42 +03:00
case pubkey :
2020-01-24 22:00:29 +03:00
keys , err := ListGPGKeys ( u . ID , ListOptions { } )
2020-01-15 11:32:57 +03:00
if err != nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2020-01-15 11:32:57 +03:00
}
if len ( keys ) == 0 {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { pubkey }
2019-10-16 16:42:42 +03:00
}
case twofa :
2020-01-15 11:32:57 +03:00
twofaModel , err := GetTwoFactorByUID ( u . ID )
2020-01-27 02:44:12 +03:00
if err != nil && ! IsErrTwoFactorNotEnrolled ( err ) {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2020-01-15 11:32:57 +03:00
}
if twofaModel == nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { twofa }
2019-10-16 16:42:42 +03:00
}
}
}
2020-09-19 19:44:55 +03:00
return true , signingKey , sig , nil
2019-10-16 16:42:42 +03:00
}
// SignWikiCommit determines if we should sign the commits to this repository wiki
2020-09-19 19:44:55 +03:00
func ( repo * Repository ) SignWikiCommit ( u * User ) ( bool , string , * git . Signature , error ) {
2019-10-16 16:42:42 +03:00
rules := signingModeFromStrings ( setting . Repository . Signing . Wiki )
2020-09-19 19:44:55 +03:00
signingKey , sig := SigningKey ( repo . WikiPath ( ) )
2019-10-16 16:42:42 +03:00
if signingKey == "" {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { noKey }
2019-10-16 16:42:42 +03:00
}
2020-07-06 15:07:07 +03:00
Loop :
2019-10-16 16:42:42 +03:00
for _ , rule := range rules {
switch rule {
case never :
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { never }
2019-10-16 16:42:42 +03:00
case always :
2020-07-06 15:07:07 +03:00
break Loop
2019-10-16 16:42:42 +03:00
case pubkey :
2020-01-24 22:00:29 +03:00
keys , err := ListGPGKeys ( u . ID , ListOptions { } )
2020-01-15 11:32:57 +03:00
if err != nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2020-01-15 11:32:57 +03:00
}
if len ( keys ) == 0 {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { pubkey }
2019-10-16 16:42:42 +03:00
}
case twofa :
2020-01-15 11:32:57 +03:00
twofaModel , err := GetTwoFactorByUID ( u . ID )
2020-01-27 02:44:12 +03:00
if err != nil && ! IsErrTwoFactorNotEnrolled ( err ) {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2020-01-15 11:32:57 +03:00
}
if twofaModel == nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { twofa }
2019-10-16 16:42:42 +03:00
}
case parentSigned :
gitRepo , err := git . OpenRepository ( repo . WikiPath ( ) )
if err != nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2019-10-16 16:42:42 +03:00
}
2019-11-13 10:01:19 +03:00
defer gitRepo . Close ( )
2019-10-16 16:42:42 +03:00
commit , err := gitRepo . GetCommit ( "HEAD" )
if err != nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2019-10-16 16:42:42 +03:00
}
if commit . Signature == nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
verification := ParseCommitWithSignature ( commit )
if ! verification . Verified {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
}
}
2020-09-19 19:44:55 +03:00
return true , signingKey , sig , nil
2019-10-16 16:42:42 +03:00
}
// SignCRUDAction determines if we should sign a CRUD commit to this repository
2020-09-19 19:44:55 +03:00
func ( repo * Repository ) SignCRUDAction ( u * User , tmpBasePath , parentCommit string ) ( bool , string , * git . Signature , error ) {
2019-10-16 16:42:42 +03:00
rules := signingModeFromStrings ( setting . Repository . Signing . CRUDActions )
2020-09-19 19:44:55 +03:00
signingKey , sig := SigningKey ( repo . RepoPath ( ) )
2019-10-16 16:42:42 +03:00
if signingKey == "" {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { noKey }
2019-10-16 16:42:42 +03:00
}
2020-07-06 15:07:07 +03:00
Loop :
2019-10-16 16:42:42 +03:00
for _ , rule := range rules {
switch rule {
case never :
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { never }
2019-10-16 16:42:42 +03:00
case always :
2020-07-06 15:07:07 +03:00
break Loop
2019-10-16 16:42:42 +03:00
case pubkey :
2020-01-24 22:00:29 +03:00
keys , err := ListGPGKeys ( u . ID , ListOptions { } )
2020-01-15 11:32:57 +03:00
if err != nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2020-01-15 11:32:57 +03:00
}
if len ( keys ) == 0 {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { pubkey }
2019-10-16 16:42:42 +03:00
}
case twofa :
2020-01-15 11:32:57 +03:00
twofaModel , err := GetTwoFactorByUID ( u . ID )
2020-01-27 02:44:12 +03:00
if err != nil && ! IsErrTwoFactorNotEnrolled ( err ) {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2020-01-15 11:32:57 +03:00
}
if twofaModel == nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { twofa }
2019-10-16 16:42:42 +03:00
}
case parentSigned :
gitRepo , err := git . OpenRepository ( tmpBasePath )
if err != nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2019-10-16 16:42:42 +03:00
}
2019-11-13 10:01:19 +03:00
defer gitRepo . Close ( )
2019-10-16 16:42:42 +03:00
commit , err := gitRepo . GetCommit ( parentCommit )
if err != nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , err
2019-10-16 16:42:42 +03:00
}
if commit . Signature == nil {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
verification := ParseCommitWithSignature ( commit )
if ! verification . Verified {
2020-09-19 19:44:55 +03:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
}
}
2020-09-19 19:44:55 +03:00
return true , signingKey , sig , nil
2019-10-16 16:42:42 +03:00
}