From: Junio C Hamano Date: Fri, 2 Nov 2018 15:53:57 +0000 (+0900) Subject: Merge branch 'mg/gpg-parse-tighten' X-Git-Tag: v2.20.0-rc0~79 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/02561896de9d4cd2354e825bf7e6304a3c19d1c3?ds=inline;hp=-c 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 --- 02561896de9d4cd2354e825bf7e6304a3c19d1c3 diff --combined gpg-interface.c index db17d65f8a,efe2c0d386..d72a43b774 --- a/gpg-interface.c +++ b/gpg-interface.c @@@ -75,48 -75,80 +75,80 @@@ void signature_check_clear(struct signa 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; - - /* Iterate over all search strings */ - for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) { - const char *found, *next; - - 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); + int seen_exclusive_status = 0; + + /* 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; + + /* 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 > 1) ++ 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,