From ffb6d7d5c99e4097e512def20b0133b7ee900953 Mon Sep 17 00:00:00 2001 From: Sebastian Götte Date: Sun, 31 Mar 2013 18:00:14 +0200 Subject: Move commit GPG signature verification to commit.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sebastian Götte Signed-off-by: Junio C Hamano --- commit.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index b4512ab0b2..66a3f4e8f4 100644 --- a/commit.c +++ b/commit.c @@ -1041,6 +1041,65 @@ free_return: free(buf); } +static struct { + char result; + const char *check; +} sigcheck_gpg_status[] = { + { 'G', "\n[GNUPG:] GOODSIG " }, + { 'B', "\n[GNUPG:] BADSIG " }, +}; + +static void parse_gpg_output(struct signature_check *sigc) +{ + const char *buf = sigc->gpg_status; + int i; + + for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) { + const char *found = strstr(buf, sigcheck_gpg_status[i].check); + const char *next; + if (!found) + continue; + sigc->result = sigcheck_gpg_status[i].result; + found += strlen(sigcheck_gpg_status[i].check); + sigc->key = xmemdupz(found, 16); + found += 17; + next = strchrnul(found, '\n'); + sigc->signer = xmemdupz(found, next - found); + break; + } +} + +void check_commit_signature(const struct commit* commit, struct signature_check *sigc) +{ + 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->object.sha1, + &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->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); + strbuf_release(&payload); + strbuf_release(&signature); +} + + + void append_merge_tag_headers(struct commit_list *parents, struct commit_extra_header ***tail) { -- cgit v1.2.3 From f8aae8d0efccd268babd482f10709b4f86a9f32e Mon Sep 17 00:00:00 2001 From: Sebastian Götte Date: Sun, 31 Mar 2013 18:01:34 +0200 Subject: commit.c/GPG signature verification: Also look at the first GPG status line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sebastian Götte Signed-off-by: Junio C Hamano --- commit.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 66a3f4e8f4..94029c9496 100644 --- a/commit.c +++ b/commit.c @@ -1054,13 +1054,20 @@ static void parse_gpg_output(struct signature_check *sigc) const char *buf = sigc->gpg_status; int i; + /* Iterate over all search strings */ for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) { - const char *found = strstr(buf, sigcheck_gpg_status[i].check); - const char *next; - if (!found) - continue; + const char *found, *next; + + if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) { + /* At the very beginning of the buffer */ + found = buf + strlen(sigcheck_gpg_status[i].check + 1); + } else { + found = strstr(buf, sigcheck_gpg_status[i].check); + if (!found) + continue; + found += strlen(sigcheck_gpg_status[i].check); + } sigc->result = sigcheck_gpg_status[i].result; - found += strlen(sigcheck_gpg_status[i].check); sigc->key = xmemdupz(found, 16); found += 17; next = strchrnul(found, '\n'); -- cgit v1.2.3 From eb307ae7bb78ccde4e2ac69f302ccf8834883628 Mon Sep 17 00:00:00 2001 From: Sebastian Götte Date: Sun, 31 Mar 2013 18:02:46 +0200 Subject: merge/pull Check for untrusted good GPG signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When --verify-signatures is specified, abort the merge in case a good GPG signature from an untrusted key is encountered. Signed-off-by: Sebastian Götte Signed-off-by: Junio C Hamano --- Documentation/merge-options.txt | 4 ++-- builtin/merge.c | 3 +++ commit.c | 14 +++++++++----- commit.h | 10 +++++----- gpg-interface.h | 1 + t/lib-gpg/pubring.gpg | Bin 1164 -> 2359 bytes t/lib-gpg/random_seed | Bin 600 -> 600 bytes t/lib-gpg/secring.gpg | Bin 1237 -> 3734 bytes t/lib-gpg/trustdb.gpg | Bin 1280 -> 1360 bytes t/t7612-merge-verify-signatures.sh | 9 +++++++++ 10 files changed, 29 insertions(+), 12 deletions(-) (limited to 'commit.c') diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 31f1067521..a0f022b41d 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -85,8 +85,8 @@ option can be used to override --squash. --verify-signatures:: --no-verify-signatures:: - Verify that the commits being merged have good GPG signatures and abort the - merge in case they do not. + Verify that the commits being merged have good and trusted GPG signatures + and abort the merge in case they do not. --summary:: --no-summary:: diff --git a/builtin/merge.c b/builtin/merge.c index e57c42c622..bac11d1605 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -1248,6 +1248,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) switch (signature_check.result) { case 'G': break; + case 'U': + die(_("Commit %s has an untrusted GPG signature, " + "allegedly by %s."), hex, signature_check.signer); case 'B': die(_("Commit %s has a bad GPG signature " "allegedly by %s."), hex, signature_check.signer); diff --git a/commit.c b/commit.c index 94029c9496..516a4ff7d2 100644 --- a/commit.c +++ b/commit.c @@ -1047,6 +1047,8 @@ static struct { } sigcheck_gpg_status[] = { { 'G', "\n[GNUPG:] GOODSIG " }, { 'B', "\n[GNUPG:] BADSIG " }, + { 'U', "\n[GNUPG:] TRUST_NEVER" }, + { 'U', "\n[GNUPG:] TRUST_UNDEFINED" }, }; static void parse_gpg_output(struct signature_check *sigc) @@ -1068,11 +1070,13 @@ static void parse_gpg_output(struct signature_check *sigc) found += strlen(sigcheck_gpg_status[i].check); } sigc->result = sigcheck_gpg_status[i].result; - sigc->key = xmemdupz(found, 16); - found += 17; - next = strchrnul(found, '\n'); - sigc->signer = xmemdupz(found, next - found); - break; + /* The trust messages are not followed by key/signer information */ + if (sigc->result != 'U') { + sigc->key = xmemdupz(found, 16); + found += 17; + next = strchrnul(found, '\n'); + sigc->signer = xmemdupz(found, next - found); + } } } diff --git a/commit.h b/commit.h index c24b844ad6..87b4b6cc0c 100644 --- a/commit.h +++ b/commit.h @@ -234,11 +234,11 @@ extern void print_commit_list(struct commit_list *list, const char *format_last); /* - * Check the signature of the given commit. The result of the check is stored in - * sig->result, 'G' for a good signature, 'B' for a bad signature and 'N' - * for no signature at all. - * This may allocate memory for sig->gpg_output, sig->gpg_status, sig->signer - * and sig->key. + * Check the signature of the given commit. The result of the check is stored + * in sig->check_result, 'G' for a good signature, 'U' for a good signature + * from an untrusted signer, 'B' for a bad signature and 'N' for no signature + * 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); diff --git a/gpg-interface.h b/gpg-interface.h index 5884aa4052..a85cb5bc97 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -6,6 +6,7 @@ struct signature_check { char *gpg_status; char result; /* 0 (not checked), * N (checked but no further result), + * U (untrusted good), * G (good) * B (bad) */ char *signer; diff --git a/t/lib-gpg/pubring.gpg b/t/lib-gpg/pubring.gpg index 83855fa4e1..1a3c2d487c 100644 Binary files a/t/lib-gpg/pubring.gpg and b/t/lib-gpg/pubring.gpg differ diff --git a/t/lib-gpg/random_seed b/t/lib-gpg/random_seed index 8fed1339ed..95d249f15f 100644 Binary files a/t/lib-gpg/random_seed and b/t/lib-gpg/random_seed differ diff --git a/t/lib-gpg/secring.gpg b/t/lib-gpg/secring.gpg index d831cd9eb3..82dca8f80b 100644 Binary files a/t/lib-gpg/secring.gpg and b/t/lib-gpg/secring.gpg differ diff --git a/t/lib-gpg/trustdb.gpg b/t/lib-gpg/trustdb.gpg index abace962b8..4879ae9a84 100644 Binary files a/t/lib-gpg/trustdb.gpg and b/t/lib-gpg/trustdb.gpg differ diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh index 6ccfbf367a..21a0bf8fb8 100755 --- a/t/t7612-merge-verify-signatures.sh +++ b/t/t7612-merge-verify-signatures.sh @@ -27,6 +27,10 @@ test_expect_success GPG 'create signed commits' ' git hash-object -w -t commit forged >forged.commit && git checkout initial && + git checkout -b side-untrusted && + echo 3 >baz && git add baz && + test_tick && git commit -SB7227189 -m "untrusted on side" + git checkout master ' @@ -40,6 +44,11 @@ test_expect_success GPG 'merge commit with bad signature with verification' ' test_i18ngrep "has a bad GPG signature" mergeerror ' +test_expect_success GPG 'merge commit with untrusted signature with verification' ' + test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror && + test_i18ngrep "has an untrusted GPG signature" mergeerror +' + test_expect_success GPG 'merge signed commit with verification' ' git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput && test_i18ngrep "has a good GPG signature" mergeoutput -- cgit v1.2.3