From a3ac15ca7d1457c9ec947c45f2942afe2d8f4a04 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 14 Aug 2014 15:59:21 -0700 Subject: receive-pack: GPG-validate push certificates Reusing the GPG signature check helpers we already have, verify the signature in receive-pack and give the results to the hooks via GIT_PUSH_CERT_{SIGNER,KEY,STATUS} environment variables. Policy decisions, such as accepting or rejecting a good signature by a key that is not fully trusted, is left to the hook and kept outside of the core. Signed-off-by: Junio C Hamano --- builtin/receive-pack.c | 29 +++++++++++++++++++++++++++++ t/t5534-push-signed.sh | 18 ++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 469e344ea0..1577aacfbc 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -15,6 +15,8 @@ #include "connected.h" #include "argv-array.h" #include "version.h" +#include "tag.h" +#include "gpg-interface.h" static const char receive_pack_usage[] = "git receive-pack "; @@ -48,6 +50,7 @@ static int shallow_update; static const char *alt_shallow_file; static struct strbuf push_cert = STRBUF_INIT; static unsigned char push_cert_sha1[20]; +static struct signature_check sigcheck; static enum deny_action parse_deny_action(const char *var, const char *value) { @@ -260,12 +263,38 @@ static void prepare_push_cert_sha1(struct child_process *proc) struct argv_array env = ARGV_ARRAY_INIT; if (!already_done) { + struct strbuf gpg_output = STRBUF_INIT; + struct strbuf gpg_status = STRBUF_INIT; + int bogs /* beginning_of_gpg_sig */; + already_done = 1; if (write_sha1_file(push_cert.buf, push_cert.len, "blob", push_cert_sha1)) hashclr(push_cert_sha1); + + memset(&sigcheck, '\0', sizeof(sigcheck)); + sigcheck.result = 'N'; + + bogs = parse_signature(push_cert.buf, push_cert.len); + if (verify_signed_buffer(push_cert.buf, bogs, + push_cert.buf + bogs, push_cert.len - bogs, + &gpg_output, &gpg_status) < 0) { + ; /* error running gpg */ + } else { + sigcheck.payload = push_cert.buf; + sigcheck.gpg_output = gpg_output.buf; + sigcheck.gpg_status = gpg_status.buf; + parse_gpg_output(&sigcheck); + } + + strbuf_release(&gpg_output); + strbuf_release(&gpg_status); } if (!is_null_sha1(push_cert_sha1)) { argv_array_pushf(&env, "GIT_PUSH_CERT=%s", sha1_to_hex(push_cert_sha1)); + argv_array_pushf(&env, "GIT_PUSH_CERT_SIGNER=%s", sigcheck.signer); + argv_array_pushf(&env, "GIT_PUSH_CERT_KEY=%s", sigcheck.key); + argv_array_pushf(&env, "GIT_PUSH_CERT_STATUS=%c", sigcheck.result); + proc->env = env.argv; } } diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh index 9ee5c29cc8..3acc8648f1 100755 --- a/t/t5534-push-signed.sh +++ b/t/t5534-push-signed.sh @@ -52,13 +52,27 @@ test_expect_success 'signed push sends push certificate' ' if test -n "${GIT_PUSH_CERT-}" then git cat-file blob $GIT_PUSH_CERT >../push-cert - fi + fi && + + cat >../push-cert-status <expect <<-\EOF && + SIGNER=C O Mitter + KEY=13B6F51ECDDE430D + STATUS=G EOF git push --signed dst noop ff +noff && grep "update $(git rev-parse noop ff) refs/heads/ff" dst/push-cert && - grep "update $(git rev-parse noop noff) refs/heads/noff" dst/push-cert + grep "update $(git rev-parse noop noff) refs/heads/noff" dst/push-cert && + test_cmp expect dst/push-cert-status ' test_done -- cgit v1.2.1