Subject: Re: [PATCH 16/18] receive-pack: GPG-validate push certificates

From: Junio C Hamano <[email-masked]>
Date: Wed, 20 Aug 2014 12:38:31 -0700

David Turner <[email-masked]> writes:

> On Wed, 2014-08-20 at 10:29 -0700, Junio C Hamano wrote:
>> On Wed, Aug 20, 2014 at 9:56 AM, David Turner <[email-masked]> wrote:
>> > On Tue, 2014-08-19 at 15:06 -0700, Junio C Hamano wrote:
>> >> 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.
>> >
>> > If I understand correctly, the hook does not have enough information to
>> > make this decision, because it is missing the date from the signature.
>> 
>> The full certificate is available to the hook so anything we can do the hook
>> has enough information to do ;-)  But of course we should try to make it
>> easier for the hook to validate the request.
>
> Excellent, then motivated hooks can do the right thing.
>
>> > This might allow an old signed push to be replayed, moving the head of a
>> > branch to an older state (say, one lacking the latest security updates).
>> 
>> ... with old-sha1 recorded in the certificate?
>
> That does prevent most replays, but it does not prevent resurrection of
> a deleted branch by a replay of its initial creation (nor an undo of a
> force-push to rollback).  So I think we still need timestamps, but
> parsing them out of the cert is not terrible.

As I aleady mentioned elsewhere, a more problematic thing about the
push certificate as presented in 15/18 is that it does not say
anything about where the push is going.  If you can capture a trial
push to some random test repository I do with my signed push
certificate, you could replay it to my public repository hosted at
a more official site (say, k.org in the far distant future where it
does not rely on ssh authentication to protect their services but
uses the GPG signature on the push certificate to make sure it is I
who is pushing).

We can add a new "pushed-to <repository URL>" header line to the
certificate, next to "pushed-by <ident> <time>", and have the
receiving end verify that it matches to prevent such a replay.  I
wonder if we can further extend it to avoid replays to the same
repository.

Instead of "pushed-to", we can tweak the capability advertisement
sent from the server upon initial contact to advertise not just
"push-cert", but "push-cert=<nonce>", add a new "push-nonce <nonce>"
header to the certificate and then have the receiving end make sure
they are the same.  That way, the receiver can make sure it is not
being fed a certificate used when a different push was done to it or
somebody else and by doing so we do not even need "pushed-to
<repository URL>" header, perhaps?

I am still fuzzy how robust such a scheme be against MITM, though.
One way I can think of to attack the "nonce-only" scheme would be to
create a "you can push anything here" service, convince me to push
garbage there, and when I try to push to it, it can turn around and
act as a client to some high-value site the attacker does not even
control, grab the <nonce>, relay it back to me and advertise the
same <nonce>, have me sign the certificate to push garbage, and
relay that push session to the high-value target.

I am not sure if that is a valid threat model we would care about,
but with "pushed-to <repository URL>" the high-value target site can
notice that I am pushing garbage to the joker site and reject the
certificate.