From: Deskin Miller <hidden> Date: 2016-06-15 22:45:41
It struck me a while back when I fetched a new tagged release from git.git that
if I wanted to verify the tag's signature, I'd have to issue another command to
do so. Shouldn't git be able to do that for me automatically, when it fetches
signed tags? Now it does. Also, 'git remote update' gets this for free.
Individual commit messages explain things reasonably well, I hope; here are a
few points for discussion:
-Is refactoring builtin-verify-tag.c the right thing to do?
-Now that the SIGPIPE ignoring is occurring at a lower level, should it be
removed from cmd_verify_tag?
-Output format: good, bad, ugly?
-What to do if a tag is found to have a bad signature?
Deskin Miller (4):
Refactor builtin-verify-tag.c
verify-tag.c: ignore SIGPIPE around gpg invocation
verify-tag.c: suppress gpg output if asked
Make git fetch verify signed tags
Makefile | 2 +
builtin-fetch.c | 25 +++++++++++----
builtin-verify-tag.c | 61 ++----------------------------------
t/t7004-tag.sh | 37 ++++++++++++++++++++++
verify-tag.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++
verify-tag.h | 10 ++++++
6 files changed, 155 insertions(+), 64 deletions(-)
create mode 100644 verify-tag.c
create mode 100644 verify-tag.h
From: Deskin Miller <hidden> Date: 2016-06-15 22:45:41
builtin-verify-tag.c already sets SIG_IGN for SIGPIPE before calling
verify_tag, but new callers of verify_tag_sha1 may not have modified the
signal handler, and shouldn't have to. Save and restore the signal
handler for SIGPIPE around the invocation of gpg.
Signed-off-by: Deskin Miller <redacted>
---
verify-tag.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
@@ -17,6 +18,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)charpath[PATH_MAX],*eol;size_tlen;intfd,ret;+sighandler_tsave_handle;fd=git_mkstemp(path,PATH_MAX,".git_vtag_tmpXXXXXX");if(fd<0)
@@ -40,8 +42,10 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)gpg.argv=args_gpg;gpg.in=-1;args_gpg[2]=path;+save_handle=signal(SIGPIPE,SIG_IGN);if(start_command(&gpg)){unlink(path);+signal(SIGPIPE,save_handle);returnerror("could not run gpg.");}
@@ -50,6 +54,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)ret=finish_command(&gpg);unlink(path);+signal(SIGPIPE,save_handle);returnret;}
From: Deskin Miller <hidden> Date: 2016-06-15 22:45:41
builtin-verify-tag.c didn't expose any of its functionality to be used
internally. Refactor some of it into new verify-tag.c and expose
verify_tag_sha1 able to be called from elsewhere in git.
Signed-off-by: Deskin Miller <redacted>
---
Makefile | 2 +
builtin-verify-tag.c | 61 ++-------------------------------------
verify-tag.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++
verify-tag.h | 10 ++++++
4 files changed, 93 insertions(+), 57 deletions(-)
create mode 100644 verify-tag.c
create mode 100644 verify-tag.h
@@ -7,65 +7,16 @@*/#include"cache.h"#include"builtin.h"-#include"tag.h"-#include"run-command.h"+#include"verify-tag.h"#include<signal.h>staticconstcharbuiltin_verify_tag_usage[]="git verify-tag [-v|--verbose] <tag>...";-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"--staticintrun_gpg_verify(constchar*buf,unsignedlongsize,intverbose)-{-structchild_processgpg;-constchar*args_gpg[]={"gpg","--verify","FILE","-",NULL};-charpath[PATH_MAX],*eol;-size_tlen;-intfd,ret;--fd=git_mkstemp(path,PATH_MAX,".git_vtag_tmpXXXXXX");-if(fd<0)-returnerror("could not create temporary file '%s': %s",-path,strerror(errno));-if(write_in_full(fd,buf,size)<0)-returnerror("failed writing temporary file '%s': %s",-path,strerror(errno));-close(fd);--/* find the length without signature */-len=0;-while(len<size&&prefixcmp(buf+len,PGP_SIGNATURE)){-eol=memchr(buf+len,'\n',size-len);-len+=eol?eol-(buf+len)+1:size-len;-}-if(verbose)-write_in_full(1,buf,len);--memset(&gpg,0,sizeof(gpg));-gpg.argv=args_gpg;-gpg.in=-1;-args_gpg[2]=path;-if(start_command(&gpg)){-unlink(path);-returnerror("could not run gpg.");-}--write_in_full(gpg.in,buf,len);-close(gpg.in);-ret=finish_command(&gpg);--unlink(path);--returnret;-}-staticintverify_tag(constchar*name,intverbose){enumobject_typetype;unsignedcharsha1[20];-char*buf;-unsignedlongsize;intret;if(get_sha1(name,sha1))
@@ -76,13 +27,9 @@ static int verify_tag(const char *name, int verbose)returnerror("%s: cannot verify a non-tag object of type %s.",name,typename(type));-buf=read_sha1_file(sha1,&type,&size);-if(!buf)-returnerror("%s: unable to read file.",name);--ret=run_gpg_verify(buf,size,verbose);--free(buf);+ret=verify_tag_sha1(sha1,verbose);+if(ret)+error("Failed to verify %s.",name);returnret;}
@@ -0,0 +1,77 @@+/*+*Internalsfor"git verify-tag"+*+*Copyright(c)2008DeskinMiller<deskinm@umich.edu>+*+*/+#include"cache.h"+#include"object.h"+#include"run-command.h"++#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"++staticintrun_gpg_verify(constchar*buf,unsignedlongsize,intverbose)+{+structchild_processgpg;+constchar*args_gpg[]={"gpg","--verify","FILE","-",NULL};+charpath[PATH_MAX],*eol;+size_tlen;+intfd,ret;++fd=git_mkstemp(path,PATH_MAX,".git_vtag_tmpXXXXXX");+if(fd<0)+returnerror("could not create temporary file '%s': %s",+path,strerror(errno));+if(write_in_full(fd,buf,size)<0)+returnerror("failed writing temporary file '%s': %s",+path,strerror(errno));+close(fd);++/* find the length without signature */+len=0;+while(len<size&&prefixcmp(buf+len,PGP_SIGNATURE)){+eol=memchr(buf+len,'\n',size-len);+len+=eol?eol-(buf+len)+1:size-len;+}+if(verbose)+write_in_full(1,buf,len);++memset(&gpg,0,sizeof(gpg));+gpg.argv=args_gpg;+gpg.in=-1;+args_gpg[2]=path;+if(start_command(&gpg)){+unlink(path);+returnerror("could not run gpg.");+}++write_in_full(gpg.in,buf,len);+close(gpg.in);+ret=finish_command(&gpg);++unlink(path);++returnret;+}++intverify_tag_sha1(constunsignedchar*sha1,intverbose)+{+enumobject_typetype;+char*buf;+unsignedlongsize;+intret;++type=sha1_object_info(sha1,NULL);+if(type!=OBJ_TAG)+returnerror("Cannot verify a non-tag object of type %s.",+typename(type));++buf=read_sha1_file(sha1,&type,&size);+if(!buf)+returnerror("Cnable to read file.");++ret=run_gpg_verify(buf,size,verbose);++free(buf);+returnret;+}
From: Deskin Miller <hidden> Date: 2016-06-15 22:45:41
Previously, tag verification would output messages from gpg on standard
error. Allow this to be controlled by a parameter to verify_tag_sha1.
Signed-off-by: Deskin Miller <redacted>
---
verify-tag.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
@@ -35,13 +35,15 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)eol=memchr(buf+len,'\n',size-len);len+=eol?eol-(buf+len)+1:size-len;}-if(verbose)+if(verbose==1)write_in_full(1,buf,len);memset(&gpg,0,sizeof(gpg));gpg.argv=args_gpg;gpg.in=-1;args_gpg[2]=path;+if(verbose==-1)+gpg.no_stderr=1;save_handle=signal(SIGPIPE,SIG_IGN);if(start_command(&gpg)){unlink(path);
From: Deskin Miller <hidden> Date: 2016-06-15 22:45:41
When git fetch downloads signed tag objects, make it verify them right
then. This extends the output summary of fetch to include "(good
signature)" for valid tags and "(BAD SIGNATURE)" for invalid tags. If
the user does not have the correct key in the gpg keyring, gpg returns
2, verify_tag_sha1 returns -2 and nothing additional is output about
the tag's validity.
Alternate fetch method 'git remote update' gets this check as well due
to the use of the fetch routines.
Signed-off-by: Deskin Miller <redacted>
---
builtin-fetch.c | 25 ++++++++++++++++++-------
t/t7004-tag.sh | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 7 deletions(-)
From: Johannes Schindelin <hidden> Date: 2016-06-15 22:45:41
Hi,
On Sun, 23 Nov 2008, Deskin Miller wrote:
When git fetch downloads signed tag objects, make it verify them right
then. This extends the output summary of fetch to include "(good
signature)" for valid tags and "(BAD SIGNATURE)" for invalid tags. If
the user does not have the correct key in the gpg keyring, gpg returns
2, verify_tag_sha1 returns -2 and nothing additional is output about the
tag's validity.
This must be turned off by default, IMO. You cannot expect each and every
developer to have gpg _and_ all those public keys installed.
Ciao,
Dscho
From: Johannes Schindelin <hidden> Date: 2016-06-15 22:45:41
Hi,
On Sun, 23 Nov 2008, Deskin Miller wrote:
builtin-verify-tag.c didn't expose any of its functionality to be used
internally. Refactor some of it into new verify-tag.c and expose
verify_tag_sha1 able to be called from elsewhere in git.
Signed-off-by: Deskin Miller <redacted>
---
Makefile | 2 +
builtin-verify-tag.c | 61 ++-------------------------------------
verify-tag.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++
verify-tag.h | 10 ++++++
4 files changed, 93 insertions(+), 57 deletions(-)
create mode 100644 verify-tag.c
create mode 100644 verify-tag.h
I'll comment on the output of "format-patch -n -C -C" instead, as that
makes it much easier to see what you actually did:
diff --git a/builtin-verify-tag.c b/verify-tag.csimilarity index 56%copy from builtin-verify-tag.ccopy to verify-tag.cindex 729a159..c9be331 100644--- a/builtin-verify-tag.c+++ b/verify-tag.c
*
- * Copyright (c) 2007 Carlos Rica [off-list ref]
+ * Copyright (c) 2008 Deskin Miller [off-list ref]
Disagree.
Even if Carlos seemed to stop his work on Git entirely, which I find
disappointing, you are _not_ free to pretend his work is yours. And given
this diff:
@@ -60,52 +54,24 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) return ret; }-static int verify_tag(const char *name, int verbose)+int verify_tag_sha1(const unsigned char *sha1, int verbose) { enum object_type type;- unsigned char sha1[20]; char *buf; unsigned long size; int ret;- if (get_sha1(name, sha1))- return error("tag '%s' not found.", name);- type = sha1_object_info(sha1, NULL); if (type != OBJ_TAG)- return error("%s: cannot verify a non-tag object of type %s.",- name, typename(type));+ return error("Cannot verify a non-tag object of type %s.",+ typename(type)); buf = read_sha1_file(sha1, &type, &size); if (!buf)- return error("%s: unable to read file.", name);+ return error("Cnable to read file."); ret = run_gpg_verify(buf, size, verbose); free(buf); return ret; }--int cmd_verify_tag(int argc, const char **argv, const char *prefix)-{- int i = 1, verbose = 0, had_error = 0;-- git_config(git_default_config, NULL);-- if (argc > 1 &&- (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) {- verbose = 1;- i++;- }-- if (argc <= i)- usage(builtin_verify_tag_usage);-- /* sometimes the program was terminated because this signal- * was received in the process of writing the gpg input: */- signal(SIGPIPE, SIG_IGN);- while (i < argc)- if (verify_tag(argv[i++], verbose))- had_error = 1;- return had_error;-}
I think pretty much all you did was deleting (and thereby you do not gain
any copyright).
Except for one change: why on earth did you think it a good idea to
suppress telling the user the _name_ of the tag when an error occurs?
I, for one, would find it way less than helpful to read
Cannot verify a non-tag object of type blob.
than to read
refs/tags/dscho-key: cannot verify a non-tag object of type blob.
Besides, I do not see where you warn that "tag <name> not found." Changes
like this one need to be justified (by saying in the commit message where
the warning is already issued, and not letting the reviewer/reader leave
wondering).
Please, next time you submit a patch like this, do the -C -C yourself.
Letting all the reviewers do it looks lousy on the overall time balance
sheet, and it may also lead to a potential reviewer preferring to do
something else instead.
Now, Junio already said that he is not (yet) convinced that this change
should be in Git proper, rather than a hook, so it is up to you to decide
if you deem it important enough to try harder to convince people.
I, for one, would think that it may be a good change: AFAIK only hard-core
gits use hooks, everybody else avoids them. So if we deem verifying
signatures important enough, we might want to have better support for it
than some example hooks.
So color me half-convinced.
Ciao,
Dscho
From: Deskin Miller <hidden> Date: 2016-06-15 22:45:42
On Mon, Nov 24, 2008 at 11:41:27AM +0100, Johannes Schindelin wrote:
On Sun, 23 Nov 2008, Deskin Miller wrote:
quoted
-What to do if a tag is found to have a bad signature?
Or even worse: if the public key was not found? In dubio pro reo, they
say, but OTOH you asked to verify the signatures...
I don't see how not finding the public key is `worse' than a bad
signature. Compared to what the user learns currently when they run git
fetch and receive new signed tags, the case of not having the required
public key leaves them in exactly the same state: the user does not know
whether the signature is valid or not.
The user didn't ask to verify, as I see it; rather, they asked git to
*try* to verify. If that fails in a way they don't expect, they're free
to investigate further with git tag -v for situations like not having
the right public key.
Deskin Miller
From: Deskin Miller <hidden> Date: 2016-06-15 22:45:42
On Mon, Nov 24, 2008 at 12:04:59PM +0100, Johannes Schindelin wrote:
Hi,
On Sun, 23 Nov 2008, Deskin Miller wrote:
quoted
builtin-verify-tag.c didn't expose any of its functionality to be used
internally. Refactor some of it into new verify-tag.c and expose
verify_tag_sha1 able to be called from elsewhere in git.
Signed-off-by: Deskin Miller <redacted>
---
Makefile | 2 +
builtin-verify-tag.c | 61 ++-------------------------------------
verify-tag.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++
verify-tag.h | 10 ++++++
4 files changed, 93 insertions(+), 57 deletions(-)
create mode 100644 verify-tag.c
create mode 100644 verify-tag.h
I'll comment on the output of "format-patch -n -C -C" instead, as that
makes it much easier to see what you actually did:
Didn't realise -C -C was the magic incantation; I'll remember it for the
future.
diff --git a/builtin-verify-tag.c b/verify-tag.csimilarity index 56%copy from builtin-verify-tag.ccopy to verify-tag.cindex 729a159..c9be331 100644--- a/builtin-verify-tag.c+++ b/verify-tag.c
*
- * Copyright (c) 2007 Carlos Rica [off-list ref]
+ * Copyright (c) 2008 Deskin Miller [off-list ref]
Disagree.
Even if Carlos seemed to stop his work on Git entirely, which I find
disappointing, you are _not_ free to pretend his work is yours. And given
this diff:
[...]
I think pretty much all you did was deleting (and thereby you do not gain
any copyright).
I realised my mistake in altering the copyright information just after
sending out these patches. I think I'd written the header first in
verify-tag.c before copying the code in; though I couldn't say what I
thought I'd be writing that would end up protected by copyright. At any
rate, it was an honest mistake, and I apologise, Carlos, for my
unintended plagarism; I'll be sure to restore the proper copyright
notice for any subsequent versions.
Except for one change: why on earth did you think it a good idea to
suppress telling the user the _name_ of the tag when an error occurs?
I, for one, would find it way less than helpful to read
Cannot verify a non-tag object of type blob.
than to read
refs/tags/dscho-key: cannot verify a non-tag object of type blob.
Besides, I do not see where you warn that "tag <name> not found." Changes
like this one need to be justified (by saying in the commit message where
the warning is already issued, and not letting the reviewer/reader leave
wondering).
The verify_tag_sha1 function is newly exposed to the rest of git, and
has a different signature from verify_tag, which could take a ref while
verify_tag_sha1 takes a sha1. verify_tag still includes both the checks
you refer to before calling verify_tag_sha1, so the error output is
identical in all cases before and after applying this patch.
The OBJ_TAG check, however, is duplicated so that internal git calls to
verify_tag_sha1 can't pass in e.g. a blob sha1 which just happens to
contain the same contents as a signed tag.
Actually, I initially did not leave the OBJ_TAG check in verify_tag, but
relied on it checking the return value of verify_tag_sha1 to see if an
error occurred, and printing 'Failed to verify <name>' in that case, for
precisely the reason you point out, that the ref name is very useful in
this failure case. However, I ultimately decided to duplicate the check
so that the error output would match up exactly.
Please, next time you submit a patch like this, do the -C -C yourself.
Letting all the reviewers do it looks lousy on the overall time balance
sheet, and it may also lead to a potential reviewer preferring to do
something else instead.
Will do; thanks for reviewing in spite of my shortcomings.
Now, Junio already said that he is not (yet) convinced that this change
should be in Git proper, rather than a hook, so it is up to you to decide
if you deem it important enough to try harder to convince people.
I, for one, would think that it may be a good change: AFAIK only hard-core
gits use hooks, everybody else avoids them. So if we deem verifying
signatures important enough, we might want to have better support for it
than some example hooks.
So color me half-convinced.
Ciao,
Dscho
From: Deskin Miller <hidden> Date: 2016-06-15 22:45:42
On Mon, Nov 24, 2008 at 11:44:40AM +0100, Johannes Schindelin wrote:
Hi,
On Sun, 23 Nov 2008, Deskin Miller wrote:
quoted
When git fetch downloads signed tag objects, make it verify them right
then. This extends the output summary of fetch to include "(good
signature)" for valid tags and "(BAD SIGNATURE)" for invalid tags. If
the user does not have the correct key in the gpg keyring, gpg returns
2, verify_tag_sha1 returns -2 and nothing additional is output about the
tag's validity.
This must be turned off by default, IMO. You cannot expect each and every
developer to have gpg _and_ all those public keys installed.
Adding a configuration variable to control this makes sense, and is on
my TODO list for v2 (core.autoVerifyTags?). However, I don't see a
compelling reason to make it off by default, as if gpg isn't found, or a
particular public key isn't in the keyring, the output is no different
from what fetch prints now.
Deskin Miller