git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: git@vger.kernel.org
Subject: [PATCH v2 16/19] receive-pack: GPG-validate push certificates
Date: Fri, 22 Aug 2014 13:30:21 -0700	[thread overview]
Message-ID: <1408739424-31429-17-git-send-email-gitster@pobox.com> (raw)
In-Reply-To: <1408739424-31429-1-git-send-email-gitster@pobox.com>

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 <gitster@pobox.com>
---
 Documentation/git-receive-pack.txt | 27 ++++++++++++++++++++++-----
 builtin/receive-pack.c             | 29 +++++++++++++++++++++++++++++
 t/t5534-push-signed.sh             | 18 ++++++++++++++++--
 3 files changed, 67 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index 6c458af..60151a6 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -56,7 +56,21 @@ sha1-old and sha1-new should be valid objects in the repository.
 When accepting a signed push (see linkgit:git-push[1]), the signed
 push certificate is stored in a blob and an environment variable
 `GIT_PUSH_CERT` can be consulted for its object name.  See the
-description of `post-receive` hook for an example.
+description of `post-receive` hook for an example.  In addition, the
+certificate is verified using GPG and the result is exported with
+the following environment variables:
+
+GIT_PUSH_CERT_SIGNER::
+	The name and the e-mail address of the owner of the key that
+	signed the push certificate.
+
+GIT_PUSH_CERT_KEY::
+	The GPG key ID of the key that signed the push certificate.
+
+GIT_PUSH_CERT_STATUS::
+	The status of GPG verification of the push certificate,
+	using the same mnemonic as used in `%G?` format of `git log`
+	family of commands (see linkgit:git-log[1]).
 
 This hook is called before any refname is updated and before any
 fast-forward checks are performed.
@@ -106,13 +120,14 @@ the update.  Refs that were created will have sha1-old equal to
 0\{40}, otherwise sha1-old and sha1-new should be valid objects in
 the repository.
 
-The `GIT_PUSH_CERT` environment variable can be inspected, just as
+The `GIT_PUSH_CERT*` environment variables can be inspected, just as
 in `pre-receive` hook, after accepting a signed push.
 
 Using this hook, it is easy to generate mails describing the updates
 to the repository.  This example script sends one mail message per
 ref listing the commits pushed to the repository, and logs the push
-certificates of signed pushes to a file:
+certificates of signed pushes with good signatures to a logger
+service:
 
 	#!/bin/sh
 	# mail out commit update information.
@@ -129,9 +144,11 @@ certificates of signed pushes to a file:
 		mail -s "Changes to ref $ref" commit-list@mydomain
 	done
 	# log signed push certificate, if any
-	if test -n "${GIT_PUSH_CERT-}"
+	if test -n "${GIT_PUSH_CERT-}" && test ${GIT_PUSH_CERT_STATUS} = G
 	then
-		git cat-file blob ${GIT_PUSH_CERT} >>/var/log/push-log
+		(
+			git cat-file blob ${GIT_PUSH_CERT}
+		) | mail -s "push from $GIT_PUSH_CERT_SIGNER" push-log@mydomain
 	fi
 	exit 0
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index f30df8a..abdc296 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 <git-dir>";
 
@@ -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 0eb5004..659bca0 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -52,12 +52,26 @@ test_expect_success GPG '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 <<E_O_F
+	SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+	KEY=${GIT_PUSH_CERT_KEY-nokey}
+	STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+	E_O_F
+
+	EOF
+
+	cat >expect <<-\EOF &&
+	SIGNER=C O Mitter <committer@example.com>
+	KEY=13B6F51ECDDE430D
+	STATUS=G
 	EOF
 
 	git push --signed dst noop ff +noff &&
 	grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert &&
-	grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert
+	grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert &&
+	test_cmp expect dst/push-cert-status
 '
 
 test_done
-- 
2.1.0-304-g950f846

  parent reply	other threads:[~2014-08-22 20:33 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-08-22 20:30 [PATCH v2 00/19] Signed push Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 01/19] receive-pack: do not overallocate command structure Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 02/19] receive-pack: parse feature request a bit earlier Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 03/19] receive-pack: do not reuse old_sha1[] for other things Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 04/19] receive-pack: factor out queueing of command Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 05/19] send-pack: move REF_STATUS_REJECT_NODELETE logic a bit higher Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 06/19] send-pack: refactor decision to send update per ref Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 07/19] send-pack: always send capabilities Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 08/19] send-pack: factor out capability string generation Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 09/19] send-pack: rename "new_refs" to "need_pack_data" Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 10/19] send-pack: refactor inspecting and resetting status and sending commands Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 11/19] send-pack: clarify that cmds_sent is a boolean Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 12/19] gpg-interface: move parse_gpg_output() to where it should be Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 13/19] gpg-interface: move parse_signature() " Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 14/19] pack-protocol doc: typofix for PKT-LINE Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 15/19] the beginning of the signed push Junio C Hamano
2014-08-22 20:30 ` Junio C Hamano [this message]
2014-08-22 20:30 ` [PATCH v2 17/19] send-pack: send feature request on push-cert packet Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 18/19] signed push: remove duplicated protocol info Junio C Hamano
2014-08-22 20:30 ` [PATCH v2 19/19] signed push: fortify against replay attacks Junio C Hamano
2014-08-24  3:29   ` Eric Sunshine
2014-08-30 11:59   ` Stefan Beller
2014-09-02 17:40     ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1408739424-31429-17-git-send-email-gitster@pobox.com \
    --to=gitster@pobox.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).