File gnupg-CVE-2019-13050_2_of_5.patch of Package gpg2.30813
commit 3a403ab04eeb45f12b34f9d9c421dac93eaf2160
Author: Werner Koch <wk@gnupg.org>
Date: Mon Jul 1 21:53:55 2019 +0200
gpg: Fallback to import with self-sigs-only on too large keyblocks.
* g10/import.c (import_one): Rename to ...
(import_one_real): this. Do not print and update stats on keyring
write errors.
(import_one): New. Add fallback code.
--
GnuPG-bug-id: 4591
Signed-off-by: Werner Koch <wk@gnupg.org>
Index: gnupg-2.0.24/g10/import.c
===================================================================
--- gnupg-2.0.24.orig/g10/import.c
+++ gnupg-2.0.24/g10/import.c
@@ -77,6 +77,7 @@ static int chk_self_sigs( const char *fn
PKT_public_key *pk, u32 *keyid, int *non_self );
static int delete_inv_parts( const char *fname, KBNODE keyblock,
u32 *keyid, unsigned int options );
+static void remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid);
static int merge_blocks( const char *fname, KBNODE keyblock_orig,
KBNODE keyblock, u32 *keyid,
int *n_uids, int *n_sigs, int *n_subk );
@@ -793,8 +794,8 @@ check_prefs(KBNODE keyblock)
* internal errorcount, so that invalid input can be detected by programs
* which called gpg.
*/
-static int
-import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
+static gpg_error_t
+import_one_real (const char *fname, KBNODE keyblock, struct stats_s *stats,
unsigned char **fpr,size_t *fpr_len,unsigned int options,
int from_sk, import_filter_t filter, void *filter_arg)
{
@@ -858,6 +859,13 @@ import_one( const char *fname, KBNODE ke
return 0;
}
+ /* Remove all non-self-sigs if requested. Noe that this is a NOP if
+ * that option has been globally set but we may also be called
+ * latter with the already parsed keyblock and a locally changed
+ * option. This is why we need to remove them here as well. */
+ if ((options & IMPORT_SELF_SIGS_ONLY))
+ remove_all_non_self_sigs (&keyblock, keyid);
+
collapse_uids(&keyblock);
/* Clean the key that we're about to import, to cut down on things
@@ -1033,8 +1041,10 @@ import_one( const char *fname, KBNODE ke
else if(non_self)
revalidation_mark ();
- /* we are ready */
- if( !opt.quiet )
+ /* we are ready. Print and update stats if we got no error.
+ * An error here comes from writing the keyblock and thus
+ * very likely means that no update happened. */
+ if (!opt.quiet)
{
char *p=get_user_id_native(keyid);
if( n_uids == 1 )
@@ -1151,6 +1161,38 @@ import_one( const char *fname, KBNODE ke
return rc;
}
+
+/* Wrapper around import_one_real to retry the import in some cases. */
+static int
+import_one (const char *fname, KBNODE keyblock, struct stats_s *stats,
+ unsigned char **fpr, size_t *fpr_len, unsigned int options,
+ int from_sk, import_filter_t filter, void *filter_arg)
+{
+ gpg_error_t err;
+
+ err = import_one_real (fname, keyblock, stats, fpr, fpr_len, options,
+ from_sk, filter, filter_arg);
+ if (gpg_err_code (err) == GPG_ERR_TOO_LARGE
+ && gpg_err_source (err) == GPG_ERR_SOURCE_KEYBOX
+ && ((options & (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN))
+ != (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN)))
+ {
+ /* We hit the maximum image length. Ask the wrapper to do
+ * everything again but this time with some extra options. */
+ u32 keyid[2];
+
+ keyid_from_pk (keyblock->pkt->pkt.public_key, keyid);
+ log_info ("key %s: keyblock too large, retrying with self-sigs-only\n",
+ keystr (keyid));
+ options |= IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN;
+ err = import_one_real (fname, keyblock, stats, fpr, fpr_len, options,
+ from_sk, filter, filter_arg);
+
+ }
+ return err;
+}
+
+
/* Walk a secret keyblock and produce a public keyblock out of it. */
static KBNODE
sec_to_pub_keyblock(KBNODE sec_keyblock)
@@ -1483,6 +1525,38 @@ import_revoke_cert( const char *fname, K
}
+/* Delete all non-self-sigs from KEYBLOCK.
+ * Returns: True if the keyblock has changed. */
+static void
+remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid)
+{
+ kbnode_t node;
+ unsigned int dropped = 0;
+
+ for (node = *keyblock; node; node = node->next)
+ {
+ if (is_deleted_kbnode (node))
+ continue;
+
+ if (node->pkt->pkttype != PKT_SIGNATURE)
+ continue;
+
+ if (node->pkt->pkt.signature->keyid[0] == keyid[0]
+ && node->pkt->pkt.signature->keyid[1] == keyid[1])
+ continue;
+ delete_kbnode (node);
+ dropped++;
+ }
+
+ if (dropped)
+ commit_kbnode (keyblock);
+
+ if (dropped && opt.verbose)
+ log_info ("key %s: number of dropped non-self-signatures: %u\n",
+ keystr (keyid), dropped);
+}
+
+
/*
* Loop over the keyblock and check all self signatures.
* Mark all user-ids with a self-signature by setting flag bit 0.