Commit 91d845ad5d8f8897e51d3f9233a9ea3e89fee80d

Josh Rickmar 2022-07-03T21:44:01

fix tag signing when the key file does not exist This should fail without creating any tag. Before, ssh-keygen(1) would print an error to stderr, but got would create an unsigned tag. ok op@

diff --git a/got/got.c b/got/got.c
index 78d2132..ce9c1f6 100644
--- a/got/got.c
+++ b/got/got.c
@@ -7214,7 +7214,7 @@ done:
 static const struct got_error *
 add_tag(struct got_repository *repo, const char *tagger,
     const char *tag_name, const char *commit_arg, const char *tagmsg_arg,
-    const char *key_file, int verbosity)
+    const char *signer_id, int verbosity)
 {
 	const struct got_error *err = NULL;
 	struct got_object_id *commit_id = NULL, *tag_id = NULL;
@@ -7280,7 +7280,7 @@ add_tag(struct got_repository *repo, const char *tagger,
 	}
 
 	err = got_object_tag_create(&tag_id, tag_name, commit_id,
-	    tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, key_file, repo,
+	    tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, signer_id, repo,
 	    verbosity);
 	if (err) {
 		if (tagmsg_path)
diff --git a/include/got_error.h b/include/got_error.h
index 4bfaed5..9dcd5b8 100644
--- a/include/got_error.h
+++ b/include/got_error.h
@@ -171,6 +171,7 @@
 #define GOT_ERR_PIN_PACK	153
 #define GOT_ERR_BAD_TAG_SIGNATURE 154
 #define GOT_ERR_VERIFY_TAG_SIGNATURE 155
+#define GOT_ERR_SIGNING_TAG	156
 
 struct got_error {
         int code;
diff --git a/lib/error.c b/lib/error.c
index 3c092e6..bde78fa 100644
--- a/lib/error.c
+++ b/lib/error.c
@@ -219,6 +219,7 @@ static const struct got_error got_errors[] = {
 	{ GOT_ERR_PIN_PACK, "could not pin pack file" },
 	{ GOT_ERR_BAD_TAG_SIGNATURE, "invalid tag signature" },
 	{ GOT_ERR_VERIFY_TAG_SIGNATURE, "cannot verify signature" },
+	{ GOT_ERR_SIGNING_TAG, "unable to sign tag" },
 };
 
 static struct got_custom_error {
diff --git a/lib/object_create.c b/lib/object_create.c
index 8f33d6b..2e773fd 100644
--- a/lib/object_create.c
+++ b/lib/object_create.c
@@ -612,7 +612,7 @@ done:
 const struct got_error *
 got_object_tag_create(struct got_object_id **id,
     const char *tag_name, struct got_object_id *object_id, const char *tagger,
-    time_t tagger_time, const char *tagmsg, const char *key_file,
+    time_t tagger_time, const char *tagmsg, const char *signer_id,
     struct got_repository *repo, int verbosity)
 {
 	const struct got_error *err = NULL;
@@ -687,7 +687,7 @@ got_object_tag_create(struct got_object_id **id,
 	while (isspace((unsigned char)msg[0]))
 		msg++;
 
-	if (key_file) {
+	if (signer_id) {
 		FILE *out;
 		pid_t pid;
 		size_t len;
@@ -721,7 +721,7 @@ got_object_tag_create(struct got_object_id **id,
 		if (err)
 			goto done;
 
-		err = got_sigs_sign_tag_ssh(&pid, &in_fd, &out_fd, key_file,
+		err = got_sigs_sign_tag_ssh(&pid, &in_fd, &out_fd, signer_id,
 		    verbosity);
 		if (err)
 			goto done;
@@ -738,6 +738,10 @@ got_object_tag_create(struct got_object_id **id,
 			err = got_error_from_errno("waitpid");
 			goto done;
 		}
+		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+			err = got_error(GOT_ERR_SIGNING_TAG);
+			goto done;
+		}
 
 		out = fdopen(out_fd, "r");
 		if (out == NULL) {
@@ -840,7 +844,7 @@ got_object_tag_create(struct got_object_id **id,
 	}
 	tagsize += n;
 
-	if (key_file && buf_len(buf) > 0) {
+	if (signer_id && buf_len(buf) > 0) {
 		len = buf_len(buf);
 		SHA1Update(&sha1_ctx, buf_get(buf), len);
 		n = fwrite(buf_get(buf), 1, len, tagfile);
diff --git a/regress/cmdline/tag.sh b/regress/cmdline/tag.sh
index b39af2b..07c00cb 100755
--- a/regress/cmdline/tag.sh
+++ b/regress/cmdline/tag.sh
@@ -417,6 +417,43 @@ test_tag_create_ssh_signed() {
 	test_done "$testroot" "$ret"
 }
 
+test_tag_create_ssh_signed_missing_key() {
+	local testroot=`test_init tag_create`
+	local commit_id=`git_show_head $testroot/repo`
+	local tag=1.0.0
+
+	# Fail to create a signed tag due to a missing SSH key
+	got tag -s $testroot/bogus -m 'test' -r $testroot/repo \
+		-c HEAD	$tag > $testroot/stdout 2> $testroot/stderr
+	ret=$?
+	if [ $ret -eq 0 ]; then
+		echo "got tag command succeeded unexpectedly"
+		test_done "$testroot" 1
+		return 1
+	fi
+
+	got ref -r $testroot/repo -l > $testroot/stdout
+	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
+	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+	printf "Couldn't load public key $testroot/bogus: " \
+	echo "got: unable to sign tag" >> $testroot/stderr.expected
+	printf "No such file or directory\r\n" >> $testroot/stderr.expected
+	cmp -s $testroot/stderr $testroot/stderr.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+	fi
+	test_done "$testroot" "$ret"
+}
+
+test_parseargs "$@"
 test_parseargs "$@"
 run_test test_tag_create
 run_test test_tag_list