@ -3,11 +3,14 @@
# include "config.h"
# include "run-command.h"
# include "strbuf.h"
# include "dir.h"
# include "gpg-interface.h"
# include "sigchain.h"
# include "tempfile.h"
# include "alias.h"
static char * configured_signing_key ;
static const char * ssh_default_key_command , * ssh_allowed_signers , * ssh_revocation_file ;
static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED ;
struct gpg_format {
@ -15,6 +18,14 @@ struct gpg_format {
const char * program ;
const char * * verify_args ;
const char * * sigs ;
int ( * verify_signed_buffer ) ( struct signature_check * sigc ,
struct gpg_format * fmt , const char * payload ,
size_t payload_size , const char * signature ,
size_t signature_size ) ;
int ( * sign_buffer ) ( struct strbuf * buffer , struct strbuf * signature ,
const char * signing_key ) ;
const char * ( * get_default_key ) ( void ) ;
const char * ( * get_key_id ) ( void ) ;
} ;
static const char * openpgp_verify_args [ ] = {
@ -35,14 +46,59 @@ static const char *x509_sigs[] = {
NULL
} ;
static const char * ssh_verify_args [ ] = { NULL } ;
static const char * ssh_sigs [ ] = {
" -----BEGIN SSH SIGNATURE----- " ,
NULL
} ;
static int verify_gpg_signed_buffer ( struct signature_check * sigc ,
struct gpg_format * fmt , const char * payload ,
size_t payload_size , const char * signature ,
size_t signature_size ) ;
static int verify_ssh_signed_buffer ( struct signature_check * sigc ,
struct gpg_format * fmt , const char * payload ,
size_t payload_size , const char * signature ,
size_t signature_size ) ;
static int sign_buffer_gpg ( struct strbuf * buffer , struct strbuf * signature ,
const char * signing_key ) ;
static int sign_buffer_ssh ( struct strbuf * buffer , struct strbuf * signature ,
const char * signing_key ) ;
static const char * get_default_ssh_signing_key ( void ) ;
static const char * get_ssh_key_id ( void ) ;
static struct gpg_format gpg_format [ ] = {
{ . name = " openpgp " , . program = " gpg " ,
. verify_args = openpgp_verify_args ,
. sigs = openpgp_sigs
{
. name = " openpgp " ,
. program = " gpg " ,
. verify_args = openpgp_verify_args ,
. sigs = openpgp_sigs ,
. verify_signed_buffer = verify_gpg_signed_buffer ,
. sign_buffer = sign_buffer_gpg ,
. get_default_key = NULL ,
. get_key_id = NULL ,
} ,
{
. name = " x509 " ,
. program = " gpgsm " ,
. verify_args = x509_verify_args ,
. sigs = x509_sigs ,
. verify_signed_buffer = verify_gpg_signed_buffer ,
. sign_buffer = sign_buffer_gpg ,
. get_default_key = NULL ,
. get_key_id = NULL ,
} ,
{ . name = " x509 " , . program = " gpgsm " ,
. verify_args = x509_verify_args ,
. sigs = x509_sigs
{
. name = " ssh " ,
. program = " ssh-keygen " ,
. verify_args = ssh_verify_args ,
. sigs = ssh_sigs ,
. verify_signed_buffer = verify_ssh_signed_buffer ,
. sign_buffer = sign_buffer_ssh ,
. get_default_key = get_default_ssh_signing_key ,
. get_key_id = get_ssh_key_id ,
} ,
} ;
@ -72,7 +128,7 @@ static struct gpg_format *get_format_by_sig(const char *sig)
void signature_check_clear ( struct signature_check * sigc )
{
FREE_AND_NULL ( sigc - > payload ) ;
FREE_AND_NULL ( sigc - > gpg_ output) ;
FREE_AND_NULL ( sigc - > output ) ;
FREE_AND_NULL ( sigc - > gpg_status ) ;
FREE_AND_NULL ( sigc - > signer ) ;
FREE_AND_NULL ( sigc - > key ) ;
@ -257,16 +313,16 @@ error:
FREE_AND_NULL ( sigc - > key ) ;
}
static int verify_signed_buffer ( const char * payload , size_t payload_size ,
const char * signature , size_t signature_size ,
struct strbuf * gpg_output ,
struct strbuf * gpg_status )
static int verify_gpg_ signed_buffer ( struct signature_check * sigc ,
struct gpg_format * fmt , const char * payload ,
size_t payload_size , const char * signature ,
size_t signature_size )
{
struct child_process gpg = CHILD_PROCESS_INIT ;
struct gpg_format * fmt ;
struct tempfile * temp ;
int ret ;
struct strbuf buf = STRBUF_INIT ;
struct strbuf gpg_stdout = STRBUF_INIT ;
struct strbuf gpg_stderr = STRBUF_INIT ;
temp = mks_tempfile_t ( " .git_vtag_tmpXXXXXX " ) ;
if ( ! temp )
@ -279,10 +335,6 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
return - 1 ;
}
fmt = get_format_by_sig ( signature ) ;
if ( ! fmt )
BUG ( " bad signature '%s' " , signature ) ;
strvec_push ( & gpg . args , fmt - > program ) ;
strvec_pushv ( & gpg . args , fmt - > verify_args ) ;
strvec_pushl ( & gpg . args ,
@ -290,18 +342,216 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
" --verify " , temp - > filename . buf , " - " ,
NULL ) ;
if ( ! gpg_status )
gpg_status = & buf ;
sigchain_push ( SIGPIPE , SIG_IGN ) ;
ret = pipe_command ( & gpg , payload , payload_size ,
gpg_status , 0 , gpg_output , 0 ) ;
ret = pipe_command ( & gpg , payload , payload_size , & gpg_stdout , 0 ,
& gpg_stderr , 0 ) ;
sigchain_pop ( SIGPIPE ) ;
delete_tempfile ( & temp ) ;
ret | = ! strstr ( gpg_status - > buf , " \n [GNUPG:] GOODSIG " ) ;
strbuf_release ( & buf ) ; /* no matter it was used or not */
ret | = ! strstr ( gpg_stdout . buf , " \n [GNUPG:] GOODSIG " ) ;
sigc - > payload = xmemdupz ( payload , payload_size ) ;
sigc - > output = strbuf_detach ( & gpg_stderr , NULL ) ;
sigc - > gpg_status = strbuf_detach ( & gpg_stdout , NULL ) ;
parse_gpg_output ( sigc ) ;
strbuf_release ( & gpg_stdout ) ;
strbuf_release ( & gpg_stderr ) ;
return ret ;
}
static void parse_ssh_output ( struct signature_check * sigc )
{
const char * line , * principal , * search ;
char * key = NULL ;
/*
* ssh - keygen output should be :
* Good " git " signature for PRINCIPAL with RSA key SHA256 : FINGERPRINT
*
* or for valid but unknown keys :
* Good " git " signature with RSA key SHA256 : FINGERPRINT
*
* Note that " PRINCIPAL " can contain whitespace , " RSA " and
* " SHA256 " part could be a different token that names of
* the algorithms used , and " FINGERPRINT " is a hexadecimal
* string . By finding the last occurence of " with " , we can
* reliably parse out the PRINCIPAL .
*/
sigc - > result = ' B ' ;
sigc - > trust_level = TRUST_NEVER ;
line = xmemdupz ( sigc - > output , strcspn ( sigc - > output , " \n " ) ) ;
if ( skip_prefix ( line , " Good \" git \" signature for " , & line ) ) {
/* Valid signature and known principal */
sigc - > result = ' G ' ;
sigc - > trust_level = TRUST_FULLY ;
/* Search for the last "with" to get the full principal */
principal = line ;
do {
search = strstr ( line , " with " ) ;
if ( search )
line = search + 1 ;
} while ( search ! = NULL ) ;
sigc - > signer = xmemdupz ( principal , line - principal - 1 ) ;
} else if ( skip_prefix ( line , " Good \" git \" signature with " , & line ) ) {
/* Valid signature, but key unknown */
sigc - > result = ' G ' ;
sigc - > trust_level = TRUST_UNDEFINED ;
} else {
return ;
}
key = strstr ( line , " key " ) ;
if ( key ) {
sigc - > fingerprint = xstrdup ( strstr ( line , " key " ) + 4 ) ;
sigc - > key = xstrdup ( sigc - > fingerprint ) ;
} else {
/*
* Output did not match what we expected
* Treat the signature as bad
*/
sigc - > result = ' B ' ;
}
}
static int verify_ssh_signed_buffer ( struct signature_check * sigc ,
struct gpg_format * fmt , const char * payload ,
size_t payload_size , const char * signature ,
size_t signature_size )
{
struct child_process ssh_keygen = CHILD_PROCESS_INIT ;
struct tempfile * buffer_file ;
int ret = - 1 ;
const char * line ;
size_t trust_size ;
char * principal ;
struct strbuf ssh_principals_out = STRBUF_INIT ;
struct strbuf ssh_principals_err = STRBUF_INIT ;
struct strbuf ssh_keygen_out = STRBUF_INIT ;
struct strbuf ssh_keygen_err = STRBUF_INIT ;
if ( ! ssh_allowed_signers ) {
error ( _ ( " gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification " ) ) ;
return - 1 ;
}
buffer_file = mks_tempfile_t ( " .git_vtag_tmpXXXXXX " ) ;
if ( ! buffer_file )
return error_errno ( _ ( " could not create temporary file " ) ) ;
if ( write_in_full ( buffer_file - > fd , signature , signature_size ) < 0 | |
close_tempfile_gently ( buffer_file ) < 0 ) {
error_errno ( _ ( " failed writing detached signature to '%s' " ) ,
buffer_file - > filename . buf ) ;
delete_tempfile ( & buffer_file ) ;
return - 1 ;
}
/* Find the principal from the signers */
strvec_pushl ( & ssh_keygen . args , fmt - > program ,
" -Y " , " find-principals " ,
" -f " , ssh_allowed_signers ,
" -s " , buffer_file - > filename . buf ,
NULL ) ;
ret = pipe_command ( & ssh_keygen , NULL , 0 , & ssh_principals_out , 0 ,
& ssh_principals_err , 0 ) ;
if ( ret & & strstr ( ssh_principals_err . buf , " usage: " ) ) {
error ( _ ( " ssh-keygen -Y find-principals/verify is needed for ssh signature verification (available in openssh version 8.2p1+) " ) ) ;
goto out ;
}
if ( ret | | ! ssh_principals_out . len ) {
/*
* We did not find a matching principal in the allowedSigners
* Check without validation
*/
child_process_init ( & ssh_keygen ) ;
strvec_pushl ( & ssh_keygen . args , fmt - > program ,
" -Y " , " check-novalidate " ,
" -n " , " git " ,
" -s " , buffer_file - > filename . buf ,
NULL ) ;
pipe_command ( & ssh_keygen , payload , payload_size ,
& ssh_keygen_out , 0 , & ssh_keygen_err , 0 ) ;
/*
* Fail on unknown keys
* we still call check - novalidate to display the signature info
*/
ret = - 1 ;
} else {
/* Check every principal we found (one per line) */
for ( line = ssh_principals_out . buf ; * line ;
line = strchrnul ( line + 1 , ' \n ' ) ) {
while ( * line = = ' \n ' )
line + + ;
if ( ! * line )
break ;
trust_size = strcspn ( line , " \n " ) ;
principal = xmemdupz ( line , trust_size ) ;
child_process_init ( & ssh_keygen ) ;
strbuf_release ( & ssh_keygen_out ) ;
strbuf_release ( & ssh_keygen_err ) ;
strvec_push ( & ssh_keygen . args , fmt - > program ) ;
/*
* We found principals
* Try with each until we find a match
*/
strvec_pushl ( & ssh_keygen . args , " -Y " , " verify " ,
" -n " , " git " ,
" -f " , ssh_allowed_signers ,
" -I " , principal ,
" -s " , buffer_file - > filename . buf ,
NULL ) ;
if ( ssh_revocation_file ) {
if ( file_exists ( ssh_revocation_file ) ) {
strvec_pushl ( & ssh_keygen . args , " -r " ,
ssh_revocation_file , NULL ) ;
} else {
warning ( _ ( " ssh signing revocation file configured but not found: %s " ) ,
ssh_revocation_file ) ;
}
}
sigchain_push ( SIGPIPE , SIG_IGN ) ;
ret = pipe_command ( & ssh_keygen , payload , payload_size ,
& ssh_keygen_out , 0 , & ssh_keygen_err , 0 ) ;
sigchain_pop ( SIGPIPE ) ;
FREE_AND_NULL ( principal ) ;
if ( ! ret )
ret = ! starts_with ( ssh_keygen_out . buf , " Good " ) ;
if ( ! ret )
break ;
}
}
sigc - > payload = xmemdupz ( payload , payload_size ) ;
strbuf_stripspace ( & ssh_keygen_out , 0 ) ;
strbuf_stripspace ( & ssh_keygen_err , 0 ) ;
/* Add stderr outputs to show the user actual ssh-keygen errors */
strbuf_add ( & ssh_keygen_out , ssh_principals_err . buf , ssh_principals_err . len ) ;
strbuf_add ( & ssh_keygen_out , ssh_keygen_err . buf , ssh_keygen_err . len ) ;
sigc - > output = strbuf_detach ( & ssh_keygen_out , NULL ) ;
sigc - > gpg_status = xstrdup ( sigc - > output ) ;
parse_ssh_output ( sigc ) ;
out :
if ( buffer_file )
delete_tempfile ( & buffer_file ) ;
strbuf_release ( & ssh_principals_out ) ;
strbuf_release ( & ssh_principals_err ) ;
strbuf_release ( & ssh_keygen_out ) ;
strbuf_release ( & ssh_keygen_err ) ;
return ret ;
}
@ -309,35 +559,32 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
int check_signature ( const char * payload , size_t plen , const char * signature ,
size_t slen , struct signature_check * sigc )
{
struct strbuf gpg_output = STRBUF_INIT ;
struct strbuf gpg_status = STRBUF_INIT ;
struct gpg_format * fmt ;
int status ;
sigc - > result = ' N ' ;
sigc - > trust_level = - 1 ;
status = verify_signed_buffer ( payload , plen , signature , slen ,
& gpg_output , & gpg_status ) ;
if ( status & & ! gpg_output . len )
goto out ;
sigc - > payload = xmemdupz ( payload , plen ) ;
sigc - > gpg_output = strbuf_detach ( & gpg_output , NULL ) ;
sigc - > gpg_status = strbuf_detach ( & gpg_status , NULL ) ;
parse_gpg_output ( sigc ) ;
fmt = get_format_by_sig ( signature ) ;
if ( ! fmt )
die ( _ ( " bad/incompatible signature '%s' " ) , signature ) ;
status = fmt - > verify_signed_buffer ( sigc , fmt , payload , plen , signature ,
slen ) ;
if ( status & & ! sigc - > output )
return ! ! status ;
status | = sigc - > result ! = ' G ' ;
status | = sigc - > trust_level < configured_min_trust_level ;
out :
strbuf_release ( & gpg_status ) ;
strbuf_release ( & gpg_output ) ;
return ! ! status ;
}
void print_signature_buffer ( const struct signature_check * sigc , unsigned flags )
{
const char * output = flags & GPG_VERIFY_RAW ?
sigc - > gpg_status : sigc - > gpg_ output;
const char * output = flags & GPG_VERIFY_RAW ? sigc - > gpg_status :
sigc - > output ;
if ( flags & GPG_VERIFY_VERBOSE & & sigc - > payload )
fputs ( sigc - > payload , stdout ) ;
@ -419,12 +666,33 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return 0 ;
}
if ( ! strcmp ( var , " gpg.ssh.defaultkeycommand " ) ) {
if ( ! value )
return config_error_nonbool ( var ) ;
return git_config_string ( & ssh_default_key_command , var , value ) ;
}
if ( ! strcmp ( var , " gpg.ssh.allowedsignersfile " ) ) {
if ( ! value )
return config_error_nonbool ( var ) ;
return git_config_pathname ( & ssh_allowed_signers , var , value ) ;
}
if ( ! strcmp ( var , " gpg.ssh.revocationfile " ) ) {
if ( ! value )
return config_error_nonbool ( var ) ;
return git_config_pathname ( & ssh_revocation_file , var , value ) ;
}
if ( ! strcmp ( var , " gpg.program " ) | | ! strcmp ( var , " gpg.openpgp.program " ) )
fmtname = " openpgp " ;
if ( ! strcmp ( var , " gpg.x509.program " ) )
fmtname = " x509 " ;
if ( ! strcmp ( var , " gpg.ssh.program " ) )
fmtname = " ssh " ;
if ( fmtname ) {
fmt = get_format_by_name ( fmtname ) ;
return git_config_string ( & fmt - > program , var , value ) ;
@ -433,18 +701,144 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return 0 ;
}
static char * get_ssh_key_fingerprint ( const char * signing_key )
{
struct child_process ssh_keygen = CHILD_PROCESS_INIT ;
int ret = - 1 ;
struct strbuf fingerprint_stdout = STRBUF_INIT ;
struct strbuf * * fingerprint ;
/*
* With SSH Signing this can contain a filename or a public key
* For textual representation we usually want a fingerprint
*/
if ( starts_with ( signing_key , " ssh- " ) ) {
strvec_pushl ( & ssh_keygen . args , " ssh-keygen " , " -lf " , " - " , NULL ) ;
ret = pipe_command ( & ssh_keygen , signing_key ,
strlen ( signing_key ) , & fingerprint_stdout , 0 ,
NULL , 0 ) ;
} else {
strvec_pushl ( & ssh_keygen . args , " ssh-keygen " , " -lf " ,
configured_signing_key , NULL ) ;
ret = pipe_command ( & ssh_keygen , NULL , 0 , & fingerprint_stdout , 0 ,
NULL , 0 ) ;
}
if ( ! ! ret )
die_errno ( _ ( " failed to get the ssh fingerprint for key '%s' " ) ,
signing_key ) ;
fingerprint = strbuf_split_max ( & fingerprint_stdout , ' ' , 3 ) ;
if ( ! fingerprint [ 1 ] )
die_errno ( _ ( " failed to get the ssh fingerprint for key '%s' " ) ,
signing_key ) ;
return strbuf_detach ( fingerprint [ 1 ] , NULL ) ;
}
/* Returns the first public key from an ssh-agent to use for signing */
static const char * get_default_ssh_signing_key ( void )
{
struct child_process ssh_default_key = CHILD_PROCESS_INIT ;
int ret = - 1 ;
struct strbuf key_stdout = STRBUF_INIT , key_stderr = STRBUF_INIT ;
struct strbuf * * keys ;
char * key_command = NULL ;
const char * * argv ;
int n ;
char * default_key = NULL ;
if ( ! ssh_default_key_command )
die ( _ ( " either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured " ) ) ;
key_command = xstrdup ( ssh_default_key_command ) ;
n = split_cmdline ( key_command , & argv ) ;
if ( n < 0 )
die ( " malformed build-time gpg.ssh.defaultKeyCommand: %s " ,
split_cmdline_strerror ( n ) ) ;
strvec_pushv ( & ssh_default_key . args , argv ) ;
ret = pipe_command ( & ssh_default_key , NULL , 0 , & key_stdout , 0 ,
& key_stderr , 0 ) ;
if ( ! ret ) {
keys = strbuf_split_max ( & key_stdout , ' \n ' , 2 ) ;
if ( keys [ 0 ] & & starts_with ( keys [ 0 ] - > buf , " ssh- " ) ) {
default_key = strbuf_detach ( keys [ 0 ] , NULL ) ;
} else {
warning ( _ ( " gpg.ssh.defaultKeycommand succeeded but returned no keys: %s %s " ) ,
key_stderr . buf , key_stdout . buf ) ;
}
strbuf_list_free ( keys ) ;
} else {
warning ( _ ( " gpg.ssh.defaultKeyCommand failed: %s %s " ) ,
key_stderr . buf , key_stdout . buf ) ;
}
free ( key_command ) ;
free ( argv ) ;
strbuf_release ( & key_stdout ) ;
return default_key ;
}
static const char * get_ssh_key_id ( void ) {
return get_ssh_key_fingerprint ( get_signing_key ( ) ) ;
}
/* Returns a textual but unique representation of the signing key */
const char * get_signing_key_id ( void )
{
if ( use_format - > get_key_id ) {
return use_format - > get_key_id ( ) ;
}
/* GPG/GPGSM only store a key id on this variable */
return get_signing_key ( ) ;
}
const char * get_signing_key ( void )
{
if ( configured_signing_key )
return configured_signing_key ;
return git_committer_info ( IDENT_STRICT | IDENT_NO_DATE ) ;
if ( use_format - > get_default_key ) {
return use_format - > get_default_key ( ) ;
}
return git_committer_info ( IDENT_STRICT | IDENT_NO_DATE ) ;
}
int sign_buffer ( struct strbuf * buffer , struct strbuf * signature , const char * signing_key )
{
return use_format - > sign_buffer ( buffer , signature , signing_key ) ;
}
/*
* Strip CR from the line endings , in case we are on Windows .
* NEEDSWORK : make it trim only CRs before LFs and rename
*/
static void remove_cr_after ( struct strbuf * buffer , size_t offset )
{
size_t i , j ;
for ( i = j = offset ; i < buffer - > len ; i + + ) {
if ( buffer - > buf [ i ] ! = ' \r ' ) {
if ( i ! = j )
buffer - > buf [ j ] = buffer - > buf [ i ] ;
j + + ;
}
}
strbuf_setlen ( buffer , j ) ;
}
static int sign_buffer_gpg ( struct strbuf * buffer , struct strbuf * signature ,
const char * signing_key )
{
struct child_process gpg = CHILD_PROCESS_INIT ;
int ret ;
size_t i , j , bottom ;
size_t bottom ;
struct strbuf gpg_status = STRBUF_INIT ;
strvec_pushl ( & gpg . args ,
@ -470,13 +864,98 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
return error ( _ ( " gpg failed to sign the data " ) ) ;
/* Strip CR from the line endings, in case we are on Windows. */
for ( i = j = bottom ; i < signature - > len ; i + + )
if ( signature - > buf [ i ] ! = ' \r ' ) {
if ( i ! = j )
signature - > buf [ j ] = signature - > buf [ i ] ;
j + + ;
}
strbuf_setlen ( signature , j ) ;
remove_cr_after ( signature , bottom ) ;
return 0 ;
}
static int sign_buffer_ssh ( struct strbuf * buffer , struct strbuf * signature ,
const char * signing_key )
{
struct child_process signer = CHILD_PROCESS_INIT ;
int ret = - 1 ;
size_t bottom , keylen ;
struct strbuf signer_stderr = STRBUF_INIT ;
struct tempfile * key_file = NULL , * buffer_file = NULL ;
char * ssh_signing_key_file = NULL ;
struct strbuf ssh_signature_filename = STRBUF_INIT ;
if ( ! signing_key | | signing_key [ 0 ] = = ' \0 ' )
return error (
_ ( " user.signingkey needs to be set for ssh signing " ) ) ;
if ( starts_with ( signing_key , " ssh- " ) ) {
/* A literal ssh key */
key_file = mks_tempfile_t ( " .git_signing_key_tmpXXXXXX " ) ;
if ( ! key_file )
return error_errno (
_ ( " could not create temporary file " ) ) ;
keylen = strlen ( signing_key ) ;
if ( write_in_full ( key_file - > fd , signing_key , keylen ) < 0 | |
close_tempfile_gently ( key_file ) < 0 ) {
error_errno ( _ ( " failed writing ssh signing key to '%s' " ) ,
key_file - > filename . buf ) ;
goto out ;
}
ssh_signing_key_file = strbuf_detach ( & key_file - > filename , NULL ) ;
} else {
/* We assume a file */
ssh_signing_key_file = expand_user_path ( signing_key , 1 ) ;
}
buffer_file = mks_tempfile_t ( " .git_signing_buffer_tmpXXXXXX " ) ;
if ( ! buffer_file ) {
error_errno ( _ ( " could not create temporary file " ) ) ;
goto out ;
}
if ( write_in_full ( buffer_file - > fd , buffer - > buf , buffer - > len ) < 0 | |
close_tempfile_gently ( buffer_file ) < 0 ) {
error_errno ( _ ( " failed writing ssh signing key buffer to '%s' " ) ,
buffer_file - > filename . buf ) ;
goto out ;
}
strvec_pushl ( & signer . args , use_format - > program ,
" -Y " , " sign " ,
" -n " , " git " ,
" -f " , ssh_signing_key_file ,
buffer_file - > filename . buf ,
NULL ) ;
sigchain_push ( SIGPIPE , SIG_IGN ) ;
ret = pipe_command ( & signer , NULL , 0 , NULL , 0 , & signer_stderr , 0 ) ;
sigchain_pop ( SIGPIPE ) ;
if ( ret ) {
if ( strstr ( signer_stderr . buf , " usage: " ) )
error ( _ ( " ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+) " ) ) ;
error ( " %s " , signer_stderr . buf ) ;
goto out ;
}
bottom = signature - > len ;
strbuf_addbuf ( & ssh_signature_filename , & buffer_file - > filename ) ;
strbuf_addstr ( & ssh_signature_filename , " .sig " ) ;
if ( strbuf_read_file ( signature , ssh_signature_filename . buf , 0 ) < 0 ) {
error_errno (
_ ( " failed reading ssh signing data buffer from '%s' " ) ,
ssh_signature_filename . buf ) ;
}
unlink_or_warn ( ssh_signature_filename . buf ) ;
/* Strip CR from the line endings, in case we are on Windows. */
remove_cr_after ( signature , bottom ) ;
out :
if ( key_file )
delete_tempfile ( & key_file ) ;
if ( buffer_file )
delete_tempfile ( & buffer_file ) ;
strbuf_release ( & signer_stderr ) ;
strbuf_release ( & ssh_signature_filename ) ;
FREE_AND_NULL ( ssh_signing_key_file ) ;
return ret ;
}