mirror of
https://github.com/git/git.git
synced 2025-12-12 20:36:24 +01:00
Merge branch 'mg/gpg-parse-tighten'
Detect and reject a signature block that has more than one GPG signature. * mg/gpg-parse-tighten: gpg-interface.c: detect and reject multiple signatures on commits
This commit is contained in:
@@ -75,48 +75,80 @@ void signature_check_clear(struct signature_check *sigc)
|
||||
FREE_AND_NULL(sigc->key);
|
||||
}
|
||||
|
||||
/* An exclusive status -- only one of them can appear in output */
|
||||
#define GPG_STATUS_EXCLUSIVE (1<<0)
|
||||
|
||||
static struct {
|
||||
char result;
|
||||
const char *check;
|
||||
unsigned int flags;
|
||||
} sigcheck_gpg_status[] = {
|
||||
{ 'G', "\n[GNUPG:] GOODSIG " },
|
||||
{ 'B', "\n[GNUPG:] BADSIG " },
|
||||
{ 'U', "\n[GNUPG:] TRUST_NEVER" },
|
||||
{ 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
|
||||
{ 'E', "\n[GNUPG:] ERRSIG "},
|
||||
{ 'X', "\n[GNUPG:] EXPSIG "},
|
||||
{ 'Y', "\n[GNUPG:] EXPKEYSIG "},
|
||||
{ 'R', "\n[GNUPG:] REVKEYSIG "},
|
||||
{ 'G', "GOODSIG ", GPG_STATUS_EXCLUSIVE },
|
||||
{ 'B', "BADSIG ", GPG_STATUS_EXCLUSIVE },
|
||||
{ 'U', "TRUST_NEVER", 0 },
|
||||
{ 'U', "TRUST_UNDEFINED", 0 },
|
||||
{ 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE },
|
||||
{ 'X', "EXPSIG ", GPG_STATUS_EXCLUSIVE },
|
||||
{ 'Y', "EXPKEYSIG ", GPG_STATUS_EXCLUSIVE },
|
||||
{ 'R', "REVKEYSIG ", GPG_STATUS_EXCLUSIVE },
|
||||
};
|
||||
|
||||
static void parse_gpg_output(struct signature_check *sigc)
|
||||
{
|
||||
const char *buf = sigc->gpg_status;
|
||||
const char *line, *next;
|
||||
int i;
|
||||
int seen_exclusive_status = 0;
|
||||
|
||||
/* Iterate over all search strings */
|
||||
for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
|
||||
const char *found, *next;
|
||||
/* Iterate over all lines */
|
||||
for (line = buf; *line; line = strchrnul(line+1, '\n')) {
|
||||
while (*line == '\n')
|
||||
line++;
|
||||
/* Skip lines that don't start with GNUPG status */
|
||||
if (!skip_prefix(line, "[GNUPG:] ", &line))
|
||||
continue;
|
||||
|
||||
if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
|
||||
found = strstr(buf, sigcheck_gpg_status[i].check);
|
||||
if (!found)
|
||||
continue;
|
||||
found += strlen(sigcheck_gpg_status[i].check);
|
||||
}
|
||||
sigc->result = sigcheck_gpg_status[i].result;
|
||||
/* The trust messages are not followed by key/signer information */
|
||||
if (sigc->result != 'U') {
|
||||
next = strchrnul(found, ' ');
|
||||
sigc->key = xmemdupz(found, next - found);
|
||||
/* The ERRSIG message is not followed by signer information */
|
||||
if (*next && sigc-> result != 'E') {
|
||||
found = next + 1;
|
||||
next = strchrnul(found, '\n');
|
||||
sigc->signer = xmemdupz(found, next - found);
|
||||
/* Iterate over all search strings */
|
||||
for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
|
||||
if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {
|
||||
if (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {
|
||||
if (seen_exclusive_status++)
|
||||
goto found_duplicate_status;
|
||||
}
|
||||
|
||||
sigc->result = sigcheck_gpg_status[i].result;
|
||||
/* The trust messages are not followed by key/signer information */
|
||||
if (sigc->result != 'U') {
|
||||
next = strchrnul(line, ' ');
|
||||
free(sigc->key);
|
||||
sigc->key = xmemdupz(line, next - line);
|
||||
/* The ERRSIG message is not followed by signer information */
|
||||
if (*next && sigc->result != 'E') {
|
||||
line = next + 1;
|
||||
next = strchrnul(line, '\n');
|
||||
free(sigc->signer);
|
||||
sigc->signer = xmemdupz(line, next - line);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
found_duplicate_status:
|
||||
/*
|
||||
* GOODSIG, BADSIG etc. can occur only once for each signature.
|
||||
* Therefore, if we had more than one then we're dealing with multiple
|
||||
* signatures. We don't support them currently, and they're rather
|
||||
* hard to create, so something is likely fishy and we should reject
|
||||
* them altogether.
|
||||
*/
|
||||
sigc->result = 'E';
|
||||
/* Clear partial data to avoid confusion */
|
||||
FREE_AND_NULL(sigc->signer);
|
||||
FREE_AND_NULL(sigc->key);
|
||||
}
|
||||
|
||||
int check_signature(const char *payload, size_t plen, const char *signature,
|
||||
|
||||
Reference in New Issue
Block a user