aboutsummaryrefslogtreecommitdiffstats
path: root/g10/import.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/import.c')
-rw-r--r--g10/import.c256
1 files changed, 208 insertions, 48 deletions
diff --git a/g10/import.c b/g10/import.c
index c2a1dd033..c32dbf059 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -102,7 +102,7 @@ static int import (ctrl_t ctrl,
unsigned char **fpr, size_t *fpr_len, unsigned int options,
import_screener_t screener, void *screener_arg,
int origin, const char *url);
-static int read_block (IOBUF a, int with_meta,
+static int read_block (IOBUF a, unsigned int options,
PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys);
static void revocation_present (ctrl_t ctrl, kbnode_t keyblock);
static gpg_error_t import_one (ctrl_t ctrl,
@@ -129,6 +129,7 @@ static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock,
u32 *keyid, unsigned int options);
static int any_uid_left (kbnode_t keyblock);
static int remove_all_uids (kbnode_t *keyblock);
+static void remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid);
static int merge_blocks (ctrl_t ctrl, unsigned int options,
kbnode_t keyblock_orig,
kbnode_t keyblock, u32 *keyid,
@@ -190,7 +191,10 @@ parse_import_options(char *str,unsigned int *options,int noisy)
N_("remove as much as possible from key after import")},
{"import-drop-uids", IMPORT_DROP_UIDS, NULL,
- N_("Do not import user id or attribute packets")},
+ N_("do not import user id or attribute packets")},
+
+ {"self-sigs-only", IMPORT_SELF_SIGS_ONLY, NULL,
+ N_("ignore key-signatures which are not self-signatures")},
{"import-export", IMPORT_EXPORT, NULL,
N_("run import filters and export key immediately")},
@@ -589,8 +593,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats,
release_armor_context (afx);
}
- while (!(rc = read_block (inp, !!(options & IMPORT_RESTORE),
- &pending_pkt, &keyblock, &v3keys)))
+ while (!(rc = read_block (inp, options, &pending_pkt, &keyblock, &v3keys)))
{
stats->v3keys += v3keys;
if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY)
@@ -669,6 +672,18 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats,
if (!(++stats->count % 100) && !opt.quiet)
log_info (_("%lu keys processed so far\n"), stats->count );
+
+ if (origin == KEYORG_WKD && stats->count >= 5)
+ {
+ /* We limit the number of keys _received_ from the WKD to 5.
+ * In fact there should be only one key but some sites want
+ * to store a few expired keys there also. gpg's key
+ * selection will later figure out which key to use. Note
+ * that for WKD we always return the fingerprint of the
+ * first imported key. */
+ log_info ("import from WKD stopped after %d keys\n", 5);
+ break;
+ }
}
stats->v3keys += v3keys;
if (rc == -1)
@@ -677,6 +692,13 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats,
log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (rc));
release_kbnode (secattic);
+
+ /* When read_block loop was stopped by error, we have PENDING_PKT left. */
+ if (pending_pkt)
+ {
+ free_packet (pending_pkt, NULL);
+ xfree (pending_pkt);
+ }
return rc;
}
@@ -826,31 +848,39 @@ valid_keyblock_packet (int pkttype)
}
-/****************
- * Read the next keyblock from stream A.
- * Meta data (ring trust packets) are only considered of WITH_META is set.
- * PENDING_PKT should be initialized to NULL and not changed by the caller.
- * Return: 0 = okay, -1 no more blocks or another errorcode.
- * The int at R_V3KEY counts the number of unsupported v3
- * keyblocks.
+/* Read the next keyblock from stream A. Meta data (ring trust
+ * packets) are only considered if OPTIONS has the IMPORT_RESTORE flag
+ * set. PENDING_PKT should be initialized to NULL and not changed by
+ * the caller.
+ *
+ * Returns 0 for okay, -1 no more blocks, or any other errorcode. The
+ * integer at R_V3KEY counts the number of unsupported v3 keyblocks.
*/
static int
-read_block( IOBUF a, int with_meta,
+read_block( IOBUF a, unsigned int options,
PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys)
{
int rc;
struct parse_packet_ctx_s parsectx;
PACKET *pkt;
kbnode_t root = NULL;
+ kbnode_t lastnode = NULL;
int in_cert, in_v3key, skip_sigs;
+ u32 keyid[2];
+ int got_keyid = 0;
+ unsigned int dropped_nonselfsigs = 0;
*r_v3keys = 0;
if (*pending_pkt)
{
- root = new_kbnode( *pending_pkt );
+ root = lastnode = new_kbnode( *pending_pkt );
*pending_pkt = NULL;
+ log_assert (root->pkt->pkttype == PKT_PUBLIC_KEY
+ || root->pkt->pkttype == PKT_SECRET_KEY);
in_cert = 1;
+ keyid_from_pk (root->pkt->pkt.public_key, keyid);
+ got_keyid = 1;
}
else
in_cert = 0;
@@ -858,7 +888,7 @@ read_block( IOBUF a, int with_meta,
pkt = xmalloc (sizeof *pkt);
init_packet (pkt);
init_parse_packet (&parsectx, a);
- if (!with_meta)
+ if (!(options & IMPORT_RESTORE))
parsectx.skip_meta = 1;
in_v3key = 0;
skip_sigs = 0;
@@ -965,24 +995,59 @@ read_block( IOBUF a, int with_meta,
init_packet(pkt);
break;
+ case PKT_SIGNATURE:
+ if (!in_cert)
+ goto x_default;
+ if (!(options & IMPORT_SELF_SIGS_ONLY))
+ goto x_default;
+ log_assert (got_keyid);
+ if (pkt->pkt.signature->keyid[0] == keyid[0]
+ && pkt->pkt.signature->keyid[1] == keyid[1])
+ { /* This is likely a self-signature. We import this one.
+ * Eventually we should use the ISSUER_FPR to compare
+ * self-signatures, but that will work only for v5 keys
+ * which are currently not even deployed.
+ * Note that we do not do any crypto verify here because
+ * that would defeat this very mitigation of DoS by
+ * importing a key with a huge amount of faked
+ * key-signatures. A verification will be done later in
+ * the processing anyway. Here we want a cheap an early
+ * way to drop non-self-signatures. */
+ goto x_default;
+ }
+ /* Skip this signature. */
+ dropped_nonselfsigs++;
+ free_packet (pkt, &parsectx);
+ init_packet(pkt);
+ break;
+
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
- if (in_cert ) /* Store this packet. */
+ if (!got_keyid)
+ {
+ keyid_from_pk (pkt->pkt.public_key, keyid);
+ got_keyid = 1;
+ }
+ if (in_cert) /* Store this packet. */
{
*pending_pkt = pkt;
pkt = NULL;
goto ready;
}
in_cert = 1;
- /* fall through */
+ goto x_default;
+
default:
x_default:
if (in_cert && valid_keyblock_packet (pkt->pkttype))
{
if (!root )
- root = new_kbnode (pkt);
+ root = lastnode = new_kbnode (pkt);
else
- add_kbnode (root, new_kbnode (pkt));
+ {
+ lastnode->next = new_kbnode (pkt);
+ lastnode = lastnode->next;
+ }
pkt = xmalloc (sizeof *pkt);
}
else
@@ -1003,6 +1068,10 @@ read_block( IOBUF a, int with_meta,
free_packet (pkt, &parsectx);
deinit_parse_packet (&parsectx);
xfree( pkt );
+ if (!rc && dropped_nonselfsigs && opt.verbose)
+ log_info ("key %s: number of dropped non-self-signatures: %u\n",
+ keystr (keyid), dropped_nonselfsigs);
+
return rc;
}
@@ -1729,12 +1798,12 @@ update_key_origin (kbnode_t keyblock, u32 curtime, int origin, const char *url)
* has valid parts.
*/
static gpg_error_t
-import_one (ctrl_t ctrl,
- kbnode_t keyblock, struct import_stats_s *stats,
- unsigned char **fpr, size_t *fpr_len, unsigned int options,
- int from_sk, int silent,
- import_screener_t screener, void *screener_arg,
- int origin, const char *url, int *r_valid)
+import_one_real (ctrl_t ctrl,
+ kbnode_t keyblock, struct import_stats_s *stats,
+ unsigned char **fpr, size_t *fpr_len, unsigned int options,
+ int from_sk, int silent,
+ import_screener_t screener, void *screener_arg,
+ int origin, const char *url, int *r_valid)
{
gpg_error_t err = 0;
PKT_public_key *pk;
@@ -1817,6 +1886,13 @@ import_one (ctrl_t ctrl,
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);
+
/* Remove or collapse the user ids. */
if ((options & IMPORT_DROP_UIDS))
remove_all_uids (&keyblock);
@@ -2026,22 +2102,25 @@ import_one (ctrl_t ctrl,
hd = NULL;
/* We are ready. */
- if (!opt.quiet && !silent)
+ if (!err && !opt.quiet && !silent)
{
- char *p = get_user_id_byfpr_native (ctrl, fpr2);
+ char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len);
log_info (_("key %s: public key \"%s\" imported\n"),
keystr(keyid), p);
xfree(p);
}
- if (is_status_enabled())
+ if (!err && is_status_enabled())
{
char *us = get_long_user_id_string (ctrl, keyid);
write_status_text( STATUS_IMPORTED, us );
xfree(us);
print_import_ok (pk, 1);
}
- stats->imported++;
- new_key = 1;
+ if (!err)
+ {
+ stats->imported++;
+ new_key = 1;
+ }
}
else /* Key already exists - merge. */
{
@@ -2111,10 +2190,12 @@ import_one (ctrl_t ctrl,
keydb_release (hd);
hd = NULL;
- /* We are ready. */
- if (!opt.quiet && !silent)
+ /* 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 (!err && !opt.quiet && !silent)
{
- char *p = get_user_id_byfpr_native (ctrl, fpr2);
+ char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len);
if (n_uids == 1 )
log_info( _("key %s: \"%s\" 1 new user ID\n"),
keystr(keyid),p);
@@ -2148,14 +2229,17 @@ import_one (ctrl_t ctrl,
xfree(p);
}
- stats->n_uids +=n_uids;
- stats->n_sigs +=n_sigs;
- stats->n_subk +=n_subk;
- stats->n_sigs_cleaned +=n_sigs_cleaned;
- stats->n_uids_cleaned +=n_uids_cleaned;
-
- if (is_status_enabled () && !silent)
- print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
+ if (!err)
+ {
+ stats->n_uids +=n_uids;
+ stats->n_sigs +=n_sigs;
+ stats->n_subk +=n_subk;
+ stats->n_sigs_cleaned +=n_sigs_cleaned;
+ stats->n_uids_cleaned +=n_uids_cleaned;
+
+ if (is_status_enabled () && !silent)
+ print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
+ }
}
else
{
@@ -2175,7 +2259,7 @@ import_one (ctrl_t ctrl,
if (!opt.quiet && !silent)
{
- char *p = get_user_id_byfpr_native (ctrl, fpr2);
+ char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len);
log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p);
xfree(p);
}
@@ -2203,14 +2287,19 @@ import_one (ctrl_t ctrl,
fingerprint of the key in all cases. */
if (fpr)
{
- xfree (*fpr);
/* Note that we need to compare against 0 here because
COUNT gets only incremented after returning from this
function. */
if (!stats->count)
- *fpr = fingerprint_from_pk (pk, NULL, fpr_len);
- else
- *fpr = NULL;
+ {
+ xfree (*fpr);
+ *fpr = fingerprint_from_pk (pk, NULL, fpr_len);
+ }
+ else if (origin != KEYORG_WKD)
+ {
+ xfree (*fpr);
+ *fpr = NULL;
+ }
}
}
@@ -2237,6 +2326,41 @@ import_one (ctrl_t ctrl,
}
+/* Wrapper around import_one_real to retry the import in some cases. */
+static gpg_error_t
+import_one (ctrl_t ctrl,
+ kbnode_t keyblock, struct import_stats_s *stats,
+ unsigned char **fpr, size_t *fpr_len, unsigned int options,
+ int from_sk, int silent,
+ import_screener_t screener, void *screener_arg,
+ int origin, const char *url, int *r_valid)
+{
+ gpg_error_t err;
+
+ err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options,
+ from_sk, silent, screener, screener_arg,
+ origin, url, r_valid);
+ 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 (ctrl, keyblock, stats, fpr, fpr_len, options,
+ from_sk, silent, screener, screener_arg,
+ origin, url, r_valid);
+ }
+ return err;
+}
+
+
/* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The
* function prints diagnostics and returns an error code. If BATCH is
* true the secret keys are stored by gpg-agent in the transfer format
@@ -2544,6 +2668,7 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock)
kbnode_t pub_keyblock = NULL;
kbnode_t ctx = NULL;
kbnode_t secnode, pubnode;
+ kbnode_t lastnode = NULL;
unsigned int tag = 0;
/* Set a tag to all nodes. */
@@ -2583,9 +2708,12 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock)
pubnode->tag = secnode->tag;
if (!pub_keyblock)
- pub_keyblock = pubnode;
+ pub_keyblock = lastnode = pubnode;
else
- add_kbnode (pub_keyblock, pubnode);
+ {
+ lastnode->next = pubnode;
+ lastnode = pubnode;
+ }
}
return pub_keyblock;
@@ -2915,7 +3043,7 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock,
/* The secret keyblock may not have nodes which are deleted in
* the public keyblock. Otherwise we would import just the
* secret key without having the public key. That would be
- * surprising and clutters out private-keys-v1.d. */
+ * surprising and clutters our private-keys-v1.d. */
err = resync_sec_with_pub_keyblock (&keyblock, pub_keyblock, &attic);
if (err)
goto leave;
@@ -3764,6 +3892,38 @@ remove_all_uids (kbnode_t *keyblock)
}
+/* 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);
+}
+
+
/*
* It may happen that the imported keyblock has duplicated user IDs.
* We check this here and collapse those user IDs together with their