Skip to content

Commit a041c0d

Browse files
illikainengitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a90aca1 commit a041c0d

File tree

4 files changed

+73
-78
lines changed

4 files changed

+73
-78
lines changed

builtin/fmt-merge-msg.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
494494
enum object_type type;
495495
unsigned long size, len;
496496
char *buf = read_object_file(oid, &type, &size);
497+
struct signature_check sigc = { 0 };
497498
struct strbuf sig = STRBUF_INIT;
498499

499500
if (!buf || type != OBJ_TAG)
@@ -502,10 +503,12 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
502503

503504
if (size == len)
504505
; /* merely annotated */
505-
else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig, NULL)) {
506-
if (!sig.len)
507-
strbuf_addstr(&sig, "gpg verification failed.\n");
508-
}
506+
else if (check_signature(buf, len, buf + len, size - len, &sigc) &&
507+
!sigc.gpg_output)
508+
strbuf_addstr(&sig, "gpg verification failed.\n");
509+
else
510+
strbuf_addstr(&sig, sigc.gpg_output);
511+
signature_check_clear(&sigc);
509512

510513
if (!tag_number++) {
511514
fmt_tag_signature(&tagbuf, &sig, buf, len);

gpg-interface.c

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,55 @@ static void parse_gpg_output(struct signature_check *sigc)
256256
FREE_AND_NULL(sigc->key);
257257
}
258258

259+
static int verify_signed_buffer(const char *payload, size_t payload_size,
260+
const char *signature, size_t signature_size,
261+
struct strbuf *gpg_output,
262+
struct strbuf *gpg_status)
263+
{
264+
struct child_process gpg = CHILD_PROCESS_INIT;
265+
struct gpg_format *fmt;
266+
struct tempfile *temp;
267+
int ret;
268+
struct strbuf buf = STRBUF_INIT;
269+
270+
temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
271+
if (!temp)
272+
return error_errno(_("could not create temporary file"));
273+
if (write_in_full(temp->fd, signature, signature_size) < 0 ||
274+
close_tempfile_gently(temp) < 0) {
275+
error_errno(_("failed writing detached signature to '%s'"),
276+
temp->filename.buf);
277+
delete_tempfile(&temp);
278+
return -1;
279+
}
280+
281+
fmt = get_format_by_sig(signature);
282+
if (!fmt)
283+
BUG("bad signature '%s'", signature);
284+
285+
argv_array_push(&gpg.args, fmt->program);
286+
argv_array_pushv(&gpg.args, fmt->verify_args);
287+
argv_array_pushl(&gpg.args,
288+
"--status-fd=1",
289+
"--verify", temp->filename.buf, "-",
290+
NULL);
291+
292+
if (!gpg_status)
293+
gpg_status = &buf;
294+
295+
sigchain_push(SIGPIPE, SIG_IGN);
296+
ret = pipe_command(&gpg, payload, payload_size,
297+
gpg_status, 0, gpg_output, 0);
298+
sigchain_pop(SIGPIPE);
299+
300+
delete_tempfile(&temp);
301+
302+
ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
303+
strbuf_release(&buf); /* no matter it was used or not */
304+
305+
return ret;
306+
}
307+
259308
int check_signature(const char *payload, size_t plen, const char *signature,
260309
size_t slen, struct signature_check *sigc)
261310
{
@@ -418,51 +467,3 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
418467

419468
return 0;
420469
}
421-
422-
int verify_signed_buffer(const char *payload, size_t payload_size,
423-
const char *signature, size_t signature_size,
424-
struct strbuf *gpg_output, struct strbuf *gpg_status)
425-
{
426-
struct child_process gpg = CHILD_PROCESS_INIT;
427-
struct gpg_format *fmt;
428-
struct tempfile *temp;
429-
int ret;
430-
struct strbuf buf = STRBUF_INIT;
431-
432-
temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
433-
if (!temp)
434-
return error_errno(_("could not create temporary file"));
435-
if (write_in_full(temp->fd, signature, signature_size) < 0 ||
436-
close_tempfile_gently(temp) < 0) {
437-
error_errno(_("failed writing detached signature to '%s'"),
438-
temp->filename.buf);
439-
delete_tempfile(&temp);
440-
return -1;
441-
}
442-
443-
fmt = get_format_by_sig(signature);
444-
if (!fmt)
445-
BUG("bad signature '%s'", signature);
446-
447-
argv_array_push(&gpg.args, fmt->program);
448-
argv_array_pushv(&gpg.args, fmt->verify_args);
449-
argv_array_pushl(&gpg.args,
450-
"--status-fd=1",
451-
"--verify", temp->filename.buf, "-",
452-
NULL);
453-
454-
if (!gpg_status)
455-
gpg_status = &buf;
456-
457-
sigchain_push(SIGPIPE, SIG_IGN);
458-
ret = pipe_command(&gpg, payload, payload_size,
459-
gpg_status, 0, gpg_output, 0);
460-
sigchain_pop(SIGPIPE);
461-
462-
delete_tempfile(&temp);
463-
464-
ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
465-
strbuf_release(&buf); /* no matter it was used or not */
466-
467-
return ret;
468-
}

gpg-interface.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,6 @@ size_t parse_signature(const char *buf, size_t size);
5454
int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
5555
const char *signing_key);
5656

57-
/*
58-
* Run "gpg" to see if the payload matches the detached signature.
59-
* gpg_output, when set, receives the diagnostic output from GPG.
60-
* gpg_status, when set, receives the status output from GPG.
61-
*/
62-
int verify_signed_buffer(const char *payload, size_t payload_size,
63-
const char *signature, size_t signature_size,
64-
struct strbuf *gpg_output, struct strbuf *gpg_status);
65-
6657
int git_gpg_config(const char *, const char *, void *);
6758
void set_signing_key(const char *);
6859
const char *get_signing_key(void);

log-tree.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -449,22 +449,21 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
449449
{
450450
struct strbuf payload = STRBUF_INIT;
451451
struct strbuf signature = STRBUF_INIT;
452-
struct strbuf gpg_output = STRBUF_INIT;
452+
struct signature_check sigc = { 0 };
453453
int status;
454454

455455
if (parse_signed_commit(commit, &payload, &signature) <= 0)
456456
goto out;
457457

458-
status = verify_signed_buffer(payload.buf, payload.len,
459-
signature.buf, signature.len,
460-
&gpg_output, NULL);
461-
if (status && !gpg_output.len)
462-
strbuf_addstr(&gpg_output, "No signature\n");
463-
464-
show_sig_lines(opt, status, gpg_output.buf);
458+
status = check_signature(payload.buf, payload.len, signature.buf,
459+
signature.len, &sigc);
460+
if (status && !sigc.gpg_output)
461+
show_sig_lines(opt, status, "No signature\n");
462+
else
463+
show_sig_lines(opt, status, sigc.gpg_output);
464+
signature_check_clear(&sigc);
465465

466466
out:
467-
strbuf_release(&gpg_output);
468467
strbuf_release(&payload);
469468
strbuf_release(&signature);
470469
}
@@ -497,8 +496,9 @@ static int show_one_mergetag(struct commit *commit,
497496
struct object_id oid;
498497
struct tag *tag;
499498
struct strbuf verify_message;
499+
struct signature_check sigc = { 0 };
500500
int status, nth;
501-
size_t payload_size, gpg_message_offset;
501+
size_t payload_size;
502502

503503
hash_object_file(the_hash_algo, extra->value, extra->len,
504504
type_name(OBJ_TAG), &oid);
@@ -520,19 +520,19 @@ static int show_one_mergetag(struct commit *commit,
520520
else
521521
strbuf_addf(&verify_message,
522522
"parent #%d, tagged '%s'\n", nth + 1, tag->tag);
523-
gpg_message_offset = verify_message.len;
524523

525524
payload_size = parse_signature(extra->value, extra->len);
526525
status = -1;
527526
if (extra->len > payload_size) {
528527
/* could have a good signature */
529-
if (!verify_signed_buffer(extra->value, payload_size,
530-
extra->value + payload_size,
531-
extra->len - payload_size,
532-
&verify_message, NULL))
533-
status = 0; /* good */
534-
else if (verify_message.len <= gpg_message_offset)
528+
status = check_signature(extra->value, payload_size,
529+
extra->value + payload_size,
530+
extra->len - payload_size, &sigc);
531+
if (sigc.gpg_output)
532+
strbuf_addstr(&verify_message, sigc.gpg_output);
533+
else
535534
strbuf_addstr(&verify_message, "No signature\n");
535+
signature_check_clear(&sigc);
536536
/* otherwise we couldn't verify, which is shown as bad */
537537
}
538538

0 commit comments

Comments
 (0)