{"thread":{"id":"39688","subject":"[PATCH v2 2/7] verify-tag: share code with verify-commit","startedAt":"2015-06-21T23:14:36Z","lastAt":"2015-06-22T00:38:36Z","messageCount":9,"participants":["brian m. carlson","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":7},"messages":[{"id":"264475","messageId":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":null,"subject":"[PATCH v2 0/7] Raw gpg output support for verify-commit and verify-tag","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:36Z","receivedAt":"2015-06-21T23:14:36Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Currently, verify-commit and verify-tag produce human-readable output.\nThis is great for humans, and awful for machines.  It also lacks a lot\nof the information that GnuPG's --status-fd output provides.\n\nFor example, if you wanted to know\n* the hash algorithm;\n* whether the signature was made with a subkey; or\n* the OpenPGP signature version\nnone of that information is available in the human-readable output.\n\nThis series introduces a --raw option for verify-commit and verify-tag.\nIf it's used, they provide the gpg --status-fd output on standard error\ninstead of the human-readable output.  The series also adds tests for\nverify-tag, since there were none; these are based off the ones for\nverify-commit.\n\nv2 of the series corrects one of two incompatibilities between\nverify-commit and verify-tag.  If a valid signature was made with an\nuntrusted key, verify-commit would exit 1, but verify-tag would exit 0.\nThis has been corrected (as Michael J Gruber suggested); both now return\n0, as the signature is in fact good.\n\nThe other incompatibility is that verify-tag --verbose always prints the\ntag contents even if no signature is found, while verify-commit\n--verbose does not print the commit.  I left this alone, as it seemed\neasy enough to accommodate at the expense of making the code slightly\nuglier.\n\nChanges since v1:\n* Unify some of the code between verify-commit and verify-tag.\n* Pass the --verbose and --raw options as flags instead of individual\n  boolean arguments.\n* Reorder the patches to ensure that verify-tag tests came before\n  refactors, to avoid regressing functionality.\n* Changed verify-commit to succeed if a signature is good, regardless of\n  whether it is trusted, at Michael J Gruber's suggestion.\n\nbrian m. carlson (7):\n  verify-tag: add tests\n  verify-tag: share code with verify-commit\n  verify-commit: add test for exit status on untrusted signature\n  gpg: centralize signature check\n  gpg: centralize printing signature buffers\n  verify-commit: add option to print raw gpg status information\n  verify-tag: add option to print raw gpg status information\n\n Documentation/git-verify-commit.txt |   4 ++\n Documentation/git-verify-tag.txt    |   4 ++\n builtin/verify-commit.c             |  25 ++++----\n builtin/verify-tag.c                |  30 +++++++---\n commit.c                            |  21 ++-----\n commit.h                            |   2 +-\n gpg-interface.c                     |  37 ++++++++++++\n gpg-interface.h                     |   6 ++\n t/t7030-verify-tag.sh               | 115 ++++++++++++++++++++++++++++++++++++\n t/t7510-signed-commit.sh            |  38 ++++++++++++\n 10 files changed, 246 insertions(+), 36 deletions(-)\n create mode 100755 t/t7030-verify-tag.sh\n\n-- \n2.4.0\n"},{"id":"264476","messageId":"1434928483-105916-2-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"[PATCH v2 1/7] verify-tag: add tests","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:37Z","receivedAt":"2015-06-21T23:14:37Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"verify-tag was lacking tests.  Add some, mirroring those used for\nverify-commit.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t7030-verify-tag.sh | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 84 insertions(+)\n create mode 100755 t/t7030-verify-tag.sh\n\ndiff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh\nnew file mode 100755\nindex 0000000..632bc53\n--- /dev/null\n+++ b/t/t7030-verify-tag.sh\n@@ -0,0 +1,84 @@\n+#!/bin/sh\n+\n+test_description='signed tag tests'\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY/lib-gpg.sh\"\n+\n+test_expect_success GPG 'create signed tags' '\n+\techo 1 >file && git add file &&\n+\ttest_tick && git commit -m initial &&\n+\tgit tag -s -m initial initial &&\n+\tgit branch side &&\n+\n+\techo 2 >file && test_tick && git commit -a -m second &&\n+\tgit tag -s -m second second &&\n+\n+\tgit checkout side &&\n+\techo 3 >elif && git add elif &&\n+\ttest_tick && git commit -m \"third on side\" &&\n+\n+\tgit checkout master &&\n+\ttest_tick && git merge -S side &&\n+\tgit tag -s -m merge merge &&\n+\n+\techo 4 >file && test_tick && git commit -a -S -m \"fourth unsigned\" &&\n+\tgit tag -a -m fourth-unsigned fourth-unsigned &&\n+\n+\ttest_tick && git commit --amend -S -m \"fourth signed\" &&\n+\tgit tag -s -m fourth fourth-signed &&\n+\n+\techo 5 >file && test_tick && git commit -a -m \"fifth\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\tgit config commit.gpgsign true &&\n+\techo 6 >file && test_tick && git commit -a -m \"sixth\" &&\n+\tgit tag -a -m sixth sixth-unsigned &&\n+\n+\ttest_tick && git rebase -f HEAD^^ && git tag -s -m 6th sixth-signed HEAD^ &&\n+\tgit tag -m seventh -s seventh-signed &&\n+\n+\techo 8 >file && test_tick && git commit -a -m eighth &&\n+\tgit tag -uB7227189 -m eighth eighth-signed-alt\n+'\n+\n+test_expect_success GPG 'verify and show signatures' '\n+\t(\n+\t\tfor tag in initial second merge fourth-signed sixth-signed seventh-signed\n+\t\tdo\n+\t\t\tgit verify-tag $tag 2>actual &&\n+\t\t\tgrep \"Good signature from\" actual &&\n+\t\t\t! grep \"BAD signature from\" actual &&\n+\t\t\techo $tag OK || exit 1\n+\t\tdone\n+\t) &&\n+\t(\n+\t\tfor tag in fourth-unsigned fifth-unsigned sixth-unsigned\n+\t\tdo\n+\t\t\ttest_must_fail git verify-tag $tag 2>actual &&\n+\t\t\t! grep \"Good signature from\" actual &&\n+\t\t\t! grep \"BAD signature from\" actual &&\n+\t\t\techo $tag OK || exit 1\n+\t\tdone\n+\t) &&\n+\t(\n+\t\tfor tag in eighth-signed-alt\n+\t\tdo\n+\t\t\tgit verify-tag $tag 2>actual &&\n+\t\t\tgrep \"Good signature from\" actual &&\n+\t\t\t! grep \"BAD signature from\" actual &&\n+\t\t\tgrep \"not certified\" actual &&\n+\t\t\techo $tag OK || exit 1\n+\t\tdone\n+\t)\n+'\n+\n+test_expect_success GPG 'detect fudged signature' '\n+\tgit cat-file tag seventh-signed >raw &&\n+\tsed -e \"s/seventh/7th forged/\" raw >forged1 &&\n+\tgit hash-object -w -t tag forged1 >forged1.tag &&\n+\ttest_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&\n+\tgrep \"BAD signature from\" actual1 &&\n+\t! grep \"Good signature from\" actual1\n+'\n+\n+test_done\n-- \n2.4.0\n"},{"id":"264472","messageId":"1434928483-105916-3-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"[PATCH v2 2/7] verify-tag: share code with verify-commit","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:38Z","receivedAt":"2015-06-21T23:14:38Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"verify-tag was executing an entirely different codepath than\nverify-commit, except for the underlying verify_signed_buffer.  Move\nmuch of the code from check_commit_signature to a generic\ncheck_signature function and adjust both codepaths to call it.\n\nUpdate verify-tag to explicitly output the signature text, as we now\ncall verify_signed_buffer with strbufs to catch the output, which\nprevents it from being printed automatically.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/verify-tag.c |  9 ++++++++-\n commit.c             | 15 +--------------\n gpg-interface.c      | 23 +++++++++++++++++++++++\n gpg-interface.h      |  2 ++\n 4 files changed, 34 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/verify-tag.c b/builtin/verify-tag.c\nindex 53c68fc..e1eb341 100644\n--- a/builtin/verify-tag.c\n+++ b/builtin/verify-tag.c\n@@ -20,8 +20,11 @@ static const char * const verify_tag_usage[] = {\n \n static int run_gpg_verify(const char *buf, unsigned long size, int verbose)\n {\n+\tstruct signature_check sigc;\n \tint len;\n \n+\tmemset(&sigc, 0, sizeof(sigc));\n+\n \tlen = parse_signature(buf, size);\n \tif (verbose)\n \t\twrite_in_full(1, buf, len);\n@@ -29,7 +32,11 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)\n \tif (size == len)\n \t\treturn error(\"no signature found\");\n \n-\treturn verify_signed_buffer(buf, len, buf + len, size - len, NULL, NULL);\n+\tcheck_signature(buf, len, buf + len, size - len, &sigc);\n+\tfputs(sigc.gpg_output, stderr);\n+\n+\tsignature_check_clear(&sigc);\n+\treturn sigc.result != 'G' && sigc.result != 'U';\n }\n \n static int verify_tag(const char *name, int verbose)\ndiff --git a/commit.c b/commit.c\nindex 6e2103c..1e2e144 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -1236,27 +1236,14 @@ void check_commit_signature(const struct commit *commit, struct signature_check\n {\n \tstruct strbuf payload = STRBUF_INIT;\n \tstruct strbuf signature = STRBUF_INIT;\n-\tstruct strbuf gpg_output = STRBUF_INIT;\n-\tstruct strbuf gpg_status = STRBUF_INIT;\n-\tint status;\n \n \tsigc->result = 'N';\n \n \tif (parse_signed_commit(commit, &payload, &signature) <= 0)\n \t\tgoto out;\n-\tstatus = verify_signed_buffer(payload.buf, payload.len,\n-\t\t\t\t      signature.buf, signature.len,\n-\t\t\t\t      &gpg_output, &gpg_status);\n-\tif (status && !gpg_output.len)\n-\t\tgoto out;\n-\tsigc->payload = strbuf_detach(&payload, NULL);\n-\tsigc->gpg_output = strbuf_detach(&gpg_output, NULL);\n-\tsigc->gpg_status = strbuf_detach(&gpg_status, NULL);\n-\tparse_gpg_output(sigc);\n+\tcheck_signature(payload.buf, payload.len, signature.buf, signature.len, sigc);\n \n  out:\n-\tstrbuf_release(&gpg_status);\n-\tstrbuf_release(&gpg_output);\n \tstrbuf_release(&payload);\n \tstrbuf_release(&signature);\n }\ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex 68b0c81..66dbee2 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -60,6 +60,29 @@ void parse_gpg_output(struct signature_check *sigc)\n \t}\n }\n \n+void check_signature(const char *payload, size_t plen, const char *signature,\n+\tsize_t slen, struct signature_check *sigc)\n+{\n+\tstruct strbuf gpg_output = STRBUF_INIT;\n+\tstruct strbuf gpg_status = STRBUF_INIT;\n+\tint status;\n+\n+\tsigc->result = 'N';\n+\n+\tstatus = verify_signed_buffer(payload, plen, signature, slen,\n+\t\t\t\t      &gpg_output, &gpg_status);\n+\tif (status && !gpg_output.len)\n+\t\tgoto out;\n+\tsigc->payload = xmemdupz(payload, plen);\n+\tsigc->gpg_output = strbuf_detach(&gpg_output, NULL);\n+\tsigc->gpg_status = strbuf_detach(&gpg_status, NULL);\n+\tparse_gpg_output(sigc);\n+\n+ out:\n+\tstrbuf_release(&gpg_status);\n+\tstrbuf_release(&gpg_output);\n+}\n+\n /*\n  * Look at GPG signed content (e.g. a signed tag object), whose\n  * payload is followed by a detached signature on it.  Return the\ndiff --git a/gpg-interface.h b/gpg-interface.h\nindex 87a4f2e..043bcaa 100644\n--- a/gpg-interface.h\n+++ b/gpg-interface.h\n@@ -27,5 +27,7 @@ extern int verify_signed_buffer(const char *payload, size_t payload_size, const\n extern int git_gpg_config(const char *, const char *, void *);\n extern void set_signing_key(const char *);\n extern const char *get_signing_key(void);\n+extern void check_signature(const char *payload, size_t plen,\n+\tconst char *signature, size_t slen, struct signature_check *sigc);\n \n #endif\n-- \n2.4.0\n"},{"id":"264473","messageId":"1434928483-105916-4-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"[PATCH v2 3/7] verify-commit: add test for exit status on untrusted signature","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:39Z","receivedAt":"2015-06-21T23:14:39Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"verify-tag exits successfully if the signature is good but the key is\nuntrusted.  verify-commit exits unsuccessfully.  This divergence in\nbehavior is unexpected and unwanted.  Since verify-tag existed earlier,\nadd a failing test to have verify-commit share verify-tag's behavior.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t7510-signed-commit.sh | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\nindex 13331e5..4fc376e 100755\n--- a/t/t7510-signed-commit.sh\n+++ b/t/t7510-signed-commit.sh\n@@ -81,6 +81,13 @@ test_expect_success GPG 'verify and show signatures' '\n \t)\n '\n \n+test_expect_failure GPG 'verify-commit exits success on untrusted signature' '\n+\tgit verify-commit eighth-signed-alt 2>actual &&\n+\tgrep \"Good signature from\" actual &&\n+\t! grep \"BAD signature from\" actual &&\n+\tgrep \"not certified\" actual\n+'\n+\n test_expect_success GPG 'show signed commit with signature' '\n \tgit show -s initial >commit &&\n \tgit show -s --show-signature initial >show &&\n-- \n2.4.0\n"},{"id":"264474","messageId":"1434928483-105916-5-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"[PATCH v2 4/7] gpg: centralize signature check","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:40Z","receivedAt":"2015-06-21T23:14:40Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"verify-commit and verify-tag both share a central codepath for verifying\ncommits: check_signature.  However, verify-tag exited successfully for\nuntrusted signature, while verify-commit exited unsuccessfully.\nCentralize this signature check and make verify-commit adopt the older\nverify-tag behavior.  This behavior is more logical anyway, as the\nsignature is in fact valid, whether or not there's a path of trust to\nthe author.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/verify-commit.c  | 5 +++--\n builtin/verify-tag.c     | 5 +++--\n commit.c                 | 8 ++++++--\n commit.h                 | 2 +-\n gpg-interface.c          | 4 +++-\n gpg-interface.h          | 2 +-\n t/t7510-signed-commit.sh | 2 +-\n 7 files changed, 18 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/verify-commit.c b/builtin/verify-commit.c\nindex ec0c4e3..e30f7cf 100644\n--- a/builtin/verify-commit.c\n+++ b/builtin/verify-commit.c\n@@ -21,10 +21,11 @@ static const char * const verify_commit_usage[] = {\n static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, int verbose)\n {\n \tstruct signature_check signature_check;\n+\tint ret;\n \n \tmemset(&signature_check, 0, sizeof(signature_check));\n \n-\tcheck_commit_signature(lookup_commit(sha1), &signature_check);\n+\tret = check_commit_signature(lookup_commit(sha1), &signature_check);\n \n \tif (verbose && signature_check.payload)\n \t\tfputs(signature_check.payload, stdout);\n@@ -33,7 +34,7 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l\n \t\tfputs(signature_check.gpg_output, stderr);\n \n \tsignature_check_clear(&signature_check);\n-\treturn signature_check.result != 'G';\n+\treturn ret;\n }\n \n static int verify_commit(const char *name, int verbose)\ndiff --git a/builtin/verify-tag.c b/builtin/verify-tag.c\nindex e1eb341..8750bef 100644\n--- a/builtin/verify-tag.c\n+++ b/builtin/verify-tag.c\n@@ -22,6 +22,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)\n {\n \tstruct signature_check sigc;\n \tint len;\n+\tint ret;\n \n \tmemset(&sigc, 0, sizeof(sigc));\n \n@@ -32,11 +33,11 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)\n \tif (size == len)\n \t\treturn error(\"no signature found\");\n \n-\tcheck_signature(buf, len, buf + len, size - len, &sigc);\n+\tret = check_signature(buf, len, buf + len, size - len, &sigc);\n \tfputs(sigc.gpg_output, stderr);\n \n \tsignature_check_clear(&sigc);\n-\treturn sigc.result != 'G' && sigc.result != 'U';\n+\treturn ret;\n }\n \n static int verify_tag(const char *name, int verbose)\ndiff --git a/commit.c b/commit.c\nindex 1e2e144..62223a3 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -1232,20 +1232,24 @@ free_return:\n \tfree(buf);\n }\n \n-void check_commit_signature(const struct commit *commit, struct signature_check *sigc)\n+int check_commit_signature(const struct commit *commit, struct signature_check *sigc)\n {\n \tstruct strbuf payload = STRBUF_INIT;\n \tstruct strbuf signature = STRBUF_INIT;\n+\tint ret = 1;\n \n \tsigc->result = 'N';\n \n \tif (parse_signed_commit(commit, &payload, &signature) <= 0)\n \t\tgoto out;\n-\tcheck_signature(payload.buf, payload.len, signature.buf, signature.len, sigc);\n+\tret = check_signature(payload.buf, payload.len, signature.buf,\n+\t\tsignature.len, sigc);\n \n  out:\n \tstrbuf_release(&payload);\n \tstrbuf_release(&signature);\n+\n+\treturn ret;\n }\n \n \ndiff --git a/commit.h b/commit.h\nindex 9a1fa96..4983bdd 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -379,7 +379,7 @@ extern void print_commit_list(struct commit_list *list,\n  * at all.  This may allocate memory for sig->gpg_output, sig->gpg_status,\n  * sig->signer and sig->key.\n  */\n-extern void check_commit_signature(const struct commit *commit, struct signature_check *sigc);\n+extern int check_commit_signature(const struct commit *commit, struct signature_check *sigc);\n \n int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);\n \ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex 66dbee2..77a4da6 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -60,7 +60,7 @@ void parse_gpg_output(struct signature_check *sigc)\n \t}\n }\n \n-void check_signature(const char *payload, size_t plen, const char *signature,\n+int check_signature(const char *payload, size_t plen, const char *signature,\n \tsize_t slen, struct signature_check *sigc)\n {\n \tstruct strbuf gpg_output = STRBUF_INIT;\n@@ -81,6 +81,8 @@ void check_signature(const char *payload, size_t plen, const char *signature,\n  out:\n \tstrbuf_release(&gpg_status);\n \tstrbuf_release(&gpg_output);\n+\n+\treturn sigc->result != 'G' && sigc->result != 'U';\n }\n \n /*\ndiff --git a/gpg-interface.h b/gpg-interface.h\nindex 043bcaa..e2aabde 100644\n--- a/gpg-interface.h\n+++ b/gpg-interface.h\n@@ -27,7 +27,7 @@ extern int verify_signed_buffer(const char *payload, size_t payload_size, const\n extern int git_gpg_config(const char *, const char *, void *);\n extern void set_signing_key(const char *);\n extern const char *get_signing_key(void);\n-extern void check_signature(const char *payload, size_t plen,\n+extern int check_signature(const char *payload, size_t plen,\n \tconst char *signature, size_t slen, struct signature_check *sigc);\n \n #endif\ndiff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\nindex 4fc376e..796138f 100755\n--- a/t/t7510-signed-commit.sh\n+++ b/t/t7510-signed-commit.sh\n@@ -81,7 +81,7 @@ test_expect_success GPG 'verify and show signatures' '\n \t)\n '\n \n-test_expect_failure GPG 'verify-commit exits success on untrusted signature' '\n+test_expect_success GPG 'verify-commit exits success on untrusted signature' '\n \tgit verify-commit eighth-signed-alt 2>actual &&\n \tgrep \"Good signature from\" actual &&\n \t! grep \"BAD signature from\" actual &&\n-- \n2.4.0\n"},{"id":"264477","messageId":"1434928483-105916-6-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"[PATCH v2 5/7] gpg: centralize printing signature buffers","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:41Z","receivedAt":"2015-06-21T23:14:41Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"The code to handle printing of signature data from a struct\nsignature_check is very similar between verify-commit and verify-tag.\nPlace this in a single function.  verify-tag retains its special case\nbehavior of printing the tag even when no valid signature is found.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/verify-commit.c | 7 +------\n builtin/verify-tag.c    | 9 +++++----\n gpg-interface.c         | 9 +++++++++\n gpg-interface.h         | 3 +++\n 4 files changed, 18 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/verify-commit.c b/builtin/verify-commit.c\nindex e30f7cf..016319a 100644\n--- a/builtin/verify-commit.c\n+++ b/builtin/verify-commit.c\n@@ -26,12 +26,7 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l\n \tmemset(&signature_check, 0, sizeof(signature_check));\n \n \tret = check_commit_signature(lookup_commit(sha1), &signature_check);\n-\n-\tif (verbose && signature_check.payload)\n-\t\tfputs(signature_check.payload, stdout);\n-\n-\tif (signature_check.gpg_output)\n-\t\tfputs(signature_check.gpg_output, stderr);\n+\tprint_signature_buffer(&signature_check, verbose ? GPG_VERIFY_VERBOSE : 0);\n \n \tsignature_check_clear(&signature_check);\n \treturn ret;\ndiff --git a/builtin/verify-tag.c b/builtin/verify-tag.c\nindex 8750bef..d67e3db 100644\n--- a/builtin/verify-tag.c\n+++ b/builtin/verify-tag.c\n@@ -27,14 +27,15 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)\n \tmemset(&sigc, 0, sizeof(sigc));\n \n \tlen = parse_signature(buf, size);\n-\tif (verbose)\n-\t\twrite_in_full(1, buf, len);\n \n-\tif (size == len)\n+\tif (size == len) {\n+\t\tif (verbose)\n+\t\t\twrite_in_full(1, buf, len);\n \t\treturn error(\"no signature found\");\n+\t}\n \n \tret = check_signature(buf, len, buf + len, size - len, &sigc);\n-\tfputs(sigc.gpg_output, stderr);\n+\tprint_signature_buffer(&sigc, verbose ? GPG_VERIFY_VERBOSE : 0);\n \n \tsignature_check_clear(&sigc);\n \treturn ret;\ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex 77a4da6..e764fb6 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -85,6 +85,15 @@ int check_signature(const char *payload, size_t plen, const char *signature,\n \treturn sigc->result != 'G' && sigc->result != 'U';\n }\n \n+void print_signature_buffer(const struct signature_check *sigc, unsigned flags)\n+{\n+\tif (flags & GPG_VERIFY_VERBOSE && sigc->payload)\n+\t\tfputs(sigc->payload, stdout);\n+\n+\tif (sigc->gpg_output)\n+\t\tfputs(sigc->gpg_output, stderr);\n+}\n+\n /*\n  * Look at GPG signed content (e.g. a signed tag object), whose\n  * payload is followed by a detached signature on it.  Return the\ndiff --git a/gpg-interface.h b/gpg-interface.h\nindex e2aabde..1207861 100644\n--- a/gpg-interface.h\n+++ b/gpg-interface.h\n@@ -1,6 +1,8 @@\n #ifndef GPG_INTERFACE_H\n #define GPG_INTERFACE_H\n \n+#define GPG_VERIFY_VERBOSE\t1\n+\n struct signature_check {\n \tchar *payload;\n \tchar *gpg_output;\n@@ -29,5 +31,6 @@ extern void set_signing_key(const char *);\n extern const char *get_signing_key(void);\n extern int check_signature(const char *payload, size_t plen,\n \tconst char *signature, size_t slen, struct signature_check *sigc);\n+void print_signature_buffer(const struct signature_check *sigc, unsigned flags);\n \n #endif\n-- \n2.4.0\n"},{"id":"264478","messageId":"1434928483-105916-7-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"[PATCH v2 6/7] verify-commit: add option to print raw gpg status information","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:42Z","receivedAt":"2015-06-21T23:14:42Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"verify-commit by default displays human-readable output on standard\nerror.  However, it can also be useful to get access to the raw gpg\nstatus information, which is machine-readable, allowing automated\nimplementation of signing policy.  Add a --raw option to make\nverify-commit produce the gpg status information on standard error\ninstead of the human-readable format.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n Documentation/git-verify-commit.txt |  4 ++++\n builtin/verify-commit.c             | 15 ++++++++++-----\n gpg-interface.c                     |  7 +++++--\n gpg-interface.h                     |  1 +\n t/t7510-signed-commit.sh            | 31 +++++++++++++++++++++++++++++++\n 5 files changed, 51 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-verify-commit.txt b/Documentation/git-verify-commit.txt\nindex 9413e28..ecf4da1 100644\n--- a/Documentation/git-verify-commit.txt\n+++ b/Documentation/git-verify-commit.txt\n@@ -16,6 +16,10 @@ Validates the gpg signature created by 'git commit -S'.\n \n OPTIONS\n -------\n+--raw::\n+\tPrint the raw gpg status output to standard error instead of the normal\n+\thuman-readable output.\n+\n -v::\n --verbose::\n \tPrint the contents of the commit object before validating it.\ndiff --git a/builtin/verify-commit.c b/builtin/verify-commit.c\nindex 016319a..38bedf8 100644\n--- a/builtin/verify-commit.c\n+++ b/builtin/verify-commit.c\n@@ -18,7 +18,7 @@ static const char * const verify_commit_usage[] = {\n \t\tNULL\n };\n \n-static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, int verbose)\n+static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, unsigned flags)\n {\n \tstruct signature_check signature_check;\n \tint ret;\n@@ -26,13 +26,13 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l\n \tmemset(&signature_check, 0, sizeof(signature_check));\n \n \tret = check_commit_signature(lookup_commit(sha1), &signature_check);\n-\tprint_signature_buffer(&signature_check, verbose ? GPG_VERIFY_VERBOSE : 0);\n+\tprint_signature_buffer(&signature_check, flags);\n \n \tsignature_check_clear(&signature_check);\n \treturn ret;\n }\n \n-static int verify_commit(const char *name, int verbose)\n+static int verify_commit(const char *name, unsigned flags)\n {\n \tenum object_type type;\n \tunsigned char sha1[20];\n@@ -50,7 +50,7 @@ static int verify_commit(const char *name, int verbose)\n \t\treturn error(\"%s: cannot verify a non-commit object of type %s.\",\n \t\t\t\tname, typename(type));\n \n-\tret = run_gpg_verify(sha1, buf, size, verbose);\n+\tret = run_gpg_verify(sha1, buf, size, flags);\n \n \tfree(buf);\n \treturn ret;\n@@ -67,8 +67,10 @@ static int git_verify_commit_config(const char *var, const char *value, void *cb\n int cmd_verify_commit(int argc, const char **argv, const char *prefix)\n {\n \tint i = 1, verbose = 0, had_error = 0;\n+\tunsigned flags = 0;\n \tconst struct option verify_commit_options[] = {\n \t\tOPT__VERBOSE(&verbose, N_(\"print commit contents\")),\n+\t\tOPT_BIT(0, \"raw\", &flags, N_(\"print raw gpg status output\"), GPG_VERIFY_RAW),\n \t\tOPT_END()\n \t};\n \n@@ -79,11 +81,14 @@ int cmd_verify_commit(int argc, const char **argv, const char *prefix)\n \tif (argc <= i)\n \t\tusage_with_options(verify_commit_usage, verify_commit_options);\n \n+\tif (verbose)\n+\t\tflags |= GPG_VERIFY_VERBOSE;\n+\n \t/* sometimes the program was terminated because this signal\n \t * was received in the process of writing the gpg input: */\n \tsignal(SIGPIPE, SIG_IGN);\n \twhile (i < argc)\n-\t\tif (verify_commit(argv[i++], verbose))\n+\t\tif (verify_commit(argv[i++], flags))\n \t\t\thad_error = 1;\n \treturn had_error;\n }\ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex e764fb6..3dc2fe3 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -87,11 +87,14 @@ int check_signature(const char *payload, size_t plen, const char *signature,\n \n void print_signature_buffer(const struct signature_check *sigc, unsigned flags)\n {\n+\tconst char *output = flags & GPG_VERIFY_RAW ?\n+\t\tsigc->gpg_status : sigc->gpg_output;\n+\n \tif (flags & GPG_VERIFY_VERBOSE && sigc->payload)\n \t\tfputs(sigc->payload, stdout);\n \n-\tif (sigc->gpg_output)\n-\t\tfputs(sigc->gpg_output, stderr);\n+\tif (output)\n+\t\tfputs(output, stderr);\n }\n \n /*\ndiff --git a/gpg-interface.h b/gpg-interface.h\nindex 1207861..ea68885 100644\n--- a/gpg-interface.h\n+++ b/gpg-interface.h\n@@ -2,6 +2,7 @@\n #define GPG_INTERFACE_H\n \n #define GPG_VERIFY_VERBOSE\t1\n+#define GPG_VERIFY_RAW\t\t2\n \n struct signature_check {\n \tchar *payload;\ndiff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\nindex 796138f..18e5cf0 100755\n--- a/t/t7510-signed-commit.sh\n+++ b/t/t7510-signed-commit.sh\n@@ -88,6 +88,37 @@ test_expect_success GPG 'verify-commit exits success on untrusted signature' '\n \tgrep \"not certified\" actual\n '\n \n+test_expect_success GPG 'verify signatures with --raw' '\n+\t(\n+\t\tfor commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed\n+\t\tdo\n+\t\t\tgit verify-commit --raw $commit 2>actual &&\n+\t\t\tgrep \"GOODSIG\" actual &&\n+\t\t\t! grep \"BADSIG\" actual &&\n+\t\t\techo $commit OK || exit 1\n+\t\tdone\n+\t) &&\n+\t(\n+\t\tfor commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned\n+\t\tdo\n+\t\t\ttest_must_fail git verify-commit --raw $commit 2>actual &&\n+\t\t\t! grep \"GOODSIG\" actual &&\n+\t\t\t! grep \"BADSIG\" actual &&\n+\t\t\techo $commit OK || exit 1\n+\t\tdone\n+\t) &&\n+\t(\n+\t\tfor commit in eighth-signed-alt\n+\t\tdo\n+\t\t\tgit verify-commit --raw $commit 2>actual &&\n+\t\t\tgrep \"GOODSIG\" actual &&\n+\t\t\t! grep \"BADSIG\" actual &&\n+\t\t\tgrep \"TRUST_UNDEFINED\" actual &&\n+\t\t\techo $commit OK || exit 1\n+\t\tdone\n+\t)\n+'\n+\n test_expect_success GPG 'show signed commit with signature' '\n \tgit show -s initial >commit &&\n \tgit show -s --show-signature initial >show &&\n-- \n2.4.0\n"},{"id":"264479","messageId":"1434928483-105916-8-git-send-email-sandals@crustytoothpaste.net","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"[PATCH v2 7/7] verify-tag: add option to print raw gpg status information","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2015-06-21T23:14:43Z","receivedAt":"2015-06-21T23:14:43Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"verify-tag by default displays human-readable output on standard error.\nHowever, it can also be useful to get access to the raw gpg status\ninformation, which is machine-readable, allowing automated\nimplementation of signing policy.  Add a --raw option to make verify-tag\nproduce the gpg status information on standard error instead of the\nhuman-readable format.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n Documentation/git-verify-tag.txt |  4 ++++\n builtin/verify-tag.c             | 17 +++++++++++------\n t/t7030-verify-tag.sh            | 31 +++++++++++++++++++++++++++++++\n 3 files changed, 46 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-verify-tag.txt b/Documentation/git-verify-tag.txt\nindex f88ba96..d590edc 100644\n--- a/Documentation/git-verify-tag.txt\n+++ b/Documentation/git-verify-tag.txt\n@@ -16,6 +16,10 @@ Validates the gpg signature created by 'git tag'.\n \n OPTIONS\n -------\n+--raw::\n+\tPrint the raw gpg status output to standard error instead of the normal\n+\thuman-readable output.\n+\n -v::\n --verbose::\n \tPrint the contents of the tag object before validating it.\ndiff --git a/builtin/verify-tag.c b/builtin/verify-tag.c\nindex d67e3db..00663f6 100644\n--- a/builtin/verify-tag.c\n+++ b/builtin/verify-tag.c\n@@ -18,7 +18,7 @@ static const char * const verify_tag_usage[] = {\n \t\tNULL\n };\n \n-static int run_gpg_verify(const char *buf, unsigned long size, int verbose)\n+static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)\n {\n \tstruct signature_check sigc;\n \tint len;\n@@ -29,19 +29,19 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)\n \tlen = parse_signature(buf, size);\n \n \tif (size == len) {\n-\t\tif (verbose)\n+\t\tif (flags & GPG_VERIFY_VERBOSE)\n \t\t\twrite_in_full(1, buf, len);\n \t\treturn error(\"no signature found\");\n \t}\n \n \tret = check_signature(buf, len, buf + len, size - len, &sigc);\n-\tprint_signature_buffer(&sigc, verbose ? GPG_VERIFY_VERBOSE : 0);\n+\tprint_signature_buffer(&sigc, flags);\n \n \tsignature_check_clear(&sigc);\n \treturn ret;\n }\n \n-static int verify_tag(const char *name, int verbose)\n+static int verify_tag(const char *name, unsigned flags)\n {\n \tenum object_type type;\n \tunsigned char sha1[20];\n@@ -61,7 +61,7 @@ static int verify_tag(const char *name, int verbose)\n \tif (!buf)\n \t\treturn error(\"%s: unable to read file.\", name);\n \n-\tret = run_gpg_verify(buf, size, verbose);\n+\tret = run_gpg_verify(buf, size, flags);\n \n \tfree(buf);\n \treturn ret;\n@@ -78,8 +78,10 @@ static int git_verify_tag_config(const char *var, const char *value, void *cb)\n int cmd_verify_tag(int argc, const char **argv, const char *prefix)\n {\n \tint i = 1, verbose = 0, had_error = 0;\n+\tunsigned flags = 0;\n \tconst struct option verify_tag_options[] = {\n \t\tOPT__VERBOSE(&verbose, N_(\"print tag contents\")),\n+\t\tOPT_BIT(0, \"raw\", &flags, N_(\"print raw gpg status output\"), GPG_VERIFY_RAW),\n \t\tOPT_END()\n \t};\n \n@@ -90,11 +92,14 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)\n \tif (argc <= i)\n \t\tusage_with_options(verify_tag_usage, verify_tag_options);\n \n+\tif (verbose)\n+\t\tflags |= GPG_VERIFY_VERBOSE;\n+\n \t/* sometimes the program was terminated because this signal\n \t * was received in the process of writing the gpg input: */\n \tsignal(SIGPIPE, SIG_IGN);\n \twhile (i < argc)\n-\t\tif (verify_tag(argv[i++], verbose))\n+\t\tif (verify_tag(argv[i++], flags))\n \t\t\thad_error = 1;\n \treturn had_error;\n }\ndiff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh\nindex 632bc53..4608e71 100755\n--- a/t/t7030-verify-tag.sh\n+++ b/t/t7030-verify-tag.sh\n@@ -81,4 +81,35 @@ test_expect_success GPG 'detect fudged signature' '\n \t! grep \"Good signature from\" actual1\n '\n \n+test_expect_success GPG 'verify signatures with --raw' '\n+\t(\n+\t\tfor tag in initial second merge fourth-signed sixth-signed seventh-signed\n+\t\tdo\n+\t\t\tgit verify-tag --raw $tag 2>actual &&\n+\t\t\tgrep \"GOODSIG\" actual &&\n+\t\t\t! grep \"BADSIG\" actual &&\n+\t\t\techo $tag OK || exit 1\n+\t\tdone\n+\t) &&\n+\t(\n+\t\tfor tag in fourth-unsigned fifth-unsigned sixth-unsigned\n+\t\tdo\n+\t\t\ttest_must_fail git verify-tag --raw $tag 2>actual &&\n+\t\t\t! grep \"GOODSIG\" actual &&\n+\t\t\t! grep \"BADSIG\" actual &&\n+\t\t\techo $tag OK || exit 1\n+\t\tdone\n+\t) &&\n+\t(\n+\t\tfor tag in eighth-signed-alt\n+\t\tdo\n+\t\t\tgit verify-tag --raw $tag 2>actual &&\n+\t\t\tgrep \"GOODSIG\" actual &&\n+\t\t\t! grep \"BADSIG\" actual &&\n+\t\t\tgrep \"TRUST_UNDEFINED\" actual &&\n+\t\t\techo $tag OK || exit 1\n+\t\tdone\n+\t)\n+'\n+\n test_done\n-- \n2.4.0\n"},{"id":"264481","messageId":"CAPc5daVX0CE0VeAp-gv6nj_tGp5JKS4kZGLojv-AYXSSkD38EA@mail.gmail.com","threadId":"39688","inReplyTo":"1434928483-105916-1-git-send-email-sandals@crustytoothpaste.net","subject":"Re: [PATCH v2 0/7] Raw gpg output support for verify-commit and verify-tag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-22T00:38:36Z","receivedAt":"2015-06-22T00:38:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Sun, Jun 21, 2015 at 4:14 PM, brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> This series introduces a --raw option for verify-commit and verify-tag.\n> If it's used, they provide the gpg --status-fd output on standard error\n> instead of the human-readable output.  The series also adds tests for\n> verify-tag, since there were none; these are based off the ones for\n> verify-commit.\n\nThe series was a logical and gradual progression that was pleasant to read.\n\nThanks.\n"}]}