Skip to content

Commit 72b006f

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 67a6ea6 commit 72b006f

File tree

4 files changed

+72
-75
lines changed

4 files changed

+72
-75
lines changed

builtin/fmt-merge-msg.c

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

500501
if (!buf || type != OBJ_TAG)
@@ -503,10 +504,12 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
503504

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

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

gpg-interface.c

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

210+
static int verify_signed_buffer(const char *payload, size_t payload_size,
211+
const char *signature, size_t signature_size,
212+
struct strbuf *gpg_output,
213+
struct strbuf *gpg_status)
214+
{
215+
struct child_process gpg = CHILD_PROCESS_INIT;
216+
struct gpg_format *fmt;
217+
struct tempfile *temp;
218+
int ret;
219+
struct strbuf buf = STRBUF_INIT;
220+
221+
temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
222+
if (!temp)
223+
return error_errno(_("could not create temporary file"));
224+
if (write_in_full(temp->fd, signature, signature_size) < 0 ||
225+
close_tempfile_gently(temp) < 0) {
226+
error_errno(_("failed writing detached signature to '%s'"),
227+
temp->filename.buf);
228+
delete_tempfile(&temp);
229+
return -1;
230+
}
231+
232+
fmt = get_format_by_sig(signature);
233+
if (!fmt)
234+
BUG("bad signature '%s'", signature);
235+
236+
argv_array_push(&gpg.args, fmt->program);
237+
argv_array_pushv(&gpg.args, fmt->verify_args);
238+
argv_array_pushl(&gpg.args,
239+
"--status-fd=1",
240+
"--verify", temp->filename.buf, "-",
241+
NULL);
242+
243+
if (!gpg_status)
244+
gpg_status = &buf;
245+
246+
sigchain_push(SIGPIPE, SIG_IGN);
247+
ret = pipe_command(&gpg, payload, payload_size,
248+
gpg_status, 0, gpg_output, 0);
249+
sigchain_pop(SIGPIPE);
250+
251+
delete_tempfile(&temp);
252+
253+
ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
254+
strbuf_release(&buf); /* no matter it was used or not */
255+
256+
return ret;
257+
}
258+
210259
int check_signature(const char *payload, size_t plen, const char *signature,
211260
size_t slen, struct signature_check *sigc)
212261
{
@@ -351,51 +400,3 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
351400

352401
return 0;
353402
}
354-
355-
int verify_signed_buffer(const char *payload, size_t payload_size,
356-
const char *signature, size_t signature_size,
357-
struct strbuf *gpg_output, struct strbuf *gpg_status)
358-
{
359-
struct child_process gpg = CHILD_PROCESS_INIT;
360-
struct gpg_format *fmt;
361-
struct tempfile *temp;
362-
int ret;
363-
struct strbuf buf = STRBUF_INIT;
364-
365-
temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
366-
if (!temp)
367-
return error_errno(_("could not create temporary file"));
368-
if (write_in_full(temp->fd, signature, signature_size) < 0 ||
369-
close_tempfile_gently(temp) < 0) {
370-
error_errno(_("failed writing detached signature to '%s'"),
371-
temp->filename.buf);
372-
delete_tempfile(&temp);
373-
return -1;
374-
}
375-
376-
fmt = get_format_by_sig(signature);
377-
if (!fmt)
378-
BUG("bad signature '%s'", signature);
379-
380-
argv_array_push(&gpg.args, fmt->program);
381-
argv_array_pushv(&gpg.args, fmt->verify_args);
382-
argv_array_pushl(&gpg.args,
383-
"--status-fd=1",
384-
"--verify", temp->filename.buf, "-",
385-
NULL);
386-
387-
if (!gpg_status)
388-
gpg_status = &buf;
389-
390-
sigchain_push(SIGPIPE, SIG_IGN);
391-
ret = pipe_command(&gpg, payload, payload_size,
392-
gpg_status, 0, gpg_output, 0);
393-
sigchain_pop(SIGPIPE);
394-
395-
delete_tempfile(&temp);
396-
397-
ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
398-
strbuf_release(&buf); /* no matter it was used or not */
399-
400-
return ret;
401-
}

gpg-interface.h

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

49-
/*
50-
* Run "gpg" to see if the payload matches the detached signature.
51-
* gpg_output, when set, receives the diagnostic output from GPG.
52-
* gpg_status, when set, receives the status output from GPG.
53-
*/
54-
int verify_signed_buffer(const char *payload, size_t payload_size,
55-
const char *signature, size_t signature_size,
56-
struct strbuf *gpg_output, struct strbuf *gpg_status);
57-
5849
int git_gpg_config(const char *, const char *, void *);
5950
void set_signing_key(const char *);
6051
const char *get_signing_key(void);

log-tree.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -448,22 +448,22 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
448448
{
449449
struct strbuf payload = STRBUF_INIT;
450450
struct strbuf signature = STRBUF_INIT;
451-
struct strbuf gpg_output = STRBUF_INIT;
451+
struct signature_check sigc = { 0 };
452452
int status;
453453

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

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

465466
out:
466-
strbuf_release(&gpg_output);
467467
strbuf_release(&payload);
468468
strbuf_release(&signature);
469469
}
@@ -496,6 +496,7 @@ static int show_one_mergetag(struct commit *commit,
496496
struct object_id oid;
497497
struct tag *tag;
498498
struct strbuf verify_message;
499+
struct signature_check sigc = { 0 };
499500
int status, nth;
500501
size_t payload_size, gpg_message_offset;
501502

@@ -524,12 +525,13 @@ static int show_one_mergetag(struct commit *commit,
524525
status = -1;
525526
if (extra->len > payload_size) {
526527
/* could have a good signature */
527-
if (!verify_signed_buffer(extra->value, payload_size,
528-
extra->value + payload_size,
529-
extra->len - payload_size,
530-
&verify_message, NULL))
528+
if (!check_signature(extra->value, payload_size,
529+
extra->value + payload_size,
530+
extra->len - payload_size, &sigc)) {
531+
strbuf_addstr(&verify_message, sigc.gpg_output);
532+
signature_check_clear(&sigc);
531533
status = 0; /* good */
532-
else if (verify_message.len <= gpg_message_offset)
534+
} else if (verify_message.len <= gpg_message_offset)
533535
strbuf_addstr(&verify_message, "No signature\n");
534536
/* otherwise we couldn't verify, which is shown as bad */
535537
}

0 commit comments

Comments
 (0)