With the interim protocol, we used to send the update commands even
though we already send a signed copy of the same information when
push certificate is in use.  Update the send-pack/receive-pack pair
not to do so.

The notable thing on the receive-pack side is that it makes sure
that there is no command sent over the traditional protocol packet
outside the push certificate.  Otherwise a pusher can claim to be
pushing one set of ref updates in the signed certificate while
issuing commands to update unrelated refs, and such an update will
evade later audits.

Signed-off-by: Junio C Hamano <gits...@pobox.com>
 Documentation/technical/pack-protocol.txt         | 20 ++++++++++++++++-
 Documentation/technical/protocol-capabilities.txt | 12 +++++++++--
 builtin/receive-pack.c                            | 26 +++++++++++++++++++++++
 send-pack.c                                       |  2 +-
 4 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/Documentation/technical/pack-protocol.txt 
index a845d51..f6c3073 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -465,7 +465,7 @@ contain all the objects that the server will need to 
complete the new
-  update-request    =  *shallow command-list [pack-file]
+  update-request    =  *shallow ( command-list | push-cert ) [pack-file]
   shallow           =  PKT-LINE("shallow" SP obj-id)
@@ -481,12 +481,30 @@ references.
   old-id            =  obj-id
   new-id            =  obj-id
+  push-cert         = PKT-LINE("push-cert" NUL capability-list LF)
+                     PKT-LINE("certificate version 0.1" LF)
+                     PKT-LINE("pusher" ident LF)
+                     PKT-LINE(LF)
+                     *PKT-LINE(command LF)
+                     *PKT-LINE(GPG signature lines LF)
+                     PKT-LINE("push-cert-end" LF)
   pack-file         = "PACK" 28*(OCTET)
 If the receiving end does not support delete-refs, the sending end MUST
 NOT ask for delete command.
+If the receiving end does not support push-cert, the sending end MUST
+NOT send a push-cert command.
+When a push-cert command is sent, command-list MUST NOT be sent; the
+commands recorded in the push certificate is used instead.  The GPG
+signature lines are detached signature for the contents recorded in
+the push certificate before the signature block begins and is used
+to certify that the commands were given by the pusher which must be
+the signer.
 The pack-file MUST NOT be sent if the only command used is 'delete'.
 A pack-file MUST be sent if either create or update command is used,
diff --git a/Documentation/technical/protocol-capabilities.txt 
index e174343..5b398e9 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -18,8 +18,8 @@ was sent.  Server MUST NOT ignore capabilities that client 
 and server advertised.  As a consequence of these rules, server MUST
 NOT advertise capabilities it does not understand.
-The 'report-status', 'delete-refs', and 'quiet' capabilities are sent and
-recognized by the receive-pack (push to server) process.
+The 'report-status', 'delete-refs', 'quiet', and 'push-cert' capabilities
+are sent and recognized by the receive-pack (push to server) process.
 The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
 by both upload-pack and receive-pack protocols.  The 'agent' capability
@@ -250,3 +250,11 @@ allow-tip-sha1-in-want
 If the upload-pack server advertises this capability, fetch-pack may
 send "want" lines with SHA-1s that exist at the server but are not
 advertised by upload-pack.
+The receive-pack server that advertises this capability is willing
+to accept a signed push certificate.  A send-pack client MUST NOT
+send push-cert packet unless the receive-pack server advertises this
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index abdc296..96e4c99 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -907,6 +907,28 @@ static struct command **queue_command(struct command **p,
        return &cmd->next;
+static void queue_commands_from_cert(struct command **p,
+                                    struct strbuf *push_cert)
+       const char *boc, *eoc;
+       if (*p)
+               die("protocol error: got both push certificate and unsigned 
+       boc = strstr(push_cert->buf, "\n\n");
+       if (!boc)
+               die("malformed push certificate %.*s", 100, push_cert->buf);
+       else
+               boc += 2;
+       eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len);
+       while (boc < eoc) {
+               const char *eol = memchr(boc, '\n', eoc - boc);
+               p = queue_command(p, boc, eol ? eol - boc : eoc - eol);
+               boc = eol ? eol + 1 : eoc;
+       }
 static struct command *read_head_info(struct sha1_array *shallow)
        struct command *commands = NULL;
@@ -962,6 +984,10 @@ static struct command *read_head_info(struct sha1_array 
                p = queue_command(p, line, linelen);
+       if (push_cert.len)
+               queue_commands_from_cert(p, &push_cert);
        return commands;
diff --git a/send-pack.c b/send-pack.c
index 2de9826..ca2ae2b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -365,7 +365,7 @@ int send_pack(struct send_pack_args *args,
        for (ref = remote_refs; ref; ref = ref->next) {
                char *old_hex, *new_hex;
-               if (args->dry_run)
+               if (args->dry_run || args->push_cert)
                if (!ref_update_to_be_sent(ref, args))

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