@ -6,6 +6,7 @@ package asymkey
import (
"context"
"fmt"
"os"
"strings"
asymkey_model "code.gitea.io/gitea/models/asymkey"
@ -85,9 +86,9 @@ func IsErrWontSign(err error) bool {
}
// SigningKey returns the KeyID and git Signature for the repo
func SigningKey ( ctx context . Context , repoPath string ) ( string , * git . Signature ) {
func SigningKey ( ctx context . Context , repoPath string ) ( * git . SigningKey , * git . Signature ) {
if setting . Repository . Signing . SigningKey == "none" {
return "" , nil
return nil , nil
}
if setting . Repository . Signing . SigningKey == "default" || setting . Repository . Signing . SigningKey == "" {
@ -95,53 +96,77 @@ func SigningKey(ctx context.Context, repoPath string) (string, *git.Signature) {
value , _ , _ := git . NewCommand ( "config" , "--get" , "commit.gpgsign" ) . RunStdString ( ctx , & git . RunOpts { Dir : repoPath } )
sign , valid := git . ParseBool ( strings . TrimSpace ( value ) )
if ! sign || ! valid {
return "" , nil
return nil , nil
}
format , _ , _ := git . NewCommand ( "config" , "--default" , git . SigningKeyFormatOpenPGP , "--get" , "gpg.format" ) . RunStdString ( ctx , & git . RunOpts { Dir : repoPath } )
signingKey , _ , _ := git . NewCommand ( "config" , "--get" , "user.signingkey" ) . RunStdString ( ctx , & git . RunOpts { Dir : repoPath } )
signingName , _ , _ := git . NewCommand ( "config" , "--get" , "user.name" ) . RunStdString ( ctx , & git . RunOpts { Dir : repoPath } )
signingEmail , _ , _ := git . NewCommand ( "config" , "--get" , "user.email" ) . RunStdString ( ctx , & git . RunOpts { Dir : repoPath } )
return strings . TrimSpace ( signingKey ) , & git . Signature {
Name : strings . TrimSpace ( signingName ) ,
Email : strings . TrimSpace ( signingEmail ) ,
if strings . TrimSpace ( signingKey ) == "" {
return nil , nil
}
return & git . SigningKey {
KeyID : strings . TrimSpace ( signingKey ) ,
Format : strings . TrimSpace ( format ) ,
} , & git . Signature {
Name : strings . TrimSpace ( signingName ) ,
Email : strings . TrimSpace ( signingEmail ) ,
}
}
return setting . Repository . Signing . SigningKey , & git . Signature {
Name : setting . Repository . Signing . SigningName ,
Email : setting . Repository . Signing . SigningEmail ,
if setting . Repository . Signing . SigningKey == "" {
return nil , nil
}
return & git . SigningKey {
KeyID : setting . Repository . Signing . SigningKey ,
Format : setting . Repository . Signing . SigningFormat ,
} , & git . Signature {
Name : setting . Repository . Signing . SigningName ,
Email : setting . Repository . Signing . SigningEmail ,
}
}
// PublicSigningKey gets the public signing key within a provided repository directory
func PublicSigningKey ( ctx context . Context , repoPath string ) ( string , error ) {
func PublicSigningKey ( ctx context . Context , repoPath string ) ( content , format string , err error ) {
signingKey , _ := SigningKey ( ctx , repoPath )
if signingKey == "" {
return "" , nil
if signingKey == nil {
return "" , "" , nil
}
if signingKey . Format == git . SigningKeyFormatSSH {
content , err := os . ReadFile ( signingKey . KeyID )
if err != nil {
log . Error ( "Unable to read SSH public key file in %s: %s, %v" , repoPath , signingKey , err )
return "" , signingKey . Format , err
}
return string ( content ) , signingKey . Format , nil
}
content , stderr , err := process . GetManager ( ) . ExecDir ( ctx , - 1 , repoPath ,
"gpg --export -a" , "gpg" , "--export" , "-a" , signingKey )
"gpg --export -a" , "gpg" , "--export" , "-a" , signingKey .KeyID )
if err != nil {
log . Error ( "Unable to get default signing key in %s: %s, %s, %v" , repoPath , signingKey , stderr , err )
return "" , err
return "" , signingKey. Format , err
}
return content , nil
return content , signingKey . Format , nil
}
// SignInitialCommit determines if we should sign the initial commit to this repository
func SignInitialCommit ( ctx context . Context , repoPath string , u * user_model . User ) ( bool , string , * git . Signature , error ) {
func SignInitialCommit ( ctx context . Context , repoPath string , u * user_model . User ) ( bool , * git . SigningKey , * git . Signature , error ) {
rules := signingModeFromStrings ( setting . Repository . Signing . InitialCommit )
signingKey , sig := SigningKey ( ctx , repoPath )
if signingKey == "" {
return false , "" , nil , & ErrWontSign { noKey }
if signingKey == nil {
return false , nil , nil , & ErrWontSign { noKey }
}
Loop :
for _ , rule := range rules {
switch rule {
case never :
return false , "" , nil , & ErrWontSign { never }
return false , nil , nil , & ErrWontSign { never }
case always :
break Loop
case pubkey :
@ -150,18 +175,18 @@ Loop:
IncludeSubKeys : true ,
} )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
if len ( keys ) == 0 {
return false , "" , nil , & ErrWontSign { pubkey }
return false , nil , nil , & ErrWontSign { pubkey }
}
case twofa :
twofaModel , err := auth . GetTwoFactorByUID ( ctx , u . ID )
if err != nil && ! auth . IsErrTwoFactorNotEnrolled ( err ) {
return false , "" , nil , err
return false , nil , nil , err
}
if twofaModel == nil {
return false , "" , nil , & ErrWontSign { twofa }
return false , nil , nil , & ErrWontSign { twofa }
}
}
}
@ -169,19 +194,19 @@ Loop:
}
// SignWikiCommit determines if we should sign the commits to this repository wiki
func SignWikiCommit ( ctx context . Context , repo * repo_model . Repository , u * user_model . User ) ( bool , string , * git . Signature , error ) {
func SignWikiCommit ( ctx context . Context , repo * repo_model . Repository , u * user_model . User ) ( bool , * git . SigningKey , * git . Signature , error ) {
repoWikiPath := repo . WikiPath ( )
rules := signingModeFromStrings ( setting . Repository . Signing . Wiki )
signingKey , sig := SigningKey ( ctx , repoWikiPath )
if signingKey == "" {
return false , "" , nil , & ErrWontSign { noKey }
if signingKey == nil {
return false , nil , nil , & ErrWontSign { noKey }
}
Loop :
for _ , rule := range rules {
switch rule {
case never :
return false , "" , nil , & ErrWontSign { never }
return false , nil , nil , & ErrWontSign { never }
case always :
break Loop
case pubkey :
@ -190,35 +215,35 @@ Loop:
IncludeSubKeys : true ,
} )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
if len ( keys ) == 0 {
return false , "" , nil , & ErrWontSign { pubkey }
return false , nil , nil , & ErrWontSign { pubkey }
}
case twofa :
twofaModel , err := auth . GetTwoFactorByUID ( ctx , u . ID )
if err != nil && ! auth . IsErrTwoFactorNotEnrolled ( err ) {
return false , "" , nil , err
return false , nil , nil , err
}
if twofaModel == nil {
return false , "" , nil , & ErrWontSign { twofa }
return false , nil , nil , & ErrWontSign { twofa }
}
case parentSigned :
gitRepo , err := gitrepo . OpenRepository ( ctx , repo . WikiStorageRepo ( ) )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
defer gitRepo . Close ( )
commit , err := gitRepo . GetCommit ( "HEAD" )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
if commit . Signature == nil {
return false , "" , nil , & ErrWontSign { parentSigned }
return false , nil , nil , & ErrWontSign { parentSigned }
}
verification := ParseCommitWithSignature ( ctx , commit )
if ! verification . Verified {
return false , "" , nil , & ErrWontSign { parentSigned }
return false , nil , nil , & ErrWontSign { parentSigned }
}
}
}
@ -226,18 +251,18 @@ Loop:
}
// SignCRUDAction determines if we should sign a CRUD commit to this repository
func SignCRUDAction ( ctx context . Context , repoPath string , u * user_model . User , tmpBasePath , parentCommit string ) ( bool , string , * git . Signature , error ) {
func SignCRUDAction ( ctx context . Context , repoPath string , u * user_model . User , tmpBasePath , parentCommit string ) ( bool , * git . SigningKey , * git . Signature , error ) {
rules := signingModeFromStrings ( setting . Repository . Signing . CRUDActions )
signingKey , sig := SigningKey ( ctx , repoPath )
if signingKey == "" {
return false , "" , nil , & ErrWontSign { noKey }
if signingKey == nil {
return false , nil , nil , & ErrWontSign { noKey }
}
Loop :
for _ , rule := range rules {
switch rule {
case never :
return false , "" , nil , & ErrWontSign { never }
return false , nil , nil , & ErrWontSign { never }
case always :
break Loop
case pubkey :
@ -246,35 +271,35 @@ Loop:
IncludeSubKeys : true ,
} )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
if len ( keys ) == 0 {
return false , "" , nil , & ErrWontSign { pubkey }
return false , nil , nil , & ErrWontSign { pubkey }
}
case twofa :
twofaModel , err := auth . GetTwoFactorByUID ( ctx , u . ID )
if err != nil && ! auth . IsErrTwoFactorNotEnrolled ( err ) {
return false , "" , nil , err
return false , nil , nil , err
}
if twofaModel == nil {
return false , "" , nil , & ErrWontSign { twofa }
return false , nil , nil , & ErrWontSign { twofa }
}
case parentSigned :
gitRepo , err := git . OpenRepository ( ctx , tmpBasePath )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
defer gitRepo . Close ( )
commit , err := gitRepo . GetCommit ( parentCommit )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
if commit . Signature == nil {
return false , "" , nil , & ErrWontSign { parentSigned }
return false , nil , nil , & ErrWontSign { parentSigned }
}
verification := ParseCommitWithSignature ( ctx , commit )
if ! verification . Verified {
return false , "" , nil , & ErrWontSign { parentSigned }
return false , nil , nil , & ErrWontSign { parentSigned }
}
}
}
@ -282,16 +307,16 @@ Loop:
}
// SignMerge determines if we should sign a PR merge commit to the base repository
func SignMerge ( ctx context . Context , pr * issues_model . PullRequest , u * user_model . User , tmpBasePath , baseCommit , headCommit string ) ( bool , string , * git . Signature , error ) {
func SignMerge ( ctx context . Context , pr * issues_model . PullRequest , u * user_model . User , tmpBasePath , baseCommit , headCommit string ) ( bool , * git . SigningKey , * git . Signature , error ) {
if err := pr . LoadBaseRepo ( ctx ) ; err != nil {
log . Error ( "Unable to get Base Repo for pull request" )
return false , "" , nil , err
return false , nil , nil , err
}
repo := pr . BaseRepo
signingKey , signer := SigningKey ( ctx , repo . RepoPath ( ) )
if signingKey == "" {
return false , "" , nil , & ErrWontSign { noKey }
if signingKey == nil {
return false , nil , nil , & ErrWontSign { noKey }
}
rules := signingModeFromStrings ( setting . Repository . Signing . Merges )
@ -302,7 +327,7 @@ Loop:
for _ , rule := range rules {
switch rule {
case never :
return false , "" , nil , & ErrWontSign { never }
return false , nil , nil , & ErrWontSign { never }
case always :
break Loop
case pubkey :
@ -311,91 +336,91 @@ Loop:
IncludeSubKeys : true ,
} )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
if len ( keys ) == 0 {
return false , "" , nil , & ErrWontSign { pubkey }
return false , nil , nil , & ErrWontSign { pubkey }
}
case twofa :
twofaModel , err := auth . GetTwoFactorByUID ( ctx , u . ID )
if err != nil && ! auth . IsErrTwoFactorNotEnrolled ( err ) {
return false , "" , nil , err
return false , nil , nil , err
}
if twofaModel == nil {
return false , "" , nil , & ErrWontSign { twofa }
return false , nil , nil , & ErrWontSign { twofa }
}
case approved :
protectedBranch , err := git_model . GetFirstMatchProtectedBranchRule ( ctx , repo . ID , pr . BaseBranch )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
if protectedBranch == nil {
return false , "" , nil , & ErrWontSign { approved }
return false , nil , nil , & ErrWontSign { approved }
}
if issues_model . GetGrantedApprovalsCount ( ctx , protectedBranch , pr ) < 1 {
return false , "" , nil , & ErrWontSign { approved }
return false , nil , nil , & ErrWontSign { approved }
}
case baseSigned :
if gitRepo == nil {
gitRepo , err = git . OpenRepository ( ctx , tmpBasePath )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
defer gitRepo . Close ( )
}
commit , err := gitRepo . GetCommit ( baseCommit )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
verification := ParseCommitWithSignature ( ctx , commit )
if ! verification . Verified {
return false , "" , nil , & ErrWontSign { baseSigned }
return false , nil , nil , & ErrWontSign { baseSigned }
}
case headSigned :
if gitRepo == nil {
gitRepo , err = git . OpenRepository ( ctx , tmpBasePath )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
defer gitRepo . Close ( )
}
commit , err := gitRepo . GetCommit ( headCommit )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
verification := ParseCommitWithSignature ( ctx , commit )
if ! verification . Verified {
return false , "" , nil , & ErrWontSign { headSigned }
return false , nil , nil , & ErrWontSign { headSigned }
}
case commitsSigned :
if gitRepo == nil {
gitRepo , err = git . OpenRepository ( ctx , tmpBasePath )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
defer gitRepo . Close ( )
}
commit , err := gitRepo . GetCommit ( headCommit )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
verification := ParseCommitWithSignature ( ctx , commit )
if ! verification . Verified {
return false , "" , nil , & ErrWontSign { commitsSigned }
return false , nil , nil , & ErrWontSign { commitsSigned }
}
// need to work out merge-base
mergeBaseCommit , _ , err := gitRepo . GetMergeBase ( "" , baseCommit , headCommit )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
commitList , err := commit . CommitsBeforeUntil ( mergeBaseCommit )
if err != nil {
return false , "" , nil , err
return false , nil , nil , err
}
for _ , commit := range commitList {
verification := ParseCommitWithSignature ( ctx , commit )
if ! verification . Verified {
return false , "" , nil , & ErrWontSign { commitsSigned }
return false , nil , nil , & ErrWontSign { commitsSigned }
}
}
}