gpg-interface: prefer check_signature() for GPG verification

This commit refactors the use of verify_signed_buffer() outside of
gpg-interface.c to use check_signature() instead.  It also turns
verify_signed_buffer() into a file-local function since it's now only
invoked internally by check_signature().

There were previously two globally scoped functions used in different
parts of Git to perform GPG signature verification:
verify_signed_buffer() and check_signature().  Now only
check_signature() is used.

The verify_signed_buffer() function doesn't guard against duplicate
signatures as described by Michał Górny [1].  Instead it only ensures a
non-erroneous exit code from GPG and the presence of at least one
GOODSIG status field.  This stands in contrast with check_signature()
that returns an error if more than one signature is encountered.

The lower degree of verification makes the use of verify_signed_buffer()
problematic if callers don't parse and validate the various parts of the
GPG status message themselves.  And processing these messages seems like
a task that should be reserved to gpg-interface.c with the function
check_signature().

Furthermore, the use of verify_signed_buffer() makes it difficult to
introduce new functionality that relies on the content of the GPG status
lines.

Now all operations that does signature verification share a single entry
point to gpg-interface.c.  This makes it easier to propagate changed or
additional functionality in GPG signature verification to all parts of
Git, without having odd edge-cases that don't perform the same degree of
verification.

[1] https://dev.gentoo.org/~mgorny/articles/attack-on-git-signature-verification.html

Signed-off-by: Hans Jerry Illikainen <hji@dyntopia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Hans Jerry Illikainen
2020-03-04 11:48:04 +00:00
committed by Junio C Hamano
parent f1e3df3169
commit 6794898198
4 changed files with 73 additions and 78 deletions

View File

@ -449,22 +449,21 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
{
struct strbuf payload = STRBUF_INIT;
struct strbuf signature = STRBUF_INIT;
struct strbuf gpg_output = STRBUF_INIT;
struct signature_check sigc = { 0 };
int status;
if (parse_signed_commit(commit, &payload, &signature) <= 0)
goto out;
status = verify_signed_buffer(payload.buf, payload.len,
signature.buf, signature.len,
&gpg_output, NULL);
if (status && !gpg_output.len)
strbuf_addstr(&gpg_output, "No signature\n");
show_sig_lines(opt, status, gpg_output.buf);
status = check_signature(payload.buf, payload.len, signature.buf,
signature.len, &sigc);
if (status && !sigc.gpg_output)
show_sig_lines(opt, status, "No signature\n");
else
show_sig_lines(opt, status, sigc.gpg_output);
signature_check_clear(&sigc);
out:
strbuf_release(&gpg_output);
strbuf_release(&payload);
strbuf_release(&signature);
}
@ -497,8 +496,9 @@ static int show_one_mergetag(struct commit *commit,
struct object_id oid;
struct tag *tag;
struct strbuf verify_message;
struct signature_check sigc = { 0 };
int status, nth;
size_t payload_size, gpg_message_offset;
size_t payload_size;
hash_object_file(the_hash_algo, extra->value, extra->len,
type_name(OBJ_TAG), &oid);
@ -520,19 +520,19 @@ static int show_one_mergetag(struct commit *commit,
else
strbuf_addf(&verify_message,
"parent #%d, tagged '%s'\n", nth + 1, tag->tag);
gpg_message_offset = verify_message.len;
payload_size = parse_signature(extra->value, extra->len);
status = -1;
if (extra->len > payload_size) {
/* could have a good signature */
if (!verify_signed_buffer(extra->value, payload_size,
extra->value + payload_size,
extra->len - payload_size,
&verify_message, NULL))
status = 0; /* good */
else if (verify_message.len <= gpg_message_offset)
status = check_signature(extra->value, payload_size,
extra->value + payload_size,
extra->len - payload_size, &sigc);
if (sigc.gpg_output)
strbuf_addstr(&verify_message, sigc.gpg_output);
else
strbuf_addstr(&verify_message, "No signature\n");
signature_check_clear(&sigc);
/* otherwise we couldn't verify, which is shown as bad */
}