From patchwork Mon Jan 11 03:58:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12009703 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C7320C433DB for ; Mon, 11 Jan 2021 04:00:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9183222473 for ; Mon, 11 Jan 2021 04:00:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727182AbhAKEAB (ORCPT ); Sun, 10 Jan 2021 23:00:01 -0500 Received: from injection.crustytoothpaste.net ([192.241.140.119]:50714 "EHLO injection.crustytoothpaste.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726643AbhAKEAA (ORCPT ); Sun, 10 Jan 2021 23:00:00 -0500 Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b978:101:b610:a2f0:36c1:12e3]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by injection.crustytoothpaste.net (Postfix) with ESMTPSA id E6D7760782; Mon, 11 Jan 2021 03:58:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1610337529; bh=6opPvStxnxPiyiq1bx8M/+wM3O+5NsfeVMGEhuFA8fc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=dFHZpWWYteVMjn+rEdo+8ChEdqRMBkTFrACCrsOXhXdkTT/D2pf2PtUFM/7i/HELI Qa2AYeMktnpnQJPF0MlZZmhz4ZGMBC06+cTLtYcTcjny+4TmOgCGyBpVPZySxw0Ggw AUcbh1LlYz+teYJNf12ts/87EMv9Vx3o+A4FW9c3rO9hl/az3aDU+y2mhYikp4W60V U1SWM/NG14thlEDcNQhnXB6GfZ4IcUhIR9CSqe//TGHwBx0lUvfozT+BWxHPHQ84pq XUdu6RfxZF7WAq64jJgiuvluLS/9TvuHyWRjpCYmQCIDws3+38/wUSGoHxHXwOVNFq 8t/yMEDpj+TouaorgPUryJ0b+ZZ3cbqKI0vHzgbgnBgySCG4CO9o4id4HKF0GY3SaL JTAS8Mh+FjNOh6aNbiBD0EbC+dzABhSsOdL8tzoA1/1ygZACEg79pJT7yu0P0um8Zi n6aKCzsr0pIvZfZ5asctjesydio6r53xLR+Rkq/vmy5wxqE8enk From: "brian m. carlson" To: Cc: Eric Sunshine , Denton Liu , Jeff King Subject: [PATCH v2 1/5] commit: ignore additional signatures when parsing signed commits Date: Mon, 11 Jan 2021 03:58:36 +0000 Message-Id: <20210111035840.2437737-2-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7 In-Reply-To: <20210111035840.2437737-1-sandals@crustytoothpaste.net> References: <20210111003740.1319996-1-sandals@crustytoothpaste.net> <20210111035840.2437737-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org When we create a commit with multiple signatures, neither of these signatures includes the other. Consequently, when we produce the payload which has been signed so we can verify the commit, we must strip off any other signatures, or the payload will differ from what was signed. Do so, and in preparation for verifying with multiple algorithms, pass the algorithm we want to verify into parse_signed_commit. Signed-off-by: brian m. carlson --- commit.c | 52 ++++++++++++++++++++++++---------------- commit.h | 3 ++- log-tree.c | 2 +- t/t7510-signed-commit.sh | 43 ++++++++++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 23 deletions(-) diff --git a/commit.c b/commit.c index f128f18a9b..93faaad764 100644 --- a/commit.c +++ b/commit.c @@ -1036,20 +1036,18 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid) } int parse_signed_commit(const struct commit *commit, - struct strbuf *payload, struct strbuf *signature) + struct strbuf *payload, struct strbuf *signature, + const struct git_hash_algo *algop) { unsigned long size; const char *buffer = get_commit_buffer(commit, &size); - int in_signature, saw_signature = -1; - const char *line, *tail; - const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)]; - int gpg_sig_header_len = strlen(gpg_sig_header); + int in_signature = 0, saw_signature = 0, other_signature = 0; + const char *line, *tail, *p; + const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algop)]; line = buffer; tail = buffer + size; - in_signature = 0; - saw_signature = 0; while (line < tail) { const char *sig = NULL; const char *next = memchr(line, '\n', tail - line); @@ -1057,9 +1055,15 @@ int parse_signed_commit(const struct commit *commit, next = next ? next + 1 : tail; if (in_signature && line[0] == ' ') sig = line + 1; - else if (starts_with(line, gpg_sig_header) && - line[gpg_sig_header_len] == ' ') - sig = line + gpg_sig_header_len + 1; + else if (skip_prefix(line, gpg_sig_header, &p) && + *p == ' ') { + sig = line + strlen(gpg_sig_header) + 1; + other_signature = 0; + } + else if (starts_with(line, "gpgsig")) + other_signature = 1; + else if (other_signature && line[0] != ' ') + other_signature = 0; if (sig) { strbuf_add(signature, sig, next - sig); saw_signature = 1; @@ -1068,7 +1072,8 @@ int parse_signed_commit(const struct commit *commit, if (*line == '\n') /* dump the whole remainder of the buffer */ next = tail; - strbuf_add(payload, line, next - line); + if (!other_signature) + strbuf_add(payload, line, next - line); in_signature = 0; } line = next; @@ -1082,23 +1087,27 @@ int remove_signature(struct strbuf *buf) const char *line = buf->buf; const char *tail = buf->buf + buf->len; int in_signature = 0; - const char *sig_start = NULL; - const char *sig_end = NULL; + struct sigbuf { + const char *start; + const char *end; + } sigs[2] = { 0 }, *sigp = &sigs[0]; + int i; + const char *orig_buf = buf->buf; while (line < tail) { const char *next = memchr(line, '\n', tail - line); next = next ? next + 1 : tail; if (in_signature && line[0] == ' ') - sig_end = next; + sigp->end = next; else if (starts_with(line, "gpgsig")) { int i; for (i = 1; i < GIT_HASH_NALGOS; i++) { const char *p; if (skip_prefix(line, gpg_sig_headers[i], &p) && *p == ' ') { - sig_start = line; - sig_end = next; + sigp->start = line; + sigp->end = next; in_signature = 1; } } @@ -1106,15 +1115,18 @@ int remove_signature(struct strbuf *buf) if (*line == '\n') /* dump the whole remainder of the buffer */ next = tail; + if (in_signature && sigp - sigs != ARRAY_SIZE(sigs)) + sigp++; in_signature = 0; } line = next; } - if (sig_start) - strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start); + for (i = ARRAY_SIZE(sigs) - 1; i >= 0; i--) + if (sigs[i].start) + strbuf_remove(buf, sigs[i].start - orig_buf, sigs[i].end - sigs[i].start); - return sig_start != NULL; + return sigs[0].start != NULL; } static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail) @@ -1165,7 +1177,7 @@ int check_commit_signature(const struct commit *commit, struct signature_check * sigc->result = 'N'; - if (parse_signed_commit(commit, &payload, &signature) <= 0) + if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0) goto out; ret = check_signature(payload.buf, payload.len, signature.buf, signature.len, sigc); diff --git a/commit.h b/commit.h index f4e7b0158e..030aa65ab8 100644 --- a/commit.h +++ b/commit.h @@ -317,7 +317,8 @@ void set_merge_remote_desc(struct commit *commit, struct commit *get_merge_parent(const char *name); int parse_signed_commit(const struct commit *commit, - struct strbuf *message, struct strbuf *signature); + struct strbuf *message, struct strbuf *signature, + const struct git_hash_algo *algop); int remove_signature(struct strbuf *buf); /* diff --git a/log-tree.c b/log-tree.c index fd0dde97ec..7e0335e548 100644 --- a/log-tree.c +++ b/log-tree.c @@ -502,7 +502,7 @@ static void show_signature(struct rev_info *opt, struct commit *commit) struct signature_check sigc = { 0 }; int status; - if (parse_signed_commit(commit, &payload, &signature) <= 0) + if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0) goto out; status = check_signature(payload.buf, payload.len, signature.buf, diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 6baaa1ad91..d78319d5c8 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -172,7 +172,7 @@ test_expect_success GPG 'show signed commit with signature' ' git cat-file commit initial >cat && grep -v -e "gpg: " -e "Warning: " show >show.commit && grep -e "gpg: " -e "Warning: " show >show.gpg && - grep -v "^ " cat | grep -v "^$(test_oid header) " >cat.commit && + grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit && test_cmp show.commit commit && test_cmp show.gpg verify.2 && test_cmp cat.commit verify.1 @@ -334,4 +334,45 @@ test_expect_success GPG 'show double signature with custom format' ' test_cmp expect actual ' + +test_expect_success GPG 'verify-commit verifies multiply signed commits' ' + git init multiply-signed && + cd multiply-signed && + test_commit first && + echo 1 >second && + git add second && + tree=$(git write-tree) && + parent=$(git rev-parse HEAD^{commit}) && + git commit --gpg-sign -m second && + git cat-file commit HEAD && + # Avoid trailing whitespace. + sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF && + Qtree $tree + Qparent $parent + Qauthor A U Thor 1112912653 -0700 + Qcommitter C O Mitter 1112912653 -0700 + Qgpgsig -----BEGIN PGP SIGNATURE----- + QZ + Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy + Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC + Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ== + Q =tQ0N + Q -----END PGP SIGNATURE----- + Qgpgsig-sha256 -----BEGIN PGP SIGNATURE----- + QZ + Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy + Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO + Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw== + Q =pIwP + Q -----END PGP SIGNATURE----- + Q + Qsecond + EOF + head=$(git hash-object -t commit -w commit) && + git reset --hard $head && + git verify-commit $head 2>actual && + grep "Good signature from" actual && + ! grep "BAD signature from" actual +' + test_done From patchwork Mon Jan 11 03:58:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12009699 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A5C26C43381 for ; Mon, 11 Jan 2021 03:59:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7BBF722472 for ; Mon, 11 Jan 2021 03:59:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726652AbhAKD7c (ORCPT ); Sun, 10 Jan 2021 22:59:32 -0500 Received: from injection.crustytoothpaste.net ([192.241.140.119]:50686 "EHLO injection.crustytoothpaste.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726049AbhAKD7c (ORCPT ); Sun, 10 Jan 2021 22:59:32 -0500 Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b978:101:b610:a2f0:36c1:12e3]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by injection.crustytoothpaste.net (Postfix) with ESMTPSA id 802D760783; Mon, 11 Jan 2021 03:58:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1610337529; bh=HQB/HDkvUDKbpEngZ0WVow5iKpUkmPz/u+OYSskzJtM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=Li3il69H8B4X1WLZke8rmuhUjTnd8KubgVJWTKV9GlXTAEE2EsHBXhTSthKDjkJm0 /OYai9O1QUphVwldxi/dfnJKFByIAW8NuWawLc3koe6zX2IORUT9o6iYND4D2uslxK 3HaRsF4DA4+xTOv2FEjVvD8XOQnEEt4mYpDB/FbUQsJTlv7m8U08GdTo9PPvne4g9d fFL1y6erwSkXR2ys9tu3yAHaoKwqJbtyrmubaQ53+Um/xlHW//C10moa3ug1Gg1C6n yfP4PYucbyv1KBt3lx4Eco6S7FqSi9u+ADRIwoNIEGjgY+jCcqsqW24TbCyhrMfMtR SYEyGc/8JHSFIa5tMLALlzs5q1DIOvmCE1Kx3ntIPNujgSdq6HDGJLm7Dj9UDkJ3MK RHKFwMUBaKzQKJ7oK2gZWC8MDmWl1FAu004AwxLqUTAtrgCNc4BxAPb+vdg8tFHyrg lVmNM4el6tXjyRnyGjE36w0Dix1msNiKt3el1Ni5Y3/13g50uaj From: "brian m. carlson" To: Cc: Eric Sunshine , Denton Liu , Jeff King Subject: [PATCH v2 2/5] gpg-interface: improve interface for parsing tags Date: Mon, 11 Jan 2021 03:58:37 +0000 Message-Id: <20210111035840.2437737-3-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7 In-Reply-To: <20210111035840.2437737-1-sandals@crustytoothpaste.net> References: <20210111003740.1319996-1-sandals@crustytoothpaste.net> <20210111035840.2437737-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org We have a function which parses a buffer with a signature at the end, parse_signature, and this function is used for signed tags. However, we'll need to store values for multiple algorithms, and we'll do this by using a header for the non-default algorithm. Adjust the parse_signature interface to store the parsed data in two strbufs and turn the existing function into parse_signed_buffer. The latter is still used in places where we know we always have a signed buffer, such as push certs. Adjust all the callers to deal with this new interface. Signed-off-by: brian m. carlson --- builtin/receive-pack.c | 4 ++-- builtin/tag.c | 16 ++++++++++++---- commit.c | 9 ++++++--- fmt-merge-msg.c | 29 ++++++++++++++++++----------- gpg-interface.c | 13 ++++++++++++- gpg-interface.h | 9 ++++++++- log-tree.c | 13 +++++++------ ref-filter.c | 18 ++++++++++++++---- tag.c | 15 ++++++++------- 9 files changed, 87 insertions(+), 39 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index d49d050e6e..b89ce31bf2 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -764,7 +764,7 @@ static void prepare_push_cert_sha1(struct child_process *proc) memset(&sigcheck, '\0', sizeof(sigcheck)); - bogs = parse_signature(push_cert.buf, push_cert.len); + bogs = parse_signed_buffer(push_cert.buf, push_cert.len); check_signature(push_cert.buf, bogs, push_cert.buf + bogs, push_cert.len - bogs, &sigcheck); @@ -2050,7 +2050,7 @@ static void queue_commands_from_cert(struct command **tail, die("malformed push certificate %.*s", 100, push_cert->buf); else boc += 2; - eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len); + eoc = push_cert->buf + parse_signed_buffer(push_cert->buf, push_cert->len); while (boc < eoc) { const char *eol = memchr(boc, '\n', eoc - boc); diff --git a/builtin/tag.c b/builtin/tag.c index ecf011776d..7162f4ccc5 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -174,11 +174,17 @@ static void write_tag_body(int fd, const struct object_id *oid) { unsigned long size; enum object_type type; - char *buf, *sp; + char *buf, *sp, *orig; + struct strbuf payload = STRBUF_INIT; + struct strbuf signature = STRBUF_INIT; - buf = read_object_file(oid, &type, &size); + orig = buf = read_object_file(oid, &type, &size); if (!buf) return; + if (parse_signature(buf, size, &payload, &signature)) { + buf = payload.buf; + size = payload.len; + } /* skip header */ sp = strstr(buf, "\n\n"); @@ -187,9 +193,11 @@ static void write_tag_body(int fd, const struct object_id *oid) return; } sp += 2; /* skip the 2 LFs */ - write_or_die(fd, sp, parse_signature(sp, buf + size - sp)); + write_or_die(fd, sp, buf + size - sp); - free(buf); + free(orig); + strbuf_release(&payload); + strbuf_release(&signature); } static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result) diff --git a/commit.c b/commit.c index 93faaad764..794dc8b593 100644 --- a/commit.c +++ b/commit.c @@ -1134,8 +1134,10 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header struct merge_remote_desc *desc; struct commit_extra_header *mergetag; char *buf; - unsigned long size, len; + unsigned long size; enum object_type type; + struct strbuf payload = STRBUF_INIT; + struct strbuf signature = STRBUF_INIT; desc = merge_remote_util(parent); if (!desc || !desc->obj) @@ -1143,8 +1145,7 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header buf = read_object_file(&desc->obj->oid, &type, &size); if (!buf || type != OBJ_TAG) goto free_return; - len = parse_signature(buf, size); - if (size == len) + if (!parse_signature(buf, size, &payload, &signature)) goto free_return; /* * We could verify this signature and either omit the tag when @@ -1163,6 +1164,8 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header **tail = mergetag; *tail = &mergetag->next; + strbuf_release(&payload); + strbuf_release(&signature); return; free_return: diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c index 9a664a4a58..7fd99f0ac1 100644 --- a/fmt-merge-msg.c +++ b/fmt-merge-msg.c @@ -509,22 +509,28 @@ static void fmt_merge_msg_sigs(struct strbuf *out) for (i = 0; i < origins.nr; i++) { struct object_id *oid = origins.items[i].util; enum object_type type; - unsigned long size, len; + unsigned long size; char *buf = read_object_file(oid, &type, &size); + char *origbuf = buf; + unsigned long len = size; struct signature_check sigc = { NULL }; - struct strbuf sig = STRBUF_INIT; + struct strbuf payload = STRBUF_INIT, sig = STRBUF_INIT; if (!buf || type != OBJ_TAG) goto next; - len = parse_signature(buf, size); - if (size == len) - ; /* merely annotated */ - else if (check_signature(buf, len, buf + len, size - len, &sigc) && - !sigc.gpg_output) - strbuf_addstr(&sig, "gpg verification failed.\n"); - else - strbuf_addstr(&sig, sigc.gpg_output); + if (!parse_signature(buf, size, &payload, &sig)) + ;/* merely annotated */ + else { + buf = payload.buf; + len = payload.len; + if (check_signature(payload.buf, payload.len, sig.buf, + sig.len, &sigc) && + !sigc.gpg_output) + strbuf_addstr(&sig, "gpg verification failed.\n"); + else + strbuf_addstr(&sig, sigc.gpg_output); + } signature_check_clear(&sigc); if (!tag_number++) { @@ -547,9 +553,10 @@ static void fmt_merge_msg_sigs(struct strbuf *out) strlen(origins.items[i].string)); fmt_tag_signature(&tagbuf, &sig, buf, len); } + strbuf_release(&payload); strbuf_release(&sig); next: - free(buf); + free(origbuf); } if (tagbuf.len) { strbuf_addch(out, '\n'); diff --git a/gpg-interface.c b/gpg-interface.c index b499270836..c6274c14af 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -345,7 +345,7 @@ void print_signature_buffer(const struct signature_check *sigc, unsigned flags) fputs(output, stderr); } -size_t parse_signature(const char *buf, size_t size) +size_t parse_signed_buffer(const char *buf, size_t size) { size_t len = 0; size_t match = size; @@ -361,6 +361,17 @@ size_t parse_signature(const char *buf, size_t size) return match; } +int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct strbuf *signature) +{ + size_t match = parse_signed_buffer(buf, size); + if (match != size) { + strbuf_add(payload, buf, match); + strbuf_add(signature, buf + match, size - match); + return 1; + } + return 0; +} + void set_signing_key(const char *key) { free(configured_signing_key); diff --git a/gpg-interface.h b/gpg-interface.h index f4e9b4f371..80567e4894 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -37,13 +37,20 @@ struct signature_check { void signature_check_clear(struct signature_check *sigc); +/* + * Look at a GPG signed tag object. If such a signature exists, store it in + * signature and the signed content in payload. Return 1 if a signature was + * found, and 0 otherwise. + */ +int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct strbuf *signature); + /* * Look at GPG signed content (e.g. a signed tag object), whose * payload is followed by a detached signature on it. Return the * offset where the embedded detached signature begins, or the end of * the data when there is no such signature. */ -size_t parse_signature(const char *buf, size_t size); +size_t parse_signed_buffer(const char *buf, size_t size); /* * Create a detached signature for the contents of "buffer" and append diff --git a/log-tree.c b/log-tree.c index 7e0335e548..b025c8da93 100644 --- a/log-tree.c +++ b/log-tree.c @@ -548,7 +548,8 @@ static int show_one_mergetag(struct commit *commit, struct strbuf verify_message; struct signature_check sigc = { 0 }; int status, nth; - size_t payload_size; + struct strbuf payload = STRBUF_INIT; + struct strbuf signature = STRBUF_INIT; hash_object_file(the_hash_algo, extra->value, extra->len, type_name(OBJ_TAG), &oid); @@ -571,13 +572,11 @@ static int show_one_mergetag(struct commit *commit, strbuf_addf(&verify_message, "parent #%d, tagged '%s'\n", nth + 1, tag->tag); - payload_size = parse_signature(extra->value, extra->len); status = -1; - if (extra->len > payload_size) { + if (parse_signature(extra->value, extra->len, &payload, &signature)) { /* could have a good signature */ - status = check_signature(extra->value, payload_size, - extra->value + payload_size, - extra->len - payload_size, &sigc); + status = check_signature(payload.buf, payload.len, + signature.buf, signature.len, &sigc); if (sigc.gpg_output) strbuf_addstr(&verify_message, sigc.gpg_output); else @@ -588,6 +587,8 @@ static int show_one_mergetag(struct commit *commit, show_sig_lines(opt, status, verify_message.buf); strbuf_release(&verify_message); + strbuf_release(&payload); + strbuf_release(&signature); return 0; } diff --git a/ref-filter.c b/ref-filter.c index aa260bfd09..8d8baec1b5 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1215,7 +1215,13 @@ static void find_subpos(const char *buf, unsigned long *nonsiglen, const char **sig, unsigned long *siglen) { + struct strbuf payload = STRBUF_INIT; + struct strbuf signature = STRBUF_INIT; const char *eol; + const char *end = buf + strlen(buf); + const char *sigstart; + + /* skip past header until we hit empty line */ while (*buf && *buf != '\n') { eol = strchrnul(buf, '\n'); @@ -1228,14 +1234,15 @@ static void find_subpos(const char *buf, buf++; /* parse signature first; we might not even have a subject line */ - *sig = buf + parse_signature(buf, strlen(buf)); - *siglen = strlen(*sig); + parse_signature(buf, end - buf, &payload, &signature); + *sig = strbuf_detach(&signature, siglen); + sigstart = buf + parse_signed_buffer(buf, strlen(buf)); /* subject is first non-empty line */ *sub = buf; /* subject goes to first empty line before signature begins */ if ((eol = strstr(*sub, "\n\n"))) { - eol = eol < *sig ? eol : *sig; + eol = eol < sigstart ? eol : sigstart; /* check if message uses CRLF */ } else if (! (eol = strstr(*sub, "\r\n\r\n"))) { /* treat whole message as subject */ @@ -1253,7 +1260,7 @@ static void find_subpos(const char *buf, buf++; *body = buf; *bodylen = strlen(buf); - *nonsiglen = *sig - buf; + *nonsiglen = sigstart - buf; } /* @@ -1291,6 +1298,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf) struct used_atom *atom = &used_atom[i]; const char *name = atom->name; struct atom_value *v = &val[i]; + if (!!deref != (*name == '*')) continue; if (deref) @@ -1336,6 +1344,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf) v->s = strbuf_detach(&s, NULL); } else if (atom->u.contents.option == C_BARE) v->s = xstrdup(subpos); + + free((void *)sigpos); } } diff --git a/tag.c b/tag.c index 1ed2684e45..3e18a41841 100644 --- a/tag.c +++ b/tag.c @@ -13,26 +13,27 @@ const char *tag_type = "tag"; static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags) { struct signature_check sigc; - size_t payload_size; + struct strbuf payload = STRBUF_INIT; + struct strbuf signature = STRBUF_INIT; int ret; memset(&sigc, 0, sizeof(sigc)); - payload_size = parse_signature(buf, size); - - if (size == payload_size) { + if (!parse_signature(buf, size, &payload, &signature)) { if (flags & GPG_VERIFY_VERBOSE) - write_in_full(1, buf, payload_size); + write_in_full(1, buf, size); return error("no signature found"); } - ret = check_signature(buf, payload_size, buf + payload_size, - size - payload_size, &sigc); + ret = check_signature(payload.buf, payload.len, signature.buf, + signature.len, &sigc); if (!(flags & GPG_VERIFY_OMIT_STATUS)) print_signature_buffer(&sigc, flags); signature_check_clear(&sigc); + strbuf_release(&payload); + strbuf_release(&signature); return ret; } From patchwork Mon Jan 11 03:58:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12009695 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 74DD2C433DB for ; Mon, 11 Jan 2021 03:59:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 411FF22473 for ; Mon, 11 Jan 2021 03:59:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726564AbhAKD7c (ORCPT ); Sun, 10 Jan 2021 22:59:32 -0500 Received: from injection.crustytoothpaste.net ([192.241.140.119]:50696 "EHLO injection.crustytoothpaste.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726151AbhAKD7b (ORCPT ); Sun, 10 Jan 2021 22:59:31 -0500 Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b978:101:b610:a2f0:36c1:12e3]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by injection.crustytoothpaste.net (Postfix) with ESMTPSA id 1327660784; Mon, 11 Jan 2021 03:58:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1610337530; bh=m2rhZX5/XNR8DiaN1eUsemknZxB+BfmCBZ7ZQuQh6qQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=c8vGL+LDiE1HMEYMUjJO6sbSLfQDYlYP3H6PMGFA/X1EzCys9DHX/WCnrJlW2U5p7 k2PWXL7aq+UuRPx2Njs5oB2ClmsIMMO8A06ZztX5mcqhGLgI6FmTuCOEqeMpVFeN2d gWYWiNV4lnO1seekk4SZjCym5XpdCK6CFcsRITRWenU4PVaqNoNNHA8e0Ast4LYc2/ jyJgDZ8ADduWGccvf9N7P21hW7WmLqlJEc3Rs3oBffOpqgidRHDQ7QD2jUjpEzp6Pm u9rjGLpsngNmfD0A5//UIjWtrRi62bPJhxnv749fw+0cv35l4bs88qrlEy0j1hJJT/ ZGSHKqzYR5YSMaIYNzVTZ38xZ0na+Zy8tFsX4nHwQvGWdpo1veOrOqVUdyT4oyKjlZ 1axFCgZzSlDcqD6woBS6B3oJzs/D2Od2bOn1bM4AW9FYMudfWr14Y3+JTYSAaTko5+ nuUsyx8GCE+xQ+JuDa5fFS11NyqUarosCT1SWSljgiaCGzaEQhN From: "brian m. carlson" To: Cc: Eric Sunshine , Denton Liu , Jeff King Subject: [PATCH v2 3/5] commit: allow parsing arbitrary buffers with headers Date: Mon, 11 Jan 2021 03:58:38 +0000 Message-Id: <20210111035840.2437737-4-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7 In-Reply-To: <20210111035840.2437737-1-sandals@crustytoothpaste.net> References: <20210111003740.1319996-1-sandals@crustytoothpaste.net> <20210111035840.2437737-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Currently only commits are signed with headers. However, in the future, we'll also sign tags with headers as well. Let's refactor out a function called parse_buffer_signed_by_header which does exactly that. In addition, since we'll want to sign things other than commits this way, let's call the function sign_with_header instead of do_sign_commit. Signed-off-by: brian m. carlson --- commit.c | 20 ++++++++++++++++---- commit.h | 9 +++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/commit.c b/commit.c index 794dc8b593..23020c0bca 100644 --- a/commit.c +++ b/commit.c @@ -995,7 +995,7 @@ static const char *gpg_sig_headers[] = { "gpgsig-sha256", }; -static int do_sign_commit(struct strbuf *buf, const char *keyid) +int sign_with_header(struct strbuf *buf, const char *keyid) { struct strbuf sig = STRBUF_INIT; int inspos, copypos; @@ -1035,13 +1035,26 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid) return 0; } + + int parse_signed_commit(const struct commit *commit, struct strbuf *payload, struct strbuf *signature, const struct git_hash_algo *algop) { - unsigned long size; const char *buffer = get_commit_buffer(commit, &size); + int ret = parse_buffer_signed_by_header(buffer, size, payload, signature, algop); + + unuse_commit_buffer(commit, buffer); + return ret; +} + +int parse_buffer_signed_by_header(const char *buffer, + unsigned long size, + struct strbuf *payload, + struct strbuf *signature, + const struct git_hash_algo *algop) +{ int in_signature = 0, saw_signature = 0, other_signature = 0; const char *line, *tail, *p; const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algop)]; @@ -1078,7 +1091,6 @@ int parse_signed_commit(const struct commit *commit, } line = next; } - unuse_commit_buffer(commit, buffer); return saw_signature; } @@ -1530,7 +1542,7 @@ int commit_tree_extended(const char *msg, size_t msg_len, if (encoding_is_utf8 && !verify_utf8(&buffer)) fprintf(stderr, _(commit_utf8_warn)); - if (sign_commit && do_sign_commit(&buffer, sign_commit)) { + if (sign_commit && sign_with_header(&buffer, sign_commit)) { result = -1; goto out; } diff --git a/commit.h b/commit.h index 030aa65ab8..e2856ce8ef 100644 --- a/commit.h +++ b/commit.h @@ -360,4 +360,13 @@ int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void LAST_ARG_MUST_BE_NULL int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...); +/* Sign a commit or tag buffer, storing the result in a header. */ +int sign_with_header(struct strbuf *buf, const char *keyid); +/* Parse the signature out of a header. */ +int parse_buffer_signed_by_header(const char *buffer, + unsigned long size, + struct strbuf *payload, + struct strbuf *signature, + const struct git_hash_algo *algop); + #endif /* COMMIT_H */ From patchwork Mon Jan 11 03:58:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12009697 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8C9EAC433E6 for ; Mon, 11 Jan 2021 03:59:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5A80F22475 for ; Mon, 11 Jan 2021 03:59:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727010AbhAKD7d (ORCPT ); Sun, 10 Jan 2021 22:59:33 -0500 Received: from injection.crustytoothpaste.net ([192.241.140.119]:50706 "EHLO injection.crustytoothpaste.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725991AbhAKD7c (ORCPT ); Sun, 10 Jan 2021 22:59:32 -0500 Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b978:101:b610:a2f0:36c1:12e3]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by injection.crustytoothpaste.net (Postfix) with ESMTPSA id 9B62E6080E; Mon, 11 Jan 2021 03:58:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1610337531; bh=Fv4Gt8h72wYuxAxF7cQhHRiMjfsMQ1PGi3Q32phIhl4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=XkEje9Dm0pnSfR0A12LnmAqc0eKD56+sH5QKET6TNFVpPdEY0j+aO2Kzv7tkLVeao t9lLB5J6HpTJs+ZJZYXVuQfROal+d9RG+tOmyxpH6VKLJZ+MWnRqBNUJOnUipHEjRH m4vnhNV42jOUTEyJXjkfg8gZxJcnimjEPuj8Xd27MpbZMk06iyTfpUIxW8cs0Qc58O lXu8+b7oxLcdbWyf3o4InfPr7f2tTMQ6EzGIhN6gmofEjwyzpsdV4iv0QZHle/yhYV vSdBoQ5VrKeZa0LjvmXkCyxGxKVdZI1d/R377FTSQCyqGQt/mw19CYBmS9CSByShUq H0wSMZOF3KQ2mgSMVnPSh4Iy/y73+yDAtcb9k0LSlGgLsNDd4QCA32f9zzFGySsDLn iZ+RtLOn4ViEtPjfVAteoSh95uC4oN1JHjw52MU7hUQC4oJ0shbtqvCB/PlUvFuEh1 AB+Qh7rt4nanKz+AGNceYBgnkWuzdg+RWY0r2AGnn6Reqsy7wLI From: "brian m. carlson" To: Cc: Eric Sunshine , Denton Liu , Jeff King Subject: [PATCH v2 4/5] ref-filter: hoist signature parsing Date: Mon, 11 Jan 2021 03:58:39 +0000 Message-Id: <20210111035840.2437737-5-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7 In-Reply-To: <20210111035840.2437737-1-sandals@crustytoothpaste.net> References: <20210111003740.1319996-1-sandals@crustytoothpaste.net> <20210111035840.2437737-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org When we parse a signature in the ref-filter code, we continually increment the buffer pointer. Hoist the signature parsing above the blank line delimiting headers and body so we can find the signature when using a header to sign the buffer. Signed-off-by: brian m. carlson --- ref-filter.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ref-filter.c b/ref-filter.c index 8d8baec1b5..32ed4d5111 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1221,6 +1221,8 @@ static void find_subpos(const char *buf, const char *end = buf + strlen(buf); const char *sigstart; + /* parse signature first; we might not even have a subject line */ + parse_signature(buf, end - buf, &payload, &signature); /* skip past header until we hit empty line */ while (*buf && *buf != '\n') { @@ -1232,9 +1234,6 @@ static void find_subpos(const char *buf, /* skip any empty lines */ while (*buf == '\n') buf++; - - /* parse signature first; we might not even have a subject line */ - parse_signature(buf, end - buf, &payload, &signature); *sig = strbuf_detach(&signature, siglen); sigstart = buf + parse_signed_buffer(buf, strlen(buf)); @@ -1330,7 +1329,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf) v->s = xmemdupz(sigpos, siglen); else if (atom->u.contents.option == C_LINES) { struct strbuf s = STRBUF_INIT; - const char *contents_end = bodylen + bodypos - siglen; + const char *contents_end = bodypos + nonsiglen; /* Size is the length of the message after removing the signature */ append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines); From patchwork Mon Jan 11 03:58:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12009701 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D3473C433E0 for ; Mon, 11 Jan 2021 04:00:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id ACAC72247F for ; Mon, 11 Jan 2021 04:00:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727191AbhAKEAM (ORCPT ); Sun, 10 Jan 2021 23:00:12 -0500 Received: from injection.crustytoothpaste.net ([192.241.140.119]:50720 "EHLO injection.crustytoothpaste.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726573AbhAKEAM (ORCPT ); Sun, 10 Jan 2021 23:00:12 -0500 Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b978:101:b610:a2f0:36c1:12e3]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by injection.crustytoothpaste.net (Postfix) with ESMTPSA id 302846081F; Mon, 11 Jan 2021 03:58:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1610337531; bh=iOk2zNQtGOchx4/xFaRbXMeuwZfTKEgGvqnvKg8PJc0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=spcl1S+JUYJI4Ew3avp9eqhkA3vGkI4Lq+A0NaL9cStsw0WrTvsU8i8ejAtbfAy1r bR2W7mNki1pK6yHWdWQTCM4So769uAv7SJH6zViJVW8lkGcHZQM1/P7OWDMAZblfZf 1EKXOtUuvs98vLJsPRNJVLcb4uiBimnsTNj4CjPEGPqqcBVJE8UYvB0xj5T0jkJ2L7 S+L/MB17c5aGJrREE5XL6CsAFxknm8HUaPRHGprhNnAXrM+i7RpGftaKgCZE09bid/ IWHxE+DrsXNgdnLnzDNhwQp+pdgOD2O71ZK2Bfnnp8ACIJvaeczbfqBzlda7CkEsFO 4LWTpZQHDV8tMZWS1w83v85NSm+QAoS6SghBOlVveETUEPFEoQbyeWJ3Q0MzzRq/ME tZwksHggOkeg+PL29oLiIMRLStkFFSchhCMUW14dYorPmyH8p0r6lb7I2Z3wNcaiOP Zz4/Lkno0iA+8fJP3lG7dlBOPx2zM4SlerkKzM8CKLWve8j0tzN From: "brian m. carlson" To: Cc: Eric Sunshine , Denton Liu , Jeff King Subject: [PATCH v2 5/5] gpg-interface: remove other signature headers before verifying Date: Mon, 11 Jan 2021 03:58:40 +0000 Message-Id: <20210111035840.2437737-6-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7 In-Reply-To: <20210111035840.2437737-1-sandals@crustytoothpaste.net> References: <20210111003740.1319996-1-sandals@crustytoothpaste.net> <20210111035840.2437737-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org When we have a multiply signed commit, we need to remove the signature in the header before verifying the object, since the trailing signature will not be over both pieces of data. Do so, and verify that we validate the signature appropriately. Signed-off-by: brian m. carlson --- gpg-interface.c | 2 ++ t/t7004-tag.sh | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/gpg-interface.c b/gpg-interface.c index c6274c14af..127aecfc2b 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "commit.h" #include "config.h" #include "run-command.h" #include "strbuf.h" @@ -366,6 +367,7 @@ int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct size_t match = parse_signed_buffer(buf, size); if (match != size) { strbuf_add(payload, buf, match); + remove_signature(payload); strbuf_add(signature, buf + match, size - match); return 1; } diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 05f411c821..6fb4e3cf11 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -17,6 +17,13 @@ tag_exists () { git show-ref --quiet --verify refs/tags/"$1" } +test_expect_success 'setup' ' + test_oid_cache <<-EOM + othersigheader sha1:gpgsig-sha256 + othersigheader sha256:gpgsig + EOM +' + test_expect_success 'listing all tags in an empty tree should succeed' ' git tag -l && git tag @@ -1371,6 +1378,24 @@ test_expect_success GPG \ 'test_config gpg.program echo && test_must_fail git tag -s -m tail tag-gpg-failure' +# try to produce invalid signature +test_expect_success GPG 'git verifies tag is valid with double signature' ' + git tag -s -m tail tag-gpg-double-sig && + git cat-file tag tag-gpg-double-sig >tag && + othersigheader=$(test_oid othersigheader) && + sed -ne "/^\$/q;p" tag >new-tag && + cat <<-EOM >>new-tag && + $othersigheader -----BEGIN PGP SIGNATURE----- + someinvaliddata + -----END PGP SIGNATURE----- + EOM + sed -e "1,/^tagger/d" tag >>new-tag && + new_tag=$(git hash-object -t tag -w new-tag) && + git update-ref refs/tags/tag-gpg-double-sig $new_tag && + git verify-tag tag-gpg-double-sig && + git fsck +' + # try to sign with bad user.signingkey test_expect_success GPGSM \ 'git tag -s fails if gpgsm is misconfigured (bad key)' \