From e8462a4f88335b200ad0a1c5d96e05a44af7e67c Mon Sep 17 00:00:00 2001 From: Allan McRae Date: Wed, 10 Jan 2018 18:18:34 +1000 Subject: Support new OpenPGP format packets lengths RFC 4880 defines two packet formats for OpenPGP. Pacman aborted its key in keyring check with an error message if it encountered the new format. This was fine until some annoying Arch Trusted User generated a key using the new format! Implement the new format. This also required parsing the hashed sub packets. requiring the parsing code to moved to its own function. Signed-off-by: Allan McRae --- lib/libalpm/signing.c | 178 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 71 deletions(-) (limited to 'lib/libalpm') diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index 51b11df6..40c44f7f 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -999,6 +999,51 @@ static size_t length_check(size_t length, size_t position, size_t a, } } +static int parse_subpacket(alpm_handle_t *handle, const char *identifier, + const unsigned char *sig, const size_t len, const size_t pos, + const size_t plen, alpm_list_t **keys) +{ + size_t slen; + size_t spos = pos; + + while(spos < pos + plen) { + if(sig[spos] < 192) { + slen = sig[spos]; + spos = spos + 1; + } else if(sig[spos] < 255) { + if(length_check(len, spos, 2, handle, identifier) != 0){ + return -1; + } + slen = (sig[spos] << 8) | sig[spos + 1]; + spos = spos + 2; + } else { + if(length_check(len, spos, 5, handle, identifier) != 0) { + return -1; + } + slen = (sig[spos + 1] << 24) | (sig[spos + 2] << 16) | (sig[spos + 3] << 8) | sig[spos + 4]; + spos = spos + 5; + } + if(sig[spos] == 16) { + /* issuer key ID */ + char key[17]; + size_t i; + if(length_check(len, spos, 8, handle, identifier) != 0) { + return -1; + } + for (i = 0; i < 8; i++) { + sprintf(&key[i * 2], "%02X", sig[spos + i + 1]); + } + *keys = alpm_list_add(*keys, strdup(key)); + break; + } + if(length_check(len, spos, slen, handle, identifier) != 0) { + return -1; + } + spos = spos + slen; + } + return 0; +} + /** * Extract the Issuer Key ID from a signature * @param sig PGP signature @@ -1009,7 +1054,7 @@ static size_t length_check(size_t length, size_t position, size_t a, int SYMEXPORT alpm_extract_keyid(alpm_handle_t *handle, const char *identifier, const unsigned char *sig, const size_t len, alpm_list_t **keys) { - size_t pos, spos, blen, hlen, ulen, slen; + size_t pos, blen, hlen, ulen; pos = 0; while(pos < len) { @@ -1020,49 +1065,69 @@ int SYMEXPORT alpm_extract_keyid(alpm_handle_t *handle, const char *identifier, } if(sig[pos] & 0x40) { - /* "new" packet format is not supported */ - _alpm_log(handle, ALPM_LOG_ERROR, - _("%s: unsupported signature format\n"), identifier); - return -1; - } - - if(((sig[pos] & 0x3f) >> 2) != 2) { - /* signature is not a "Signature Packet" */ - _alpm_log(handle, ALPM_LOG_ERROR, - _("%s: signature format error\n"), identifier); - return -1; - } + /* new packet format */ + if(length_check(len, pos, 1, handle, identifier) != 0) { + return -1; + } + pos = pos + 1; - switch(sig[pos] & 0x03) { - case 0: - if(length_check(len, pos, 2, handle, identifier) != 0) { + if(sig[pos] < 192) { + if(length_check(len, pos, 1, handle, identifier) != 0) { return -1; } - blen = sig[pos + 1]; - pos = pos + 2; - break; - - case 1: - if(length_check(len, pos, 3, handle, identifier)) { + blen = sig[pos]; + pos = pos + 1; + } else if (sig[pos] < 224) { + if(length_check(len, pos, 2, handle, identifier) != 0) { return -1; } - blen = (sig[pos + 1] << 8) | sig[pos + 2]; - pos = pos + 3; - break; - - case 2: + blen = ((sig[pos] - 192) << 8) + sig[pos + 1] + 192; + pos = pos + 2; + } else if (sig[pos] == 255) { if(length_check(len, pos, 5, handle, identifier)) { return -1; } blen = (sig[pos + 1] << 24) | (sig[pos + 2] << 16) | (sig[pos + 3] << 8) | sig[pos + 4]; pos = pos + 5; - break; - - case 3: + } else { /* partial body length not supported */ _alpm_log(handle, ALPM_LOG_ERROR, _("%s: unsupported signature format\n"), identifier); return -1; + } + } else { + /* old package format */ + switch(sig[pos] & 0x03) { + case 0: + if(length_check(len, pos, 2, handle, identifier) != 0) { + return -1; + } + blen = sig[pos + 1]; + pos = pos + 2; + break; + + case 1: + if(length_check(len, pos, 3, handle, identifier) != 0) { + return -1; + } + blen = (sig[pos + 1] << 8) | sig[pos + 2]; + pos = pos + 3; + break; + + case 2: + if(length_check(len, pos, 5, handle, identifier) != 0) { + return -1; + } + blen = (sig[pos + 1] << 24) | (sig[pos + 2] << 16) | (sig[pos + 3] << 8) | sig[pos + 4]; + pos = pos + 5; + break; + + case 3: + /* partial body length not supported */ + _alpm_log(handle, ALPM_LOG_ERROR, + _("%s: unsupported signature format\n"), identifier); + return -1; + } } if(sig[pos] != 4) { @@ -1071,14 +1136,12 @@ int SYMEXPORT alpm_extract_keyid(alpm_handle_t *handle, const char *identifier, _("%s: unsupported signature format\n"), identifier); return -1; } - if(sig[pos + 1] != 0x00) { /* not a signature of a binary document */ _alpm_log(handle, ALPM_LOG_ERROR, _("%s: signature format error\n"), identifier); return -1; } - pos = pos + 4; /* pos got changed above, so an explicit check is necessary @@ -1087,55 +1150,28 @@ int SYMEXPORT alpm_extract_keyid(alpm_handle_t *handle, const char *identifier, return -1; } hlen = (sig[pos] << 8) | sig[pos + 1]; + if(length_check(len, pos, hlen + 2, handle, identifier) != 0) { + return -1; + } + pos = pos + 2; - if(length_check(len, pos, hlen + 2, handle, identifier)) { + if(parse_subpacket(handle, identifier, sig, len, pos, hlen, keys) == -1) { return -1; } - pos = pos + hlen + 2; + pos = pos + hlen; ulen = (sig[pos] << 8) | sig[pos + 1]; + if(length_check(len, pos, ulen + 2, handle, identifier) != 0) { + return -1; + } pos = pos + 2; - spos = pos; - - while(spos < pos + ulen) { - if(sig[spos] < 192) { - slen = sig[spos]; - spos = spos + 1; - } else if(sig[spos] < 255) { - if(length_check(pos + ulen, spos, 2, handle, identifier)){ - return -1; - } - slen = (sig[spos] << 8) | sig[spos + 1]; - spos = spos + 2; - } else { - /* check for pos and spos, as spos is still pos */ - if(length_check(len, pos, 5, handle, identifier)) { - return -1; - } - slen = (sig[spos + 1] << 24) | (sig[spos + 2] << 16) | (sig[spos + 3] << 8) | sig[spos + 4]; - spos = spos + 5; - } - if(sig[spos] == 16) { - /* issuer key ID */ - char key[17]; - size_t i; - if(length_check(pos + ulen, spos, 8, handle, identifier)) { - return -1; - } - for (i = 0; i < 8; i++) { - sprintf(&key[i * 2], "%02X", sig[spos + i + 1]); - } - *keys = alpm_list_add(*keys, strdup(key)); - break; - } - if(length_check(pos + ulen + 1, spos, slen, handle, identifier)) { - return -1; - } - spos = spos + slen; + if(parse_subpacket(handle, identifier, sig, len, pos, ulen, keys) == -1) { + return -1; } pos = pos + (blen - hlen - 8); } + return 0; } -- cgit v1.2.3-24-g4f1b