On Fri, Aug 22, 2014 at 10:59 AM, Junio C Hamano <gits...@pobox.com> wrote:
> Junio C Hamano <gits...@pobox.com> writes:
>
>> There are a few gotchas I can certainly use help on, especially from
>> a smart-http expert ;-).
>>
>>  * "pushed-to <URL>" will identify the site and the repository, so
>>    you cannot MITM my push to an experimental server and replay it
>>    against the authoritative server.
>>
>>    However, the receiving end may not even know what name its users
>>    call the repository being pushed into.  Obviously gethostname()
>>    may not be what the pusher called us, and getcwd() may not match
>>    the repository name without leading "/var/repos/shard3/" path
>>    components stripped, for example.
>>
>>    I am not sure if we even have the necessary information at
>>    send-pack.c::send_pack() level, where it already has an
>>    established connection to the server (hence it does not need to
>>    know to whom it is talking to).
>>
>>
>>  * The receiving end will issue "push-cert=<nonce>" in its initial
>>    capability advertisement, and this <nonce> will be given on the
>>    PUSH_CERT_NONCE environment to the pre/post-receive hooks, to
>>    allow the "nonce <nonce>" header in the signed certificate to be
>>    checked against it.  You cannot capture my an earlier push to the
>>    authoritative server and replay it later.
>>
>>    That would all work well within a single receive-pack process,
>>    but with "stateless" RPC, it is unclear to me how we should
>>    arrange the <nonce> the initial instance of receive-pack placed
>>    on its capability advertisement to be securely passed to the
>>    instance of receive-pack that actually receives the push
>>    certificate.
>
> A good <nonce> may be something like taking the SHA-1 hash of the
> concatenation of the sitename, repo-path and the timestamp when the
> receive-pack generated the <nonce>.  Replaying a push certificate
> for a push to a repository at a site that gives such a <nonce> can
> succeed at the same chance of finding a SHA-1 collision [*1*].  As
> long as you exercise good hygiene and only push to repositories that
> give such <nonce>, we can do without checking "pushed-to" that says
> where the push went.

Yes, this is an interesting solution.

As you know, the stateless HTTP thing doesn't allow the nonce on the
server to be carried from the initial ref advertisement into the final
receive-pack. We would either need to write the nonce to disk and load
it back up later (ick), or use some sort of stateless nonce.

A stateless nonce could look like:

  nonce = HMAC_SHA1( SHA1(site+path) + '.' + now, site_key )

where site_key is a private key known to the server. It doesn't have
to be per-repo.

receive-pack would then be willing to accept any nonce whose timestamp
is within a window, e.g. 10 minutes of the current time, and whose
signature verifies in the HMAC. The 10 minute window is important to
allow clients time to generate the object list, perform delta
compression, and begin transmitting to the server.

> So "nonce <nonce>" is the only thing that is necessary to make them
> impossible to replay.  For auditing purposes, "pushed-to <URL>" that
> records the repository the pusher intended to push to may help but
> probably not necessary [*2*].

So pushed-to is still useful as a documentation/historical artifact,
but isn't important for verifying the certificate. That fixes a lot of
problems with repositories having different paths under e.g. git://
vs. ssh:// vs. https:// on the same host.
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to