diff --git a/g10/export.c b/g10/export.c index 21ff23c8d..e94e959fb 100644 --- a/g10/export.c +++ b/g10/export.c @@ -2003,15 +2003,18 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, } /* Always do the cleaning on the public key part if requested. - * Note that both export-clean and export-minimal only apply to - * UID sigs (0x10, 0x11, 0x12, and 0x13). A designated - * revocation is never stripped, even with export-minimal set. */ + * A designated revocation is never stripped, even with + * export-minimal set. */ if ((options & EXPORT_CLEAN)) { merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&EXPORT_MINIMAL), NULL, NULL); - clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, + (options&EXPORT_MINIMAL)? KEY_CLEAN_ALL + /**/ : KEY_CLEAN_AUTHENCR, + NULL, NULL); + commit_kbnode (&keyblock); } if (export_keep_uid) diff --git a/g10/import.c b/g10/import.c index 6b6411c24..6dad8ee55 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1746,7 +1746,8 @@ import_one (ctrl_t ctrl, merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL); - clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); } clear_kbnode_flags( keyblock ); @@ -1892,7 +1893,8 @@ import_one (ctrl_t ctrl, merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), &n_uids_cleaned,&n_sigs_cleaned); - clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); } /* Unless we are in restore mode apply meta data to the @@ -1983,7 +1985,8 @@ import_one (ctrl_t ctrl, clean_all_uids (ctrl, keyblock_orig, opt.verbose, (options&IMPORT_MINIMAL), &n_uids_cleaned,&n_sigs_cleaned); - clean_all_subkeys (ctrl, keyblock_orig, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock_orig, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); } if (n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) diff --git a/g10/key-clean.c b/g10/key-clean.c index 10478a46e..097ca17e9 100644 --- a/g10/key-clean.c +++ b/g10/key-clean.c @@ -408,24 +408,101 @@ clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, } -/* This function only marks the deleted nodes and the caller is - * responsible to skip or remove them. Needs to be called after a - * merge_keys_and_selfsig. */ -void -clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, - int *subkeys_cleaned, int *sigs_cleaned) +/* Helper for clean_all_subkeys. */ +static int +clean_one_subkey (ctrl_t ctrl, kbnode_t subkeynode, int noisy, int clean_level) { kbnode_t node; + PKT_public_key *pk = subkeynode->pkt->pkt.public_key; + unsigned int use = pk->pubkey_usage; + int do_clean = 0; + + (void)ctrl; + (void)noisy; + + log_assert (subkeynode->pkt->pkttype == PKT_PUBLIC_SUBKEY + || subkeynode->pkt->pkttype == PKT_SECRET_SUBKEY); + + if (DBG_LOOKUP) + log_debug ("\tchecking subkey %08lX [%c%c%c%c%c]\n", + (ulong) keyid_from_pk (pk, NULL), + (use & PUBKEY_USAGE_ENC)? 'e':'-', + (use & PUBKEY_USAGE_SIG)? 's':'-', + (use & PUBKEY_USAGE_CERT)? 'c':'-', + (use & PUBKEY_USAGE_AUTH)? 'a':'-', + (use & PUBKEY_USAGE_UNKNOWN)? '?':'-'); + + if (!pk->flags.valid) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey not valid\n"); + if (clean_level == KEY_CLEAN_INVALID) + do_clean = 1; + } + if (pk->has_expired) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey has expired\n"); + if (clean_level == KEY_CLEAN_ALL) + do_clean = 1; + else if (clean_level == KEY_CLEAN_AUTHENCR + && (use & (PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH)) + && !(use & (PUBKEY_USAGE_SIG | PUBKEY_USAGE_CERT))) + do_clean = 1; + else if (clean_level == KEY_CLEAN_ENCR + && (use & PUBKEY_USAGE_ENC) + && !(use & (PUBKEY_USAGE_SIG | PUBKEY_USAGE_CERT + | PUBKEY_USAGE_AUTH))) + do_clean = 1; + } + if (pk->flags.revoked) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey has been revoked (keeping)\n"); + /* Avoid any cleaning because revocations are important. */ + do_clean = 0; + } + if (!do_clean) + return 0; + + if (DBG_LOOKUP) + log_debug ("\t=> removing this subkey\n"); + + delete_kbnode (subkeynode); + for (node = subkeynode->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + delete_kbnode (node); + + return 1; +} + + +/* This function only marks the deleted nodes and the caller is + * responsible to skip or remove them. Needs to be called after a + * merge_keys_and_selfsig. CLEAN_LEVEL is one of the KEY_CLEAN_* + * values. */ +void +clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, int clean_level, + int *subkeys_cleaned, int *sigs_cleaned) +{ + kbnode_t first_subkey, node; + + if (DBG_LOOKUP) + log_debug ("clean_all_subkeys: checking key %08lX\n", + (ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL)); for (node = keyblock->next; node; node = node->next) if (!is_deleted_kbnode (node) && (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY)) break; + first_subkey = node; /* Remove bogus subkey binding signatures: The only signatures * allowed are of class 0x18 and 0x28. */ - for (; node; node = node->next) + for (node = first_subkey; node; node = node->next) { if (is_deleted_kbnode (node)) continue; @@ -438,4 +515,21 @@ clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, ++*sigs_cleaned; } } + + /* Do the selected cleaning. */ + if (clean_level > KEY_CLEAN_NONE) + { + for (node = first_subkey; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + if (clean_one_subkey (ctrl, node, noisy, clean_level)) + { + if (subkeys_cleaned) + ++*subkeys_cleaned; + } + } + } } diff --git a/g10/key-clean.h b/g10/key-clean.h index 693843064..a0fb76950 100644 --- a/g10/key-clean.h +++ b/g10/key-clean.h @@ -23,6 +23,18 @@ #include "gpg.h" +/* No explict cleaning. */ +#define KEY_CLEAN_NONE 0 +/* Remove only invalid subkeys (ie. missing key-bindings) */ +#define KEY_CLEAN_INVALID 1 +/* Remove expired encryption keys */ +#define KEY_CLEAN_ENCR 2 +/* Remove expired authentication and encryption keys. */ +#define KEY_CLEAN_AUTHENCR 3 +/* Remove all expired subkeys. */ +#define KEY_CLEAN_ALL 4 + + void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, u32 *main_kid, struct key_item *klist, u32 curtime, u32 *next_expire); @@ -32,7 +44,8 @@ void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, int *uids_cleaned, int *sigs_cleaned); void clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, int *uids_cleaned,int *sigs_cleaned); -void clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, +void clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, + int noisy, int clean_level, int *subkeys_cleaned, int *sigs_cleaned);