git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC PATCH 4/4] Make git fetch verify signed tags

From
Deskin Miller <deskinm@umich.edu>
Date
Nov 24, 2008, 03:23 UTC
Message-ID
<1227497000-8684-5-git-send-email-deskinm@umich.edu>
In-Reply-To
<1227497000-8684-4-git-send-email-deskinm@umich.edu>

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 <deskinm@umich.edu>
---
 builtin-fetch.c |   25 ++++++++++++++++++-------
 t/t7004-tag.sh  |   37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+), 7 deletions(-)
diff --git a/builtin-fetch.c b/builtin-fetch.c
index f151cfa..f7a50b7 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -10,6 +10,7 @@
 #include "transport.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "verify-tag.h"
 
 static const char * const builtin_fetch_usage[] = {
 	"git fetch [options] [<repository> <refspec>...]",
@@ -233,11 +234,16 @@ static int update_local_ref(struct ref *ref,
 
 	if (!is_null_sha1(ref->old_sha1) &&
 	    !prefixcmp(ref->name, "refs/tags/")) {
-		int r;
+		int r, v;
 		r = s_update_ref("updating tag", ref, 0);
-		sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
+		if (type == OBJ_TAG)
+			v = verify_tag_sha1(ref->new_sha1, -1);
+		sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' :
+			(type == OBJ_TAG ? (v == -1 ? '!' : '-') : '-'),
 			SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
-			pretty_ref, r ? "  (unable to update local ref)" : "");
+			pretty_ref, r ? "  (unable to update local ref)" :
+			(type == OBJ_TAG ? (v == 0 ? " (good signature)" :
+			(v == -1 ? " (BAD SIGNATURE)" : "")) : ""));
 		return r;
 	}
 
@@ -246,7 +252,7 @@ static int update_local_ref(struct ref *ref,
 	if (!current || !updated) {
 		const char *msg;
 		const char *what;
-		int r;
+		int r, v;
 		if (!strncmp(ref->name, "refs/tags/", 10)) {
 			msg = "storing tag";
 			what = "[new tag]";
@@ -257,9 +263,14 @@ static int update_local_ref(struct ref *ref,
 		}
 
 		r = s_update_ref(msg, ref, 0);
-		sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
-			SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
-			r ? "  (unable to update local ref)" : "");
+		if (type == OBJ_TAG)
+			v = verify_tag_sha1(ref->new_sha1, -1);
+		sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' :
+			(type == OBJ_TAG ? (v == -1 ? '!' : '*') : '*'),
+			SUMMARY_WIDTH, what, REFCOL_WIDTH, remote,
+			pretty_ref, r ? "  (unable to update local ref)" :
+			(type == OBJ_TAG ? (v == 0 ? " (good signature)" :
+			(v == -1 ? " (BAD SIGNATURE)" : "")) : ""));
 		return r;
 	}
 
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index f377fea..00327cc 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1037,6 +1037,43 @@ test_expect_success \
 	'test_must_fail git tag -s -m tail tag-gpg-failure'
 git config --unset user.signingkey
 
+git tag -s -m 'good tag' good-tag HEAD
+bad=$(git cat-file tag good-tag | sed -e 's/good-tag/bad-tag/' | git mktag)
+git tag bad-tag $bad
+head=$(git rev-parse HEAD)
+nonkey=$(cat <<EOF | git mktag
+object $head
+type commit
+tag v1.6.0.4
+tagger Junio C Hamano <gitster@pobox.com> 1226208581 -0800
+
+GIT 1.6.0.4
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iEYEABECAAYFAkkWdUUACgkQwMbZpPMRm5rSmwCfWu+K/hXyLUnEWoOMYy1eKuMK
+KcoAnjB2qir794ibWPy6cn11uUbk7AlC
+=eaFZ
+-----END PGP SIGNATURE-----
+EOF
+)
+git tag nonkey-tag $nonkey
+
+echo 'bad-tag (BAD SIGNATURE)' > expect
+echo 'good-tag (good signature)' >> expect
+echo 'nonkey-tag' >> expect
+
+test_expect_success \
+	'git fetch verifies tags' \
+	'mkdir clone &&
+	(cd clone &&
+	git init &&
+	git remote add origin file://"$(cd .. && pwd)" &&
+	git fetch origin 2>../actual) &&
+	sed -i -ne "/ \(bad\|good\|nonkey\)-tag/s/^.*->[^a-z]*//p" actual &&
+	test_cmp expect actual
+	'
+
 # try to verify without gpg:
 
 rm -rf gpghome
-- 
1.6.0.4.770.ga8394
Previous: Deskin MillerNext: Johannes Schindelin
Message 5 of 16 in “Teach git fetch to verify signed tags automatically”
  1. 0/4 Teach git fetch to verify signed tags automaticallyDeskin Miller, Nov 24, 2008
  2. 1/4 Refactor builtin-verify-tag.cDeskin Miller, Nov 24, 2008
  3. 2/4 verify-tag.c: ignore SIGPIPE around gpg invocationDeskin Miller, Nov 24, 2008
  4. 3/4 verify-tag.c: suppress gpg output if askedDeskin Miller, Nov 24, 2008
  5. 4/4 Make git fetch verify signed tagsDeskin Miller, Nov 24, 2008
  6. Johannes SchindelinNov 24, 2008
  7. Deskin MillerNov 28, 2008
  8. Johannes SchindelinNov 24, 2008
  9. Deskin MillerNov 28, 2008
  10. Junio C HamanoNov 24, 2008
  11. Junio C HamanoNov 24, 2008
  12. Deskin MillerNov 28, 2008
  13. Johannes SchindelinNov 28, 2008
  14. Johannes SchindelinNov 24, 2008
  15. Deskin MillerNov 28, 2008
  16. Junio C HamanoNov 28, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.