* [PATCH 5/6] Add MD support for fetch, pull, and push.
@ 2011-12-21 7:12 Bill Zaumen
0 siblings, 0 replies; only message in thread
From: Bill Zaumen @ 2011-12-21 7:12 UTC (permalink / raw)
To: git, peff, pclouds, gitster
A new capability, "mds-check" is defined. When present, a client will
(when possible and useful) send the server a SHA-1 value and a message
digest, separated by a '-'. This is used to detect hash collisions,
with a goal of finding problems early if a malicious attempt is made
to forge commits with different commits with the same SHA-1 value in
different repositories. It is a simple, fast test - a look-up and a
comparison.
Signed-off-by: Bill Zaumen <bill.zaumen+git@gmail.com>
---
builtin/fetch-pack.c | 29 +++++++++++-
builtin/receive-pack.c | 117 +++++++++++++++++++++++++++++++++++++++++++-----
builtin/send-pack.c | 26 ++++++++++-
http.c | 19 ++++++--
t/t5500-fetch-pack.sh | 10 +++--
t/t5510-fetch.sh | 12 ++++-
upload-pack.c | 11 ++++-
7 files changed, 196 insertions(+), 28 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 6207ecd..2f5b7ef 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -18,6 +18,8 @@ static int prefer_ofs_delta = 1;
static int no_done;
static int fetch_fsck_objects = -1;
static int transfer_fsck_objects = -1;
+static int mds_check = 0;
+
static struct fetch_pack_args args = {
/* .uploadpack = */ "git-upload-pack",
};
@@ -390,9 +392,25 @@ static int find_common(int fd[2], unsigned char *result_sha1,
flushes = 0;
retval = -1;
while ((sha1 = get_rev())) {
- packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
- if (args.verbose)
- fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
+ if (mds_check) {
+ mdigest_t digest;
+ if (has_sha1_file_digest(sha1, &digest)) {
+ packet_buf_write(&req_buf, "have %s\n",
+ sha1_to_hex_digest(sha1,
+ &digest));
+
+ } else {
+ packet_buf_write(&req_buf, "have %s\n",
+ sha1_to_hex(sha1));
+ }
+ if (args.verbose)
+ fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
+ } else {
+ packet_buf_write(&req_buf, "have %s\n",
+ sha1_to_hex(sha1));
+ if (args.verbose)
+ fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
+ }
in_vain++;
if (flush_at <= ++count) {
int ack;
@@ -807,6 +825,11 @@ static struct ref *do_fetch_pack(int fd[2],
fprintf(stderr, "Server supports ofs-delta\n");
} else
prefer_ofs_delta = 0;
+ if (server_supports("mds-check")) {
+ if (args.verbose)
+ fprintf(stderr, "Server supports mds-check\n");
+ mds_check = 1;
+ }
if (everything_local(&ref, nr_match, match)) {
packet_flush(fd[1]);
goto all_done;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d2dcb7e..b9d1c1f 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -122,7 +122,8 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void
else
packet_write(1, "%s %s%c%s%s\n",
sha1_to_hex(sha1), path, 0,
- " report-status delete-refs side-band-64k",
+ " report-status delete-refs side-band-64k"
+ " mds-check",
prefer_ofs_delta ? " ofs-delta" : "");
sent_capabilities = 1;
return 0;
@@ -709,37 +710,131 @@ static struct command *read_head_info(void)
struct command *commands = NULL;
struct command **p = &commands;
for (;;) {
- static char line[1000];
+ static char line[1500];
unsigned char old_sha1[20], new_sha1[20];
struct command *cmd;
char *refname;
int len, reflen;
+ int has_old_sha1_digest = 0;
+ int has_new_sha1_digest = 0;
+ mdigest_t old_sha1_digest;
+ mdigest_t new_sha1_digest;
+ mdigest_t digest;
+ int old_hashlen = 40;
+ int new_hashlen = 40;
+ int hashlen = 81; /* includes blank between two hashes */
+ int digest_field_len = 0;
+
+ mdigest_clear(&old_sha1_digest);
+ mdigest_clear(&new_sha1_digest);
len = packet_read_line(0, line, sizeof(line));
if (!len)
break;
if (line[len-1] == '\n')
line[--len] = 0;
- if (len < 83 ||
- line[40] != ' ' ||
- line[81] != ' ' ||
- get_sha1_hex(line, old_sha1) ||
- get_sha1_hex(line + 41, new_sha1))
+ if (len > (old_hashlen + 1) && line[old_hashlen] == '-') {
+ digest_field_len = 1
+ + get_hex_field_size(line+old_hashlen+1);
+ }
+ if (len > (old_hashlen + digest_field_len) &&
+ line[old_hashlen] == '-') {
+ old_hashlen += digest_field_len;
+ hashlen += digest_field_len;
+ digest_field_len = 0;
+ if (len > (old_hashlen + 1)
+ && line[old_hashlen] == '-') {
+ digest_field_len = 1 +
+ get_hex_field_size(line+old_hashlen+1);
+ }
+ if (len > (old_hashlen + digest_field_len + 1) &&
+ line[old_hashlen] == '-') {
+ old_hashlen += digest_field_len;
+ hashlen += digest_field_len;
+ }
+ }
+ if (line[old_hashlen] != ' ') {
+ die("protocol error: expected old/new/ref, got '%s'",
+ line);
+ }
+ digest_field_len = 0;
+ if (len > hashlen + 1 && line[hashlen] == '-') {
+ digest_field_len = 1 +
+ get_hex_field_size(line+hashlen+1);
+ }
+ if (len > (hashlen + digest_field_len + 1) &&
+ line[hashlen] == '-') {
+ new_hashlen += digest_field_len;
+ hashlen += digest_field_len;
+ digest_field_len = 0;
+ if (len > (hashlen + 1)
+ && line[hashlen] == '-') {
+ digest_field_len = 1 +
+ get_hex_field_size(line+hashlen+1);
+ }
+ if (len > (hashlen + digest_field_len + 1) &&
+ line[hashlen] == '-') {
+ new_hashlen += digest_field_len;
+ hashlen += digest_field_len;
+ }
+ }
+ if (line[hashlen] != ' ') {
die("protocol error: expected old/new/ref, got '%s'",
line);
+ }
+
+ if (len < hashlen + 1 ||
+ line[old_hashlen] != ' ' ||
+ line[hashlen] != ' ' ||
+ get_sha1_hex_digest(line, old_sha1,
+ &has_old_sha1_digest, &old_sha1_digest) ||
+ get_sha1_hex_digest(line + old_hashlen + 1, new_sha1,
+ &has_new_sha1_digest,
+ &new_sha1_digest)) {
+ die("protocol error: expected old/new/ref, got '%s'",
+ line);
+ }
+
+ if (has_old_sha1_digest &&
+ has_sha1_file_digest(old_sha1, &digest)) {
+ if (mdigest_tst(&old_sha1_digest, &digest)) {
+ die("hash collision for %s",
+ sha1_to_hex(old_sha1));
+ }
+ }
+
+
+ if (has_new_sha1_digest &&
+ has_sha1_file_digest(new_sha1, &digest)) {
+ if (mdigest_tst(&new_sha1_digest, &digest)) {
+ die("hash collision for %s",
+ sha1_to_hex(new_sha1));
+ }
+ }
- refname = line + 82;
+ refname = line + hashlen + 1;
reflen = strlen(refname);
- if (reflen + 82 < len) {
+ if (reflen + hashlen + 1 < len) {
if (strstr(refname + reflen + 1, "report-status"))
report_status = 1;
if (strstr(refname + reflen + 1, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
}
- cmd = xcalloc(1, sizeof(struct command) + len - 80);
+ /*
+ * Without the additional digests,
+ * old_hashlen + new_hashlen = 80
+ * hashlen = 81,
+ * hashlen + 1 = 82
+ * which puts the same numeric values into the last argument
+ * of xcalloc, and the second & third argument of memcpy
+ * that were used in commit
+ * fc14b89a7e6899b8ac3b5751ec2d8c98203ea4c2.
+ */
+ cmd = xcalloc(1, sizeof(struct command) + len
+ - (old_hashlen + new_hashlen));
hashcpy(cmd->old_sha1, old_sha1);
hashcpy(cmd->new_sha1, new_sha1);
- memcpy(cmd->ref_name, line + 82, len - 81);
+ memcpy(cmd->ref_name, line + hashlen + 1, len - (hashlen));
*p = cmd;
p = &cmd->next;
}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index cd1115f..1eb9704 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -250,6 +250,7 @@ int send_pack(struct send_pack_args *args,
int allow_deleting_refs = 0;
int status_report = 0;
int use_sideband = 0;
+ int mds_check = 0;
unsigned cmds_sent = 0;
int ret;
struct async demux;
@@ -263,6 +264,8 @@ int send_pack(struct send_pack_args *args,
args->use_ofs_delta = 1;
if (server_supports("side-band-64k"))
use_sideband = 1;
+ if (server_supports("mds-check"))
+ mds_check = 1;
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -298,8 +301,27 @@ int send_pack(struct send_pack_args *args,
if (args->dry_run) {
ref->status = REF_STATUS_OK;
} else {
- char *old_hex = sha1_to_hex(ref->old_sha1);
- char *new_hex = sha1_to_hex(ref->new_sha1);
+ char *old_hex, *new_hex;
+ if (mds_check) {
+ mdigest_t digest;
+ if (has_sha1_file_digest(ref->old_sha1,
+ &digest)) {
+ old_hex = sha1_to_hex_digest
+ (ref->old_sha1, &digest);
+ } else {
+ old_hex = sha1_to_hex(ref->old_sha1);
+ }
+ if (has_sha1_file_digest(ref->new_sha1,
+ &digest)) {
+ new_hex = sha1_to_hex_digest
+ (ref->new_sha1, &digest);
+ } else {
+ new_hex = sha1_to_hex(ref->new_sha1);
+ }
+ } else {
+ old_hex = sha1_to_hex(ref->old_sha1);
+ new_hex = sha1_to_hex(ref->new_sha1);
+ }
if (!cmds_sent && (status_report || use_sideband)) {
packet_buf_write(&req_buf, "%s %s %s%c%s%s",
diff --git a/http.c b/http.c
index 0ffd79c..e4e3ec7 100644
--- a/http.c
+++ b/http.c
@@ -1014,8 +1014,9 @@ int finish_http_pack_request(struct http_pack_request *preq)
struct packed_git **lst;
struct packed_git *p = preq->target;
char *tmp_idx;
+ char *tmp_mds;
struct child_process ip;
- const char *ip_argv[8];
+ const char *ip_argv[10];
close_pack_index(p);
@@ -1028,14 +1029,20 @@ int finish_http_pack_request(struct http_pack_request *preq)
*lst = (*lst)->next;
tmp_idx = xstrdup(preq->tmpfile);
+ tmp_mds = xstrdup(preq->tmpfile);
strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
".idx.temp");
+ strcpy(tmp_mds + strlen(tmp_mds) - strlen(".pack.temp"),
+ ".mds.temp");
+
ip_argv[0] = "index-pack";
ip_argv[1] = "-o";
ip_argv[2] = tmp_idx;
- ip_argv[3] = preq->tmpfile;
- ip_argv[4] = NULL;
+ ip_argv[3] = "-m";
+ ip_argv[4] = tmp_mds;
+ ip_argv[5] = preq->tmpfile;
+ ip_argv[6] = NULL;
memset(&ip, 0, sizeof(ip));
ip.argv = ip_argv;
@@ -1046,20 +1053,24 @@ int finish_http_pack_request(struct http_pack_request *preq)
if (run_command(&ip)) {
unlink(preq->tmpfile);
unlink(tmp_idx);
+ unlink(tmp_mds);
free(tmp_idx);
+ free(tmp_mds);
return -1;
}
unlink(sha1_pack_index_name(p->sha1));
if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
- || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+ || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))
+ || move_temp_to_file(tmp_mds, sha1_pack_mds_name(p->sha1))) {
free(tmp_idx);
return -1;
}
install_packed_git(p);
free(tmp_idx);
+ free(tmp_mds);
return 0;
}
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 9bf69e9..b6632d2 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -53,8 +53,8 @@ pull_to_client () {
git symbolic-ref HEAD refs/heads/`echo $heads \
| sed -e "s/^\(.\).*$/\1/"` &&
- git fsck --full &&
-
+ git fsck --full &&
+ test -z "`git count-objects -v -M | grep MD`" &&
mv .git/objects/pack/pack-* . &&
p=`ls -1 pack-*.pack` &&
git unpack-objects <$p &&
@@ -142,7 +142,8 @@ test_expect_success 'fsck in shallow repo' '
test_expect_success 'simple fetch in shallow repo' '
(
cd shallow &&
- git fetch
+ git fetch &&
+ test -z "`git count-objects -v -M | grep MD`"
)
'
@@ -245,7 +246,8 @@ test_expect_success 'clone shallow object count' '
cd shallow &&
git count-objects -v
) > count.shallow &&
- grep "^count: 52" count.shallow
+ grep "^count: 52" count.shallow &&
+ test -z "`git count-objects -v -M | grep MD`"
'
test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index e88dbd5..5e3b8c6 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -14,6 +14,12 @@ test_bundle_object_count () {
test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l)
}
+test_bundle_mds_count () {
+ git verify-pack -v -M "$1" >verify.out &&
+ test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | grep -v "<no md>" | wc -l)
+}
+
+
test_expect_success setup '
echo >file original &&
git add file &&
@@ -214,7 +220,8 @@ test_expect_success 'bundle 1 has only 3 files ' '
cat
) <bundle1 >bundle.pack &&
git index-pack bundle.pack &&
- test_bundle_object_count bundle.pack 3
+ test_bundle_object_count bundle.pack 3 &&
+ test_bundle_mds_count bundle.pack 3
'
test_expect_success 'unbundle 2' '
@@ -237,7 +244,8 @@ test_expect_success 'bundle does not prerequisite objects' '
cat
) <bundle3 >bundle.pack &&
git index-pack bundle.pack &&
- test_bundle_object_count bundle.pack 3
+ test_bundle_object_count bundle.pack 3 &&
+ test_bundle_mds_count bundle.pack 3
'
test_expect_success 'bundle should be able to create a full history' '
diff --git a/upload-pack.c b/upload-pack.c
index 6f36f62..1e77826 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -320,11 +320,18 @@ static int got_sha1(char *hex, unsigned char *sha1)
{
struct object *o;
int we_knew_they_have = 0;
+ int has_sha1_digest, has_digest;
+ mdigest_t sha1_digest, digest;
- if (get_sha1_hex(hex, sha1))
+ if (get_sha1_hex_digest(hex, sha1, &has_sha1_digest, &sha1_digest))
die("git upload-pack: expected SHA1 object, got '%s'", hex);
if (!has_sha1_file(sha1))
return -1;
+ has_digest = has_sha1_file_digest(sha1, &digest);
+ if (has_sha1_digest && has_digest
+ && mdigest_tst(&digest, &sha1_digest)) {
+ die("git upload-pack: SHA1 collision on MD for %s", hex);
+ }
o = lookup_object(sha1);
if (!(o && o->parsed))
@@ -719,7 +726,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow no-progress"
- " include-tag multi_ack_detailed";
+ " include-tag multi_ack_detailed" " mds-check";
struct object *o = parse_object(sha1);
const char *refname_nons = strip_namespace(refname);
--
1.7.1
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2011-12-21 7:12 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-21 7:12 [PATCH 5/6] Add MD support for fetch, pull, and push Bill Zaumen
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 NNTP newsgroup(s).