From d66aeff21e8ce92d742aa04c5e59ca3eee5e39d8 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 21 Jun 2015 23:14:37 +0000 Subject: [PATCH 1/7] verify-tag: add tests verify-tag was lacking tests. Add some, mirroring those used for verify-commit. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- t/t7030-verify-tag.sh | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 t/t7030-verify-tag.sh diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh new file mode 100755 index 00000000000..632bc534404 --- /dev/null +++ b/t/t7030-verify-tag.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +test_description='signed tag tests' +. ./test-lib.sh +. "$TEST_DIRECTORY/lib-gpg.sh" + +test_expect_success GPG 'create signed tags' ' + echo 1 >file && git add file && + test_tick && git commit -m initial && + git tag -s -m initial initial && + git branch side && + + echo 2 >file && test_tick && git commit -a -m second && + git tag -s -m second second && + + git checkout side && + echo 3 >elif && git add elif && + test_tick && git commit -m "third on side" && + + git checkout master && + test_tick && git merge -S side && + git tag -s -m merge merge && + + echo 4 >file && test_tick && git commit -a -S -m "fourth unsigned" && + git tag -a -m fourth-unsigned fourth-unsigned && + + test_tick && git commit --amend -S -m "fourth signed" && + git tag -s -m fourth fourth-signed && + + echo 5 >file && test_tick && git commit -a -m "fifth" && + git tag fifth-unsigned && + + git config commit.gpgsign true && + echo 6 >file && test_tick && git commit -a -m "sixth" && + git tag -a -m sixth sixth-unsigned && + + test_tick && git rebase -f HEAD^^ && git tag -s -m 6th sixth-signed HEAD^ && + git tag -m seventh -s seventh-signed && + + echo 8 >file && test_tick && git commit -a -m eighth && + git tag -uB7227189 -m eighth eighth-signed-alt +' + +test_expect_success GPG 'verify and show signatures' ' + ( + for tag in initial second merge fourth-signed sixth-signed seventh-signed + do + git verify-tag $tag 2>actual && + grep "Good signature from" actual && + ! grep "BAD signature from" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in fourth-unsigned fifth-unsigned sixth-unsigned + do + test_must_fail git verify-tag $tag 2>actual && + ! grep "Good signature from" actual && + ! grep "BAD signature from" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in eighth-signed-alt + do + git verify-tag $tag 2>actual && + grep "Good signature from" actual && + ! grep "BAD signature from" actual && + grep "not certified" actual && + echo $tag OK || exit 1 + done + ) +' + +test_expect_success GPG 'detect fudged signature' ' + git cat-file tag seventh-signed >raw && + sed -e "s/seventh/7th forged/" raw >forged1 && + git hash-object -w -t tag forged1 >forged1.tag && + test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 && + grep "BAD signature from" actual1 && + ! grep "Good signature from" actual1 +' + +test_done From a4cc18f2934b8d2f00c7c3e11107acb6bfafe2c6 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 21 Jun 2015 23:14:38 +0000 Subject: [PATCH 2/7] verify-tag: share code with verify-commit verify-tag was executing an entirely different codepath than verify-commit, except for the underlying verify_signed_buffer. Move much of the code from check_commit_signature to a generic check_signature function and adjust both codepaths to call it. Update verify-tag to explicitly output the signature text, as we now call verify_signed_buffer with strbufs to catch the output, which prevents it from being printed automatically. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/verify-tag.c | 9 ++++++++- commit.c | 15 +-------------- gpg-interface.c | 23 +++++++++++++++++++++++ gpg-interface.h | 2 ++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 53c68fce3ac..e1eb341baea 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -20,8 +20,11 @@ static const char * const verify_tag_usage[] = { static int run_gpg_verify(const char *buf, unsigned long size, int verbose) { + struct signature_check sigc; int len; + memset(&sigc, 0, sizeof(sigc)); + len = parse_signature(buf, size); if (verbose) write_in_full(1, buf, len); @@ -29,7 +32,11 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) if (size == len) return error("no signature found"); - return verify_signed_buffer(buf, len, buf + len, size - len, NULL, NULL); + check_signature(buf, len, buf + len, size - len, &sigc); + fputs(sigc.gpg_output, stderr); + + signature_check_clear(&sigc); + return sigc.result != 'G' && sigc.result != 'U'; } static int verify_tag(const char *name, int verbose) diff --git a/commit.c b/commit.c index a8c7577d28a..d07a9849850 100644 --- a/commit.c +++ b/commit.c @@ -1231,27 +1231,14 @@ void check_commit_signature(const struct commit *commit, struct signature_check { struct strbuf payload = STRBUF_INIT; struct strbuf signature = STRBUF_INIT; - struct strbuf gpg_output = STRBUF_INIT; - struct strbuf gpg_status = STRBUF_INIT; - int status; sigc->result = 'N'; if (parse_signed_commit(commit, &payload, &signature) <= 0) goto out; - status = verify_signed_buffer(payload.buf, payload.len, - signature.buf, signature.len, - &gpg_output, &gpg_status); - if (status && !gpg_output.len) - goto out; - sigc->payload = strbuf_detach(&payload, NULL); - sigc->gpg_output = strbuf_detach(&gpg_output, NULL); - sigc->gpg_status = strbuf_detach(&gpg_status, NULL); - parse_gpg_output(sigc); + check_signature(payload.buf, payload.len, signature.buf, signature.len, sigc); out: - strbuf_release(&gpg_status); - strbuf_release(&gpg_output); strbuf_release(&payload); strbuf_release(&signature); } diff --git a/gpg-interface.c b/gpg-interface.c index 68b0c814f78..66dbee25b3c 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -60,6 +60,29 @@ void parse_gpg_output(struct signature_check *sigc) } } +void check_signature(const char *payload, size_t plen, const char *signature, + size_t slen, struct signature_check *sigc) +{ + struct strbuf gpg_output = STRBUF_INIT; + struct strbuf gpg_status = STRBUF_INIT; + int status; + + sigc->result = 'N'; + + status = verify_signed_buffer(payload, plen, signature, slen, + &gpg_output, &gpg_status); + if (status && !gpg_output.len) + goto out; + sigc->payload = xmemdupz(payload, plen); + sigc->gpg_output = strbuf_detach(&gpg_output, NULL); + sigc->gpg_status = strbuf_detach(&gpg_status, NULL); + parse_gpg_output(sigc); + + out: + strbuf_release(&gpg_status); + strbuf_release(&gpg_output); +} + /* * Look at GPG signed content (e.g. a signed tag object), whose * payload is followed by a detached signature on it. Return the diff --git a/gpg-interface.h b/gpg-interface.h index 87a4f2e3fad..043bcaa6306 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -27,5 +27,7 @@ extern int verify_signed_buffer(const char *payload, size_t payload_size, const extern int git_gpg_config(const char *, const char *, void *); extern void set_signing_key(const char *); extern const char *get_signing_key(void); +extern void check_signature(const char *payload, size_t plen, + const char *signature, size_t slen, struct signature_check *sigc); #endif From 8e98e5f27aba812c0f095b7e546871e14a4139f6 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 21 Jun 2015 23:14:39 +0000 Subject: [PATCH 3/7] verify-commit: add test for exit status on untrusted signature verify-tag exits successfully if the signature is good but the key is untrusted. verify-commit exits unsuccessfully. This divergence in behavior is unexpected and unwanted. Since verify-tag existed earlier, add a failing test to have verify-commit share verify-tag's behavior. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- t/t7510-signed-commit.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 13331e533bf..4fc376edb6a 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -81,6 +81,13 @@ test_expect_success GPG 'verify and show signatures' ' ) ' +test_expect_failure GPG 'verify-commit exits success on untrusted signature' ' + git verify-commit eighth-signed-alt 2>actual && + grep "Good signature from" actual && + ! grep "BAD signature from" actual && + grep "not certified" actual +' + test_expect_success GPG 'show signed commit with signature' ' git show -s initial >commit && git show -s --show-signature initial >show && From 434060ec6d9bf50f095db901da3fb9b557e11df1 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 21 Jun 2015 23:14:40 +0000 Subject: [PATCH 4/7] gpg: centralize signature check verify-commit and verify-tag both share a central codepath for verifying commits: check_signature. However, verify-tag exited successfully for untrusted signature, while verify-commit exited unsuccessfully. Centralize this signature check and make verify-commit adopt the older verify-tag behavior. This behavior is more logical anyway, as the signature is in fact valid, whether or not there's a path of trust to the author. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/verify-commit.c | 5 +++-- builtin/verify-tag.c | 5 +++-- commit.c | 8 ++++++-- commit.h | 2 +- gpg-interface.c | 4 +++- gpg-interface.h | 2 +- t/t7510-signed-commit.sh | 2 +- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index ec0c4e3d836..e30f7cfbc16 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -21,10 +21,11 @@ static const char * const verify_commit_usage[] = { static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, int verbose) { struct signature_check signature_check; + int ret; memset(&signature_check, 0, sizeof(signature_check)); - check_commit_signature(lookup_commit(sha1), &signature_check); + ret = check_commit_signature(lookup_commit(sha1), &signature_check); if (verbose && signature_check.payload) fputs(signature_check.payload, stdout); @@ -33,7 +34,7 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l fputs(signature_check.gpg_output, stderr); signature_check_clear(&signature_check); - return signature_check.result != 'G'; + return ret; } static int verify_commit(const char *name, int verbose) diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index e1eb341baea..8750bef0163 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -22,6 +22,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) { struct signature_check sigc; int len; + int ret; memset(&sigc, 0, sizeof(sigc)); @@ -32,11 +33,11 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) if (size == len) return error("no signature found"); - check_signature(buf, len, buf + len, size - len, &sigc); + ret = check_signature(buf, len, buf + len, size - len, &sigc); fputs(sigc.gpg_output, stderr); signature_check_clear(&sigc); - return sigc.result != 'G' && sigc.result != 'U'; + return ret; } static int verify_tag(const char *name, int verbose) diff --git a/commit.c b/commit.c index d07a9849850..909419a13b7 100644 --- a/commit.c +++ b/commit.c @@ -1227,20 +1227,24 @@ free_return: free(buf); } -void check_commit_signature(const struct commit *commit, struct signature_check *sigc) +int check_commit_signature(const struct commit *commit, struct signature_check *sigc) { struct strbuf payload = STRBUF_INIT; struct strbuf signature = STRBUF_INIT; + int ret = 1; sigc->result = 'N'; if (parse_signed_commit(commit, &payload, &signature) <= 0) goto out; - check_signature(payload.buf, payload.len, signature.buf, signature.len, sigc); + ret = check_signature(payload.buf, payload.len, signature.buf, + signature.len, sigc); out: strbuf_release(&payload); strbuf_release(&signature); + + return ret; } diff --git a/commit.h b/commit.h index 9f189cb0542..afa0f6f0416 100644 --- a/commit.h +++ b/commit.h @@ -375,7 +375,7 @@ extern void print_commit_list(struct commit_list *list, * at all. This may allocate memory for sig->gpg_output, sig->gpg_status, * sig->signer and sig->key. */ -extern void check_commit_signature(const struct commit *commit, struct signature_check *sigc); +extern int check_commit_signature(const struct commit *commit, struct signature_check *sigc); int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused); diff --git a/gpg-interface.c b/gpg-interface.c index 66dbee25b3c..77a4da627e2 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -60,7 +60,7 @@ void parse_gpg_output(struct signature_check *sigc) } } -void check_signature(const char *payload, size_t plen, const char *signature, +int check_signature(const char *payload, size_t plen, const char *signature, size_t slen, struct signature_check *sigc) { struct strbuf gpg_output = STRBUF_INIT; @@ -81,6 +81,8 @@ void check_signature(const char *payload, size_t plen, const char *signature, out: strbuf_release(&gpg_status); strbuf_release(&gpg_output); + + return sigc->result != 'G' && sigc->result != 'U'; } /* diff --git a/gpg-interface.h b/gpg-interface.h index 043bcaa6306..e2aabde305b 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -27,7 +27,7 @@ extern int verify_signed_buffer(const char *payload, size_t payload_size, const extern int git_gpg_config(const char *, const char *, void *); extern void set_signing_key(const char *); extern const char *get_signing_key(void); -extern void check_signature(const char *payload, size_t plen, +extern int check_signature(const char *payload, size_t plen, const char *signature, size_t slen, struct signature_check *sigc); #endif diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 4fc376edb6a..796138fc3da 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -81,7 +81,7 @@ test_expect_success GPG 'verify and show signatures' ' ) ' -test_expect_failure GPG 'verify-commit exits success on untrusted signature' ' +test_expect_success GPG 'verify-commit exits success on untrusted signature' ' git verify-commit eighth-signed-alt 2>actual && grep "Good signature from" actual && ! grep "BAD signature from" actual && From ca194d50b84b53a0b711fef46d1a47657ec5da41 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 21 Jun 2015 23:14:41 +0000 Subject: [PATCH 5/7] gpg: centralize printing signature buffers The code to handle printing of signature data from a struct signature_check is very similar between verify-commit and verify-tag. Place this in a single function. verify-tag retains its special case behavior of printing the tag even when no valid signature is found. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/verify-commit.c | 7 +------ builtin/verify-tag.c | 9 +++++---- gpg-interface.c | 9 +++++++++ gpg-interface.h | 3 +++ 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index e30f7cfbc16..016319ada38 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -26,12 +26,7 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l memset(&signature_check, 0, sizeof(signature_check)); ret = check_commit_signature(lookup_commit(sha1), &signature_check); - - if (verbose && signature_check.payload) - fputs(signature_check.payload, stdout); - - if (signature_check.gpg_output) - fputs(signature_check.gpg_output, stderr); + print_signature_buffer(&signature_check, verbose ? GPG_VERIFY_VERBOSE : 0); signature_check_clear(&signature_check); return ret; diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 8750bef0163..d67e3dbd10f 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -27,14 +27,15 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) memset(&sigc, 0, sizeof(sigc)); len = parse_signature(buf, size); - if (verbose) - write_in_full(1, buf, len); - if (size == len) + if (size == len) { + if (verbose) + write_in_full(1, buf, len); return error("no signature found"); + } ret = check_signature(buf, len, buf + len, size - len, &sigc); - fputs(sigc.gpg_output, stderr); + print_signature_buffer(&sigc, verbose ? GPG_VERIFY_VERBOSE : 0); signature_check_clear(&sigc); return ret; diff --git a/gpg-interface.c b/gpg-interface.c index 77a4da627e2..e764fb625ba 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -85,6 +85,15 @@ int check_signature(const char *payload, size_t plen, const char *signature, return sigc->result != 'G' && sigc->result != 'U'; } +void print_signature_buffer(const struct signature_check *sigc, unsigned flags) +{ + if (flags & GPG_VERIFY_VERBOSE && sigc->payload) + fputs(sigc->payload, stdout); + + if (sigc->gpg_output) + fputs(sigc->gpg_output, stderr); +} + /* * Look at GPG signed content (e.g. a signed tag object), whose * payload is followed by a detached signature on it. Return the diff --git a/gpg-interface.h b/gpg-interface.h index e2aabde305b..1207861e7a6 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -1,6 +1,8 @@ #ifndef GPG_INTERFACE_H #define GPG_INTERFACE_H +#define GPG_VERIFY_VERBOSE 1 + struct signature_check { char *payload; char *gpg_output; @@ -29,5 +31,6 @@ extern void set_signing_key(const char *); extern const char *get_signing_key(void); extern int check_signature(const char *payload, size_t plen, const char *signature, size_t slen, struct signature_check *sigc); +void print_signature_buffer(const struct signature_check *sigc, unsigned flags); #endif From aeff29dd4dab01b497b2a2cf73e982e846a5fe4c Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 21 Jun 2015 23:14:42 +0000 Subject: [PATCH 6/7] verify-commit: add option to print raw gpg status information verify-commit by default displays human-readable output on standard error. However, it can also be useful to get access to the raw gpg status information, which is machine-readable, allowing automated implementation of signing policy. Add a --raw option to make verify-commit produce the gpg status information on standard error instead of the human-readable format. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- Documentation/git-verify-commit.txt | 4 ++++ builtin/verify-commit.c | 15 +++++++++----- gpg-interface.c | 7 +++++-- gpg-interface.h | 1 + t/t7510-signed-commit.sh | 31 +++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/Documentation/git-verify-commit.txt b/Documentation/git-verify-commit.txt index 9413e2802a7..ecf4da16cf0 100644 --- a/Documentation/git-verify-commit.txt +++ b/Documentation/git-verify-commit.txt @@ -16,6 +16,10 @@ Validates the gpg signature created by 'git commit -S'. OPTIONS ------- +--raw:: + Print the raw gpg status output to standard error instead of the normal + human-readable output. + -v:: --verbose:: Print the contents of the commit object before validating it. diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index 016319ada38..38bedf8f9fe 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -18,7 +18,7 @@ static const char * const verify_commit_usage[] = { NULL }; -static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, int verbose) +static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, unsigned flags) { struct signature_check signature_check; int ret; @@ -26,13 +26,13 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l memset(&signature_check, 0, sizeof(signature_check)); ret = check_commit_signature(lookup_commit(sha1), &signature_check); - print_signature_buffer(&signature_check, verbose ? GPG_VERIFY_VERBOSE : 0); + print_signature_buffer(&signature_check, flags); signature_check_clear(&signature_check); return ret; } -static int verify_commit(const char *name, int verbose) +static int verify_commit(const char *name, unsigned flags) { enum object_type type; unsigned char sha1[20]; @@ -50,7 +50,7 @@ static int verify_commit(const char *name, int verbose) return error("%s: cannot verify a non-commit object of type %s.", name, typename(type)); - ret = run_gpg_verify(sha1, buf, size, verbose); + ret = run_gpg_verify(sha1, buf, size, flags); free(buf); return ret; @@ -67,8 +67,10 @@ static int git_verify_commit_config(const char *var, const char *value, void *cb int cmd_verify_commit(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; + unsigned flags = 0; const struct option verify_commit_options[] = { OPT__VERBOSE(&verbose, N_("print commit contents")), + OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW), OPT_END() }; @@ -79,11 +81,14 @@ int cmd_verify_commit(int argc, const char **argv, const char *prefix) if (argc <= i) usage_with_options(verify_commit_usage, verify_commit_options); + if (verbose) + flags |= GPG_VERIFY_VERBOSE; + /* 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_commit(argv[i++], verbose)) + if (verify_commit(argv[i++], flags)) had_error = 1; return had_error; } diff --git a/gpg-interface.c b/gpg-interface.c index e764fb625ba..3dc2fe397e3 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -87,11 +87,14 @@ int check_signature(const char *payload, size_t plen, const char *signature, void print_signature_buffer(const struct signature_check *sigc, unsigned flags) { + const char *output = flags & GPG_VERIFY_RAW ? + sigc->gpg_status : sigc->gpg_output; + if (flags & GPG_VERIFY_VERBOSE && sigc->payload) fputs(sigc->payload, stdout); - if (sigc->gpg_output) - fputs(sigc->gpg_output, stderr); + if (output) + fputs(output, stderr); } /* diff --git a/gpg-interface.h b/gpg-interface.h index 1207861e7a6..ea68885ad5b 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -2,6 +2,7 @@ #define GPG_INTERFACE_H #define GPG_VERIFY_VERBOSE 1 +#define GPG_VERIFY_RAW 2 struct signature_check { char *payload; diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 796138fc3da..18e5cf06630 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -88,6 +88,37 @@ test_expect_success GPG 'verify-commit exits success on untrusted signature' ' grep "not certified" actual ' +test_expect_success GPG 'verify signatures with --raw' ' + ( + for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed + do + git verify-commit --raw $commit 2>actual && + grep "GOODSIG" actual && + ! grep "BADSIG" actual && + echo $commit OK || exit 1 + done + ) && + ( + for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned + do + test_must_fail git verify-commit --raw $commit 2>actual && + ! grep "GOODSIG" actual && + ! grep "BADSIG" actual && + echo $commit OK || exit 1 + done + ) && + ( + for commit in eighth-signed-alt + do + git verify-commit --raw $commit 2>actual && + grep "GOODSIG" actual && + ! grep "BADSIG" actual && + grep "TRUST_UNDEFINED" actual && + echo $commit OK || exit 1 + done + ) +' + test_expect_success GPG 'show signed commit with signature' ' git show -s initial >commit && git show -s --show-signature initial >show && From e18443ece711564145d545b650851c4615717128 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 21 Jun 2015 23:14:43 +0000 Subject: [PATCH 7/7] verify-tag: add option to print raw gpg status information verify-tag by default displays human-readable output on standard error. However, it can also be useful to get access to the raw gpg status information, which is machine-readable, allowing automated implementation of signing policy. Add a --raw option to make verify-tag produce the gpg status information on standard error instead of the human-readable format. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- Documentation/git-verify-tag.txt | 4 ++++ builtin/verify-tag.c | 17 +++++++++++------ t/t7030-verify-tag.sh | 31 +++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Documentation/git-verify-tag.txt b/Documentation/git-verify-tag.txt index f88ba96f023..d590edcebd9 100644 --- a/Documentation/git-verify-tag.txt +++ b/Documentation/git-verify-tag.txt @@ -16,6 +16,10 @@ Validates the gpg signature created by 'git tag'. OPTIONS ------- +--raw:: + Print the raw gpg status output to standard error instead of the normal + human-readable output. + -v:: --verbose:: Print the contents of the tag object before validating it. diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index d67e3dbd10f..00663f6a300 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -18,7 +18,7 @@ static const char * const verify_tag_usage[] = { NULL }; -static int run_gpg_verify(const char *buf, unsigned long size, int verbose) +static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags) { struct signature_check sigc; int len; @@ -29,19 +29,19 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) len = parse_signature(buf, size); if (size == len) { - if (verbose) + if (flags & GPG_VERIFY_VERBOSE) write_in_full(1, buf, len); return error("no signature found"); } ret = check_signature(buf, len, buf + len, size - len, &sigc); - print_signature_buffer(&sigc, verbose ? GPG_VERIFY_VERBOSE : 0); + print_signature_buffer(&sigc, flags); signature_check_clear(&sigc); return ret; } -static int verify_tag(const char *name, int verbose) +static int verify_tag(const char *name, unsigned flags) { enum object_type type; unsigned char sha1[20]; @@ -61,7 +61,7 @@ static int verify_tag(const char *name, int verbose) if (!buf) return error("%s: unable to read file.", name); - ret = run_gpg_verify(buf, size, verbose); + ret = run_gpg_verify(buf, size, flags); free(buf); return ret; @@ -78,8 +78,10 @@ static int git_verify_tag_config(const char *var, const char *value, void *cb) int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; + unsigned flags = 0; const struct option verify_tag_options[] = { OPT__VERBOSE(&verbose, N_("print tag contents")), + OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW), OPT_END() }; @@ -90,11 +92,14 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) if (argc <= i) usage_with_options(verify_tag_usage, verify_tag_options); + if (verbose) + flags |= GPG_VERIFY_VERBOSE; + /* 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)) + if (verify_tag(argv[i++], flags)) had_error = 1; return had_error; } diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh index 632bc534404..4608e713438 100755 --- a/t/t7030-verify-tag.sh +++ b/t/t7030-verify-tag.sh @@ -81,4 +81,35 @@ test_expect_success GPG 'detect fudged signature' ' ! grep "Good signature from" actual1 ' +test_expect_success GPG 'verify signatures with --raw' ' + ( + for tag in initial second merge fourth-signed sixth-signed seventh-signed + do + git verify-tag --raw $tag 2>actual && + grep "GOODSIG" actual && + ! grep "BADSIG" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in fourth-unsigned fifth-unsigned sixth-unsigned + do + test_must_fail git verify-tag --raw $tag 2>actual && + ! grep "GOODSIG" actual && + ! grep "BADSIG" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in eighth-signed-alt + do + git verify-tag --raw $tag 2>actual && + grep "GOODSIG" actual && + ! grep "BADSIG" actual && + grep "TRUST_UNDEFINED" actual && + echo $tag OK || exit 1 + done + ) +' + test_done