Merge branch 'jc/push-cert' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 27 Apr 2015 19:23:47 +0000 (12:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Apr 2015 19:23:50 +0000 (12:23 -0700)
The "git push --signed" protocol extension did not limit what the
"nonce" that is a server-chosen string can contain or how long it
can be, which was unnecessarily lax. Limit both the length and the
alphabet to a reasonably small space that can still have enough
entropy.

* jc/push-cert:
push --signed: tighten what the receiving end can ask to sign

1  2 
send-pack.c
diff --combined send-pack.c
index 25947d7df9dd3af6e665d5d5eb63ec970617aef1,22498080271a5702a7e79a72af1f1f8491d15121..677bac31930298a7cc4c0087aef8b62fb10249f2
@@@ -47,9 -47,8 +47,9 @@@ static int pack_objects(int fd, struct 
                NULL,
                NULL,
                NULL,
 +              NULL,
        };
 -      struct child_process po;
 +      struct child_process po = CHILD_PROCESS_INIT;
        int i;
  
        i = 4;
@@@ -61,8 -60,7 +61,8 @@@
                argv[i++] = "-q";
        if (args->progress)
                argv[i++] = "--progress";
 -      memset(&po, 0, sizeof(po));
 +      if (is_repository_shallow())
 +              argv[i++] = "--shallow";
        po.argv = argv;
        po.in = -1;
        po.out = args->stateless_rpc ? -1 : fd;
@@@ -234,15 -232,15 +234,15 @@@ static int generate_push_cert(struct st
                              const char *push_cert_nonce)
  {
        const struct ref *ref;
 -      char stamp[60];
        char *signing_key = xstrdup(get_signing_key());
        const char *cp, *np;
        struct strbuf cert = STRBUF_INIT;
        int update_seen = 0;
  
 -      datestamp(stamp, sizeof(stamp));
        strbuf_addf(&cert, "certificate version 0.1\n");
 -      strbuf_addf(&cert, "pusher %s %s\n", signing_key, stamp);
 +      strbuf_addf(&cert, "pusher %s ", signing_key);
 +      datestamp(&cert);
 +      strbuf_addch(&cert, '\n');
        if (args->url && *args->url) {
                char *anon_url = transport_anonymize_url(args->url);
                strbuf_addf(&cert, "pushee %s\n", anon_url);
@@@ -281,6 -279,28 +281,28 @@@ free_return
        return update_seen;
  }
  
+ #define NONCE_LEN_LIMIT 256
+ static void reject_invalid_nonce(const char *nonce, int len)
+ {
+       int i = 0;
+       if (NONCE_LEN_LIMIT <= len)
+               die("the receiving end asked to sign an invalid nonce <%.*s>",
+                   len, nonce);
+       for (i = 0; i < len; i++) {
+               int ch = nonce[i] & 0xFF;
+               if (isalnum(ch) ||
+                   ch == '-' || ch == '.' ||
+                   ch == '/' || ch == '+' ||
+                   ch == '=' || ch == '_')
+                       continue;
+               die("the receiving end asked to sign an invalid nonce <%.*s>",
+                   len, nonce);
+       }
+ }
  int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
                push_cert_nonce = server_feature_value("push-cert", &len);
                if (!push_cert_nonce)
                        die(_("the receiving end does not support --signed push"));
+               reject_invalid_nonce(push_cert_nonce, len);
                push_cert_nonce = xmemdupz(push_cert_nonce, len);
        }