Skip to contentSkip to conversation

git-tag -s must fail if gpg is broken and cannot sign tags

PATCH In review Shawn O. Pearce 3 participants 5 messages Jun 15, 2016

lore
Share & tools
lore
#0 lore raw

PATCH 2/2 git-tag -s must fail if gpg is broken and cannot sign tags

If the user has misconfigured user.signingkey in their .git/config or just doesn't have any secret keys on their keyring and they ask for a signed tag with git tag -s we better make sure the resulting tag was actually signed by gpg.

Prior versions of builtin git-tag allowed this failure to slip by without error as they were not checking the return value of the finish_command() so they did not notice when gpg exited with an error exit status. They also did not fail if gpg produced an empty output or if read_in_full received an error from the read system call while trying to read the pipe back from gpg.

Finally we did not actually honor any return value from the do_sign function as it returns ssize_t but was being stored into an unsigned long. This caused the compiler to optimize out the die condition, allowing git-tag to continue along and create the tag object.

With these issues fixed git-tag -s will now fail to create the tag and will report a non-zero exit status to its caller, thereby allowing automated helper scripts to detect (and recover from) failure if gpg is not working properly.

---

I think this and my prior contrib/workdir patch should both go into maint. This one in particular; it hurt us today when an automated tool that runs git tag -s didn't notice the GnuPG problems.

 builtin-tag.c  |    8 +++++---
 t/t7004-tag.sh |    7 +++++++
 2 files changed, 12 insertions(+), 3 deletions(-)
builtin-tag.c +5 -3
index 348919c..aadf850 100644
@@ -217,7 +217,8 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
 	gpg.close_in = 0;
 	len = read_in_full(gpg.out, buffer + size, max - size);
 
-	finish_command(&gpg);
+	if (finish_command(&gpg) || !len || len < 0)
+		return error("gpg failed to sign the tag");
 
 	if (len == max - size)
 		return error("could not read the entire signature from gpg.");
@@ -310,9 +311,10 @@ static void create_tag(const unsigned char *object, const char *tag,
 	size += header_len;
 
 	if (sign) {
-		size = do_sign(buffer, size, max_size);
-		if (size < 0)
+		ssize_t r = do_sign(buffer, size, max_size);
+		if (r < 0)
 			die("unable to sign the tag");
+		size = r;
 	}
 
 	if (write_sha1_file(buffer, size, tag_type, result) < 0)
t/t7004-tag.sh +7 -0
index 606d4f2..0d07bc3 100755
@@ -990,6 +990,13 @@ test_expect_success \
 	git diff expect actual
 '
 
+# try to sign with bad user.signingkey
+git config user.signingkey BobTheMouse
+test_expect_failure \
+	'git-tag -s fails if gpg is misconfigured' \
+	'git tag -s -m tail tag-gpg-failure'
+git config --unset user.signingkey
+
 # try to verify without gpg:
 
 rm -rf gpghome
Signature · 1 line
1.5.3.1.840.g0fedbc
#1 lore raw
Quoted 4 lines · Shawn O. Pearce

If the user has misconfigured user.signingkey in their .git/config or just doesn't have any secret keys on their keyring and they ask for a signed tag with git tag -s we better make sure the resulting tag was actually signed by gpg.

...

Quoted 3 lines

I think this and my prior contrib/workdir patch should both go into maint. This one in particular; it hurt us today when an automated tool that runs git tag -s didn't notice the GnuPG problems.

I'm sorry, despite the subject of this email this is actually a *one* patch series. The 2/2 is because I applied and tested this on top of the contrib/workdir patch I was talking about above and my email sending script thought it was a two patch series.

Signature · 1 line
Shawn.
#2 lore raw
Quoted 6 lines · Shawn O. Pearce
Quoted 4 lines · Shawn O. Pearce

If the user has misconfigured user.signingkey in their .git/config or just doesn't have any secret keys on their keyring and they ask for a signed tag with git tag -s we better make sure the resulting tag was actually signed by gpg.

...

Quoted 8 lines
Quoted 3 lines

I think this and my prior contrib/workdir patch should both go into maint. This one in particular; it hurt us today when an automated tool that runs git tag -s didn't notice the GnuPG problems.

I'm sorry, despite the subject of this email this is actually a *one* patch series. The 2/2 is because I applied and tested this on top of the contrib/workdir patch I was talking about above and my email sending script thought it was a two patch series.

This seems to fail the test depending on the order processes happen to be scheduled. I haven't looked at it closely yet.

#3 lore raw
Quoted 10 lines · Junio C Hamano
Quoted 5 lines · Shawn O. Pearce
Quoted 4 lines · Shawn O. Pearce

If the user has misconfigured user.signingkey in their .git/config or just doesn't have any secret keys on their keyring and they ask for a signed tag with git tag -s we better make sure the resulting tag was actually signed by gpg.

This seems to fail the test depending on the order processes happen to be scheduled. I haven't looked at it closely yet.

That's not good. I noticed stepping through the code last night that if gpg is misconfigured (e.g. set a bad user.signingkey in .git/config) it will terminate and send SIGPIPE to git-tag, which makes it terminate.

All my change did was implement proper error handling. So if you are seeing failures now then we probably have a problem with the code without my patch too...

Signature · 1 line
Shawn.
#4 lore raw

2007/9/7, Shawn O. Pearce <[email protected]>:

Quoted 9 lines
Quoted 8 lines · Junio C Hamano
Quoted 5 lines · Shawn O. Pearce
Quoted 4 lines · Shawn O. Pearce

If the user has misconfigured user.signingkey in their .git/config or just doesn't have any secret keys on their keyring and they ask for a signed tag with git tag -s we better make sure the resulting tag was actually signed by gpg.

Quoted 7 lines
Quoted 2 lines

This seems to fail the test depending on the order processes happen to be scheduled. I haven't looked at it closely yet.

That's not good. I noticed stepping through the code last night that if gpg is misconfigured (e.g. set a bad user.signingkey in .git/config) it will terminate and send SIGPIPE to git-tag, which makes it terminate.

I haven't tested it enough, but now I know that the program is terminated in write_or_die(gpg.in, buffer, size), and it is passing the test or not depending on the system, because I added some code before the test and then it worked for me and if I remove that test, it is failing again. These messages are printed:
gpg: skipped "BobTheMouse": secret key not available
gpg: signing failed: secret key not available
Just after start_command and before write_in_full.

Possibly the reason is that code in write_in_full() that makes exit(0) without a warning when EPIPE is returned, or possibly is write() in xwrite(), that dies directly when EPIPE is received like it was for builtin-verify-tag.c. Catching the signal EPIPE doesn't worked for me, so I will do some checks more to trace the code more exactly in my system.

Quoted 3 lines

All my change did was implement proper error handling. So if you are seeing failures now then we probably have a problem with the code without my patch too...

The test seems to fail also without your patch, as you say.