aboutsummaryrefslogtreecommitdiffstats
path: root/g10
diff options
context:
space:
mode:
Diffstat (limited to 'g10')
-rw-r--r--g10/Makefile.am1
-rw-r--r--g10/card-util.c1
-rw-r--r--g10/export.c19
-rw-r--r--g10/getkey.c49
-rw-r--r--g10/gpg.c17
-rw-r--r--g10/gpgcompose.c2
-rw-r--r--g10/gpgv.c15
-rw-r--r--g10/import.c550
-rw-r--r--g10/key-check.c241
-rw-r--r--g10/key-clean.c614
-rw-r--r--g10/key-clean.h52
-rw-r--r--g10/keydb.h38
-rw-r--r--g10/keyedit.c1
-rw-r--r--g10/keygen.c2
-rw-r--r--g10/keylist.c72
-rw-r--r--g10/main.h3
-rw-r--r--g10/mainproc.c27
-rw-r--r--g10/packet.h2
-rw-r--r--g10/pkclist.c4
-rw-r--r--g10/sig-check.c17
-rw-r--r--g10/sign.c2
-rw-r--r--g10/test-stubs.c14
-rw-r--r--g10/trust.c388
-rw-r--r--g10/trustdb.c1
-rw-r--r--g10/trustdb.h41
25 files changed, 1498 insertions, 675 deletions
diff --git a/g10/Makefile.am b/g10/Makefile.am
index b8b92d702..3b4464364 100644
--- a/g10/Makefile.am
+++ b/g10/Makefile.am
@@ -152,6 +152,7 @@ gpg_sources = server.c \
trust.c $(trust_source) $(tofu_source) \
$(card_source) \
exec.c exec.h \
+ key-clean.c key-clean.h \
key-check.c key-check.h
gpg_SOURCES = gpg.c \
diff --git a/g10/card-util.c b/g10/card-util.c
index 587f181f2..b7eedc0c8 100644
--- a/g10/card-util.c
+++ b/g10/card-util.c
@@ -851,6 +851,7 @@ fetch_url (ctrl_t ctrl)
}
}
+ agent_release_card_info (&info);
return rc;
}
diff --git a/g10/export.c b/g10/export.c
index c538dc1f1..e94e959fb 100644
--- a/g10/export.c
+++ b/g10/export.c
@@ -41,6 +41,8 @@
#include "../common/init.h"
#include "trustdb.h"
#include "call-agent.h"
+#include "key-clean.h"
+
/* An object to keep track of subkeys. */
struct subkey_list_s
@@ -2001,12 +2003,19 @@ 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))
- clean_key (ctrl, keyblock, opt.verbose,
- (options&EXPORT_MINIMAL), NULL, NULL);
+ {
+ 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,
+ (options&EXPORT_MINIMAL)? KEY_CLEAN_ALL
+ /**/ : KEY_CLEAN_AUTHENCR,
+ NULL, NULL);
+ commit_kbnode (&keyblock);
+ }
if (export_keep_uid)
{
diff --git a/g10/getkey.c b/g10/getkey.c
index 55c862877..41afeb9e8 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -677,6 +677,24 @@ pk_from_block (PKT_public_key *pk, kbnode_t keyblock, kbnode_t found_key)
}
+/* Specialized version of get_pubkey which retrieves the key based on
+ * information in SIG. In contrast to get_pubkey PK is required. */
+gpg_error_t
+get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig)
+{
+ const byte *fpr;
+ size_t fprlen;
+
+ /* First try the new ISSUER_FPR info. */
+ fpr = issuer_fpr_raw (sig, &fprlen);
+ if (fpr && !get_pubkey_byfprint (ctrl, pk, NULL, fpr, fprlen))
+ return 0;
+
+ /* Fallback to use the ISSUER_KEYID. */
+ return get_pubkey (ctrl, pk, sig->keyid);
+}
+
+
/* Return the public key with the key id KEYID and store it at PK.
* The resources in *PK should be released using
* release_public_key_parts(). This function also stores a copy of
@@ -739,8 +757,9 @@ get_pubkey (ctrl_t ctrl, PKT_public_key * pk, u32 * keyid)
/* Do a lookup. */
{
struct getkey_ctx_s ctx;
- KBNODE kb = NULL;
- KBNODE found_key = NULL;
+ kbnode_t kb = NULL;
+ kbnode_t found_key = NULL;
+
memset (&ctx, 0, sizeof ctx);
ctx.exact = 1; /* Use the key ID exactly as given. */
ctx.not_allocated = 1;
@@ -863,6 +882,28 @@ get_pubkey_fast (PKT_public_key * pk, u32 * keyid)
}
+/* Return the entire keyblock used to create SIG. This is a
+ * specialized version of get_pubkeyblock.
+ *
+ * FIXME: This is a hack because get_pubkey_for_sig was already called
+ * and it could have used a cache to hold the key. */
+kbnode_t
+get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig)
+{
+ const byte *fpr;
+ size_t fprlen;
+ kbnode_t keyblock;
+
+ /* First try the new ISSUER_FPR info. */
+ fpr = issuer_fpr_raw (sig, &fprlen);
+ if (fpr && !get_pubkey_byfprint (ctrl, NULL, &keyblock, fpr, fprlen))
+ return keyblock;
+
+ /* Fallback to use the ISSUER_KEYID. */
+ return get_pubkeyblock (ctrl, sig->keyid);
+}
+
+
/* Return the key block for the key with key id KEYID or NULL, if an
* error occurs. Use release_kbnode() to release the key block.
*
@@ -1802,6 +1843,8 @@ get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock,
memset (&ctx, 0, sizeof ctx);
ctx.exact = 1;
ctx.not_allocated = 1;
+ /* FIXME: We should get the handle from the cache like we do in
+ * get_pubkey. */
ctx.kr_handle = keydb_new ();
if (!ctx.kr_handle)
return gpg_error_from_syserror ();
@@ -3142,7 +3185,7 @@ buf_to_sig (const byte * buf, size_t len)
if (parse_signature (iobuf, PKT_SIGNATURE, len, sig) != 0)
{
- xfree (sig);
+ free_seckey_enc (sig);
sig = NULL;
}
diff --git a/g10/gpg.c b/g10/gpg.c
index 70bdddfda..600f8440d 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -150,6 +150,7 @@ enum cmd_and_opt_values
aSearchKeys,
aRefreshKeys,
aFetchKeys,
+ aShowKeys,
aExport,
aExportSecret,
aExportSecretSub,
@@ -500,6 +501,7 @@ static ARGPARSE_OPTS opts[] = {
N_("update all keys from a keyserver")),
ARGPARSE_c (aLocateKeys, "locate-keys", "@"),
ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ),
+ ARGPARSE_c (aShowKeys, "show-keys" , "@" ),
ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ),
ARGPARSE_c (aExportSecretSub, "export-secret-subkeys" , "@" ),
ARGPARSE_c (aExportSshKey, "export-ssh-key", "@" ),
@@ -740,6 +742,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aListKeys, "list-key", "@"), /* alias */
ARGPARSE_c (aListSigs, "list-sig", "@"), /* alias */
ARGPARSE_c (aCheckKeys, "check-sig", "@"), /* alias */
+ ARGPARSE_c (aShowKeys, "show-key", "@"), /* alias */
ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"),
ARGPARSE_s_n (oSkipHiddenRecipients, "skip-hidden-recipients", "@"),
ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"),
@@ -2642,6 +2645,17 @@ main (int argc, char **argv)
greeting=1;
break;
+ case aShowKeys:
+ set_cmd (&cmd, pargs.r_opt);
+ opt.import_options |= IMPORT_SHOW;
+ opt.import_options |= IMPORT_DRY_RUN;
+ opt.import_options &= ~IMPORT_REPAIR_KEYS;
+ opt.list_options |= LIST_SHOW_UNUSABLE_UIDS;
+ opt.list_options |= LIST_SHOW_UNUSABLE_SUBKEYS;
+ opt.list_options |= LIST_SHOW_NOTATIONS;
+ opt.list_options |= LIST_SHOW_POLICY_URLS;
+ break;
+
case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break;
case aDecryptFiles: multifile=1; /* fall through */
@@ -3611,7 +3625,7 @@ main (int argc, char **argv)
else
{
pargs.err = ARGPARSE_PRINT_ERROR;
- /* The argparse fucntion calls a plain exit and thus
+ /* The argparse function calls a plain exit and thus
* we need to print a status here. */
write_status_failure ("option-parser",
gpg_error(GPG_ERR_GENERAL));
@@ -4638,6 +4652,7 @@ main (int argc, char **argv)
case aFastImport:
opt.import_options |= IMPORT_FAST; /* fall through */
case aImport:
+ case aShowKeys:
import_keys (ctrl, argc? argv:NULL, argc, NULL,
opt.import_options, opt.key_origin, opt.key_origin_url);
break;
diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c
index 094bc7614..b3f7ecdce 100644
--- a/g10/gpgcompose.c
+++ b/g10/gpgcompose.c
@@ -1835,7 +1835,7 @@ signature (const char *option, int argc, char *argv[], void *cookie)
debug ("Wrote signature packet:\n");
dump_component (&pkt);
- xfree (sig);
+ free_seckey_enc (sig);
release_kbnode (si.issuer_kb);
xfree (si.revocation_key);
diff --git a/g10/gpgv.c b/g10/gpgv.c
index 16d90440c..b33590655 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -772,3 +772,18 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
return 0;
}
+
+
+int
+get_revocation_reason (PKT_signature *sig, char **r_reason,
+ char **r_comment, size_t *r_commentlen)
+{
+ (void)sig;
+ (void)r_commentlen;
+
+ if (r_reason)
+ *r_reason = NULL;
+ if (r_comment)
+ *r_comment = NULL;
+ return 0;
+}
diff --git a/g10/import.c b/g10/import.c
index 9fc769df9..1eb3ecceb 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -41,6 +41,7 @@
#include "../common/init.h"
#include "../common/mbox-util.h"
#include "key-check.h"
+#include "key-clean.h"
struct import_stats_s
@@ -113,8 +114,8 @@ static int import_secret_one (ctrl_t ctrl, kbnode_t keyblock,
struct import_stats_s *stats, int batch,
unsigned int options, int for_migration,
import_screener_t screener, void *screener_arg);
-static int import_revoke_cert (ctrl_t ctrl,
- kbnode_t node, struct import_stats_s *stats);
+static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options,
+ struct import_stats_s *stats);
static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
int *non_self);
static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock,
@@ -494,7 +495,9 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames,
if (!stats_handle)
{
- import_print_stats (stats);
+ if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN))
+ != (IMPORT_SHOW | IMPORT_DRY_RUN))
+ import_print_stats (stats);
import_release_stats_handle (stats);
}
@@ -588,7 +591,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats,
screener, screener_arg);
else if (keyblock->pkt->pkttype == PKT_SIGNATURE
&& IS_KEY_REV (keyblock->pkt->pkt.signature) )
- rc = import_revoke_cert (ctrl, keyblock, stats);
+ rc = import_revoke_cert (ctrl, keyblock, options, stats);
else
{
log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype);
@@ -778,7 +781,7 @@ read_block( IOBUF a, int with_meta,
struct parse_packet_ctx_s parsectx;
PACKET *pkt;
kbnode_t root = NULL;
- int in_cert, in_v3key;
+ int in_cert, in_v3key, skip_sigs;
*r_v3keys = 0;
@@ -797,6 +800,7 @@ read_block( IOBUF a, int with_meta,
if (!with_meta)
parsectx.skip_meta = 1;
in_v3key = 0;
+ skip_sigs = 0;
while ((rc=parse_packet (&parsectx, pkt)) != -1)
{
if (rc && (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY
@@ -811,8 +815,25 @@ read_block( IOBUF a, int with_meta,
}
else if (rc ) /* (ignore errors) */
{
+ skip_sigs = 0;
if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET)
; /* Do not show a diagnostic. */
+ else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET
+ && (pkt->pkttype == PKT_USER_ID
+ || pkt->pkttype == PKT_ATTRIBUTE))
+ {
+ /* This indicates a too large user id or attribute
+ * packet. We skip this packet and all following
+ * signatures. Sure, this won't allow to repair a
+ * garbled keyring in case one of the signatures belong
+ * to another user id. However, this better mitigates
+ * DoS using inserted user ids. */
+ skip_sigs = 1;
+ }
+ else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET
+ && (pkt->pkttype == PKT_OLD_COMMENT
+ || pkt->pkttype == PKT_COMMENT))
+ ; /* Ignore too large comment packets. */
else
{
log_error("read_block: read error: %s\n", gpg_strerror (rc) );
@@ -824,76 +845,88 @@ read_block( IOBUF a, int with_meta,
continue;
}
- if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY
- || pkt->pkttype == PKT_SECRET_KEY))
- {
- free_packet (pkt, &parsectx);
- init_packet(pkt);
- continue;
- }
- in_v3key = 0;
+ if (skip_sigs)
+ {
+ if (pkt->pkttype == PKT_SIGNATURE)
+ {
+ free_packet (pkt, &parsectx);
+ init_packet (pkt);
+ continue;
+ }
+ skip_sigs = 0;
+ }
- if (!root && pkt->pkttype == PKT_SIGNATURE
- && IS_KEY_REV (pkt->pkt.signature) )
- {
- /* This is a revocation certificate which is handled in a
- * special way. */
- root = new_kbnode( pkt );
- pkt = NULL;
- goto ready;
- }
+ if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY
+ || pkt->pkttype == PKT_SECRET_KEY))
+ {
+ free_packet (pkt, &parsectx);
+ init_packet(pkt);
+ continue;
+ }
+ in_v3key = 0;
- /* Make a linked list of all packets. */
- switch (pkt->pkttype)
- {
- case PKT_COMPRESSED:
- if (check_compress_algo (pkt->pkt.compressed->algorithm))
- {
- rc = GPG_ERR_COMPR_ALGO;
- goto ready;
- }
- else
- {
- compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx );
- pkt->pkt.compressed->buf = NULL;
- if (push_compress_filter2 (a, cfx,
- pkt->pkt.compressed->algorithm, 1))
- xfree (cfx); /* e.g. in case of compression_algo NONE. */
- }
- free_packet (pkt, &parsectx);
- init_packet(pkt);
- break;
+ if (!root && pkt->pkttype == PKT_SIGNATURE
+ && IS_KEY_REV (pkt->pkt.signature) )
+ {
+ /* This is a revocation certificate which is handled in a
+ * special way. */
+ root = new_kbnode( pkt );
+ pkt = NULL;
+ goto ready;
+ }
- case PKT_RING_TRUST:
- /* Skip those packets unless we are in restore mode. */
- if ((opt.import_options & IMPORT_RESTORE))
- goto x_default;
- free_packet (pkt, &parsectx);
- init_packet(pkt);
- break;
+ /* Make a linked list of all packets. */
+ switch (pkt->pkttype)
+ {
+ case PKT_COMPRESSED:
+ if (check_compress_algo (pkt->pkt.compressed->algorithm))
+ {
+ rc = GPG_ERR_COMPR_ALGO;
+ goto ready;
+ }
+ else
+ {
+ compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx );
+ pkt->pkt.compressed->buf = NULL;
+ if (push_compress_filter2 (a, cfx,
+ pkt->pkt.compressed->algorithm, 1))
+ xfree (cfx); /* e.g. in case of compression_algo NONE. */
+ }
+ free_packet (pkt, &parsectx);
+ init_packet(pkt);
+ break;
- case PKT_PUBLIC_KEY:
- case PKT_SECRET_KEY:
- if (in_cert ) /* Store this packet. */
- {
- *pending_pkt = pkt;
- pkt = NULL;
- goto ready;
- }
- in_cert = 1; /* fall through */
- default:
- x_default:
- if (in_cert && valid_keyblock_packet (pkt->pkttype))
- {
- if (!root )
- root = new_kbnode (pkt);
- else
- add_kbnode (root, new_kbnode (pkt));
- pkt = xmalloc (sizeof *pkt);
- }
- init_packet(pkt);
- break;
- }
+ case PKT_RING_TRUST:
+ /* Skip those packets unless we are in restore mode. */
+ if ((opt.import_options & IMPORT_RESTORE))
+ goto x_default;
+ free_packet (pkt, &parsectx);
+ init_packet(pkt);
+ break;
+
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ if (in_cert ) /* Store this packet. */
+ {
+ *pending_pkt = pkt;
+ pkt = NULL;
+ goto ready;
+ }
+ in_cert = 1;
+ /* fall through */
+ default:
+ x_default:
+ if (in_cert && valid_keyblock_packet (pkt->pkttype))
+ {
+ if (!root )
+ root = new_kbnode (pkt);
+ else
+ add_kbnode (root, new_kbnode (pkt));
+ pkt = xmalloc (sizeof *pkt);
+ }
+ init_packet(pkt);
+ break;
+ }
}
ready:
@@ -1312,6 +1345,16 @@ impex_filter_getval (void *cookie, const char *propname)
{
result = pk_is_disabled (pk)? "1":"0";
}
+ else if (!strcmp (propname, "usage"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%s%s%s%s%s",
+ (pk->pubkey_usage & PUBKEY_USAGE_ENC)?"e":"",
+ (pk->pubkey_usage & PUBKEY_USAGE_SIG)?"s":"",
+ (pk->pubkey_usage & PUBKEY_USAGE_CERT)?"c":"",
+ (pk->pubkey_usage & PUBKEY_USAGE_AUTH)?"a":"",
+ (pk->pubkey_usage & PUBKEY_USAGE_UNKNOWN)?"?":"");
+ result = numbuf;
+ }
else
result = NULL;
}
@@ -1653,6 +1696,10 @@ import_one (ctrl_t ctrl,
int any_filter = 0;
KEYDB_HANDLE hd = NULL;
+ /* If show-only is active we don't won't any extra output. */
+ if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN)))
+ silent = 1;
+
/* Get the key and print some info about it. */
node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
if (!node )
@@ -1712,9 +1759,14 @@ import_one (ctrl_t ctrl,
that we have to clean later. This has no practical impact on the
end result, but does result in less logging which might confuse
the user. */
- if (options&IMPORT_CLEAN)
- clean_key (ctrl, keyblock,
- opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL);
+ if ((options & IMPORT_CLEAN))
+ {
+ 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, KEY_CLEAN_NONE,
+ NULL, NULL);
+ }
clear_kbnode_flags( keyblock );
@@ -1855,8 +1907,13 @@ import_one (ctrl_t ctrl,
log_info (_("writing to '%s'\n"), keydb_get_resource_name (hd) );
if ((options & IMPORT_CLEAN))
- clean_key (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL),
- &n_uids_cleaned,&n_sigs_cleaned);
+ {
+ 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, KEY_CLEAN_NONE,
+ NULL, NULL);
+ }
/* Unless we are in restore mode apply meta data to the
* keyblock. Note that this will never change the first packet
@@ -1941,8 +1998,14 @@ import_one (ctrl_t ctrl,
goto leave;
if ((options & IMPORT_CLEAN))
- clean_key (ctrl, keyblock_orig, opt.verbose, (options&IMPORT_MINIMAL),
- &n_uids_cleaned,&n_sigs_cleaned);
+ {
+ merge_keys_and_selfsig (ctrl, keyblock_orig);
+ 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, KEY_CLEAN_NONE,
+ NULL, NULL);
+ }
if (n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned)
{
@@ -2616,11 +2679,216 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock,
}
+
+/* Return the recocation reason from signature SIG. If no revocation
+ * reason is availabale 0 is returned, in other cases the reason
+ * (0..255). If R_REASON is not NULL a malloced textual
+ * representation of the code is stored there. If R_COMMENT is not
+ * NULL the comment from the reason is stored there and its length at
+ * R_COMMENTLEN. Note that the value at R_COMMENT is not filtered but
+ * user supplied data in UTF8; thus it needs to be escaped for display
+ * purposes. Both return values are either NULL or a malloced
+ * string/buffer. */
+int
+get_revocation_reason (PKT_signature *sig, char **r_reason,
+ char **r_comment, size_t *r_commentlen)
+{
+ int reason_seq = 0;
+ size_t reason_n;
+ const byte *reason_p;
+ char reason_code_buf[20];
+ const char *reason_text = NULL;
+ int reason_code = 0;
+
+ if (r_reason)
+ *r_reason = NULL;
+ if (r_comment)
+ *r_comment = NULL;
+
+ /* Skip over empty reason packets. */
+ while ((reason_p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REVOC_REASON,
+ &reason_n, &reason_seq, NULL))
+ && !reason_n)
+ ;
+ if (reason_p)
+ {
+ reason_code = *reason_p;
+ reason_n--; reason_p++;
+ switch (reason_code)
+ {
+ case 0x00: reason_text = _("No reason specified"); break;
+ case 0x01: reason_text = _("Key is superseded"); break;
+ case 0x02: reason_text = _("Key has been compromised"); break;
+ case 0x03: reason_text = _("Key is no longer used"); break;
+ case 0x20: reason_text = _("User ID is no longer valid"); break;
+ default:
+ snprintf (reason_code_buf, sizeof reason_code_buf,
+ "code=%02x", reason_code);
+ reason_text = reason_code_buf;
+ break;
+ }
+
+ if (r_reason)
+ *r_reason = xstrdup (reason_text);
+
+ if (r_comment && reason_n)
+ {
+ *r_comment = xmalloc (reason_n);
+ memcpy (*r_comment, reason_p, reason_n);
+ *r_commentlen = reason_n;
+ }
+ }
+
+ return reason_code;
+}
+
+
+/* List the recocation signature as a "rvs" record. SIGRC shows the
+ * character from the signature verification or 0 if no public key was
+ * found. */
+static void
+list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc)
+{
+ char *siguid = NULL;
+ size_t siguidlen = 0;
+ char *issuer_fpr = NULL;
+ int reason_code = 0;
+ char *reason_text = NULL;
+ char *reason_comment = NULL;
+ size_t reason_commentlen;
+
+ if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
+ {
+ int nouid;
+ siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid);
+ if (nouid)
+ sigrc = '?';
+ }
+
+ reason_code = get_revocation_reason (sig, &reason_text,
+ &reason_comment, &reason_commentlen);
+
+ if (opt.with_colons)
+ {
+ es_fputs ("rvs:", es_stdout);
+ if (sigrc)
+ es_putc (sigrc, es_stdout);
+ es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:::",
+ sig->pubkey_algo,
+ (ulong) sig->keyid[0], (ulong) sig->keyid[1],
+ colon_datestr_from_sig (sig),
+ colon_expirestr_from_sig (sig));
+
+ if (siguid)
+ es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL);
+
+ es_fprintf (es_stdout, ":%02x%c", sig->sig_class,
+ sig->flags.exportable ? 'x' : 'l');
+ if (reason_text)
+ es_fprintf (es_stdout, ",%02x", reason_code);
+ es_fputs ("::", es_stdout);
+
+ if ((issuer_fpr = issuer_fpr_string (sig)))
+ es_fputs (issuer_fpr, es_stdout);
+
+ es_fprintf (es_stdout, ":::%d:", sig->digest_algo);
+
+ if (reason_comment)
+ {
+ es_fputs ("::::", es_stdout);
+ es_write_sanitized (es_stdout, reason_comment, reason_commentlen,
+ ":", NULL);
+ es_putc (':', es_stdout);
+ }
+ es_putc ('\n', es_stdout);
+
+ if (opt.show_subpackets)
+ print_subpackets_colon (sig);
+ }
+ else /* Human readable. */
+ {
+ es_fputs ("rvs", es_stdout);
+ es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s",
+ sigrc, (sig->sig_class - 0x10 > 0 &&
+ sig->sig_class - 0x10 <
+ 4) ? '0' + sig->sig_class - 0x10 : ' ',
+ sig->flags.exportable ? ' ' : 'L',
+ sig->flags.revocable ? ' ' : 'R',
+ sig->flags.policy_url ? 'P' : ' ',
+ sig->flags.notation ? 'N' : ' ',
+ sig->flags.expired ? 'X' : ' ',
+ (sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
+ 0) ? '0' +
+ sig->trust_depth : ' ', keystr (sig->keyid),
+ datestr_from_sig (sig));
+ if (siguid)
+ {
+ es_fprintf (es_stdout, " ");
+ print_utf8_buffer (es_stdout, siguid, siguidlen);
+ }
+ es_putc ('\n', es_stdout);
+
+ if (sig->flags.policy_url
+ && (opt.list_options & LIST_SHOW_POLICY_URLS))
+ show_policy_url (sig, 3, 0);
+
+ if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS))
+ show_notation (sig, 3, 0,
+ ((opt.list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0)
+ +
+ ((opt.list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0));
+
+ if (sig->flags.pref_ks
+ && (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
+ show_keyserver_url (sig, 3, 0);
+
+ if (reason_text)
+ {
+ es_fprintf (es_stdout, " %s%s\n",
+ _("reason for revocation: "), reason_text);
+ if (reason_comment)
+ {
+ const byte *s, *s_lf;
+ size_t n, n_lf;
+
+ s = reason_comment;
+ n = reason_commentlen;
+ s_lf = NULL;
+ do
+ {
+ /* We don't want any empty lines, so we skip them. */
+ for (;n && *s == '\n'; s++, n--)
+ ;
+ if (n)
+ {
+ s_lf = memchr (s, '\n', n);
+ n_lf = s_lf? s_lf - s : n;
+ es_fprintf (es_stdout, " %s",
+ _("revocation comment: "));
+ es_write_sanitized (es_stdout, s, n_lf, NULL, NULL);
+ es_putc ('\n', es_stdout);
+ s += n_lf; n -= n_lf;
+ }
+ } while (s_lf);
+ }
+ }
+ }
+
+ es_fflush (es_stdout);
+
+ xfree (reason_text);
+ xfree (reason_comment);
+ xfree (siguid);
+ xfree (issuer_fpr);
+}
+
+
/****************
* Import a revocation certificate; this is a single signature packet.
*/
static int
-import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
+import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options,
+ struct import_stats_s *stats)
{
PKT_public_key *pk = NULL;
kbnode_t onode;
@@ -2628,6 +2896,11 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
KEYDB_HANDLE hd = NULL;
u32 keyid[2];
int rc = 0;
+ int sigrc = 0;
+ int silent;
+
+ /* No error output for --show-keys. */
+ silent = (options & (IMPORT_SHOW | IMPORT_DRY_RUN));
log_assert (!node->next );
log_assert (node->pkt->pkttype == PKT_SIGNATURE );
@@ -2640,15 +2913,16 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
rc = get_pubkey (ctrl, pk, keyid );
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY )
{
- log_error(_("key %s: no public key -"
- " can't apply revocation certificate\n"), keystr(keyid));
+ if (!silent)
+ log_error (_("key %s: no public key -"
+ " can't apply revocation certificate\n"), keystr(keyid));
rc = 0;
goto leave;
}
else if (rc )
{
- log_error(_("key %s: public key not found: %s\n"),
- keystr(keyid), gpg_strerror (rc));
+ log_error (_("key %s: public key not found: %s\n"),
+ keystr(keyid), gpg_strerror (rc));
goto leave;
}
@@ -2685,12 +2959,21 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
/* it is okay, that node is not in keyblock because
* check_key_signature works fine for sig_class 0x20 (KEY_REV) in
- * this special case. */
+ * this special case. SIGRC is only used for IMPORT_SHOW. */
rc = check_key_signature (ctrl, keyblock, node, NULL);
+ switch (gpg_err_code (rc))
+ {
+ case 0: sigrc = '!'; break;
+ case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break;
+ case GPG_ERR_NO_PUBKEY: sigrc = '?'; break;
+ case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break;
+ default: sigrc = '%'; break;
+ }
if (rc )
{
- log_error( _("key %s: invalid revocation certificate"
- ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc));
+ if (!silent)
+ log_error (_("key %s: invalid revocation certificate"
+ ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc));
goto leave;
}
@@ -2710,33 +2993,39 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
/* insert it */
insert_kbnode( keyblock, clone_kbnode(node), 0 );
- /* and write the keyblock back */
- rc = keydb_update_keyblock (ctrl, hd, keyblock );
- if (rc)
- log_error (_("error writing keyring '%s': %s\n"),
- keydb_get_resource_name (hd), gpg_strerror (rc) );
- keydb_release (hd);
- hd = NULL;
-
- /* we are ready */
- if (!opt.quiet )
+ /* and write the keyblock back unless in dry run mode. */
+ if (!(opt.dry_run || (options & IMPORT_DRY_RUN)))
{
- char *p=get_user_id_native (ctrl, keyid);
- log_info( _("key %s: \"%s\" revocation certificate imported\n"),
- keystr(keyid),p);
- xfree(p);
- }
- stats->n_revoc++;
+ rc = keydb_update_keyblock (ctrl, hd, keyblock );
+ if (rc)
+ log_error (_("error writing keyring '%s': %s\n"),
+ keydb_get_resource_name (hd), gpg_strerror (rc) );
+ keydb_release (hd);
+ hd = NULL;
- /* If the key we just revoked was ultimately trusted, remove its
- ultimate trust. This doesn't stop the user from putting the
- ultimate trust back, but is a reasonable solution for now. */
- if (get_ownertrust (ctrl, pk) == TRUST_ULTIMATE)
- clear_ownertrusts (ctrl, pk);
+ /* we are ready */
+ if (!opt.quiet )
+ {
+ char *p=get_user_id_native (ctrl, keyid);
+ log_info( _("key %s: \"%s\" revocation certificate imported\n"),
+ keystr(keyid),p);
+ xfree(p);
+ }
- revalidation_mark (ctrl);
+ /* If the key we just revoked was ultimately trusted, remove its
+ * ultimate trust. This doesn't stop the user from putting the
+ * ultimate trust back, but is a reasonable solution for now. */
+ if (get_ownertrust (ctrl, pk) == TRUST_ULTIMATE)
+ clear_ownertrusts (ctrl, pk);
+
+ revalidation_mark (ctrl);
+ }
+ stats->n_revoc++;
leave:
+ if ((options & IMPORT_SHOW))
+ list_standalone_revocation (ctrl, node->pkt->pkt.signature, sigrc);
+
keydb_release (hd);
release_kbnode( keyblock );
free_public_key( pk );
@@ -2744,8 +3033,9 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
}
-/* Loop over the keyblock and check all self signatures. On return
- * the following bis in the node flags are set:
+/* Loop over the KEYBLOCK and check all self signatures. KEYID is the
+ * keyid of the primary key for reporting purposes. On return the
+ * following bits in the node flags are set:
*
* - NODE_GOOD_SELFSIG :: User ID or subkey has a self-signature
* - NODE_BAD_SELFSIG :: Used ID or subkey has an invalid self-signature
@@ -2760,17 +3050,22 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
static int
chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self)
{
- kbnode_t n, knode = NULL;
+ kbnode_t knode = NULL; /* The node of the current subkey. */
+ PKT_public_key *subpk = NULL; /* and its packet. */
+ kbnode_t bsnode = NULL; /* Subkey binding signature node. */
+ u32 bsdate = 0; /* Timestamp of that node. */
+ kbnode_t rsnode = NULL; /* Subkey recocation signature node. */
+ u32 rsdate = 0; /* Timestamp of tha node. */
PKT_signature *sig;
int rc;
- u32 bsdate=0, rsdate=0;
- kbnode_t bsnode = NULL, rsnode = NULL;
+ kbnode_t n;
for (n=keyblock; (n = find_next_kbnode (n, 0)); )
{
if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
knode = n;
+ subpk = knode->pkt->pkt.public_key;
bsdate = 0;
rsdate = 0;
bsnode = NULL;
@@ -2859,11 +3154,14 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self)
if ( rc )
{
if (opt.verbose)
- log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
- _("key %s: unsupported public key"
- " algorithm\n"):
- _("key %s: invalid subkey binding\n"),
- keystr (keyid));
+ {
+ keyid_from_pk (subpk, NULL);
+ log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
+ _("key %s: unsupported public key"
+ " algorithm\n"):
+ _("key %s: invalid subkey binding\n"),
+ keystr_with_sub (keyid, subpk->keyid));
+ }
n->flag |= NODE_DELETION_MARK;
}
else
@@ -2878,8 +3176,12 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self)
one is newer */
bsnode->flag |= NODE_DELETION_MARK;
if (opt.verbose)
- log_info (_("key %s: removed multiple subkey"
- " binding\n"),keystr(keyid));
+ {
+ keyid_from_pk (subpk, NULL);
+ log_info (_("key %s: removed multiple subkey"
+ " binding\n"),
+ keystr_with_sub (keyid, subpk->keyid));
+ }
}
bsnode = n;
@@ -2958,6 +3260,7 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
{
kbnode_t node;
int nvalid=0, uid_seen=0, subkey_seen=0;
+ PKT_public_key *pk;
for (node=keyblock->next; node; node = node->next )
{
@@ -2995,7 +3298,12 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
|| !(node->flag & NODE_GOOD_SELFSIG))
{
if (opt.verbose )
- log_info( _("key %s: skipped subkey\n"),keystr(keyid));
+ {
+ pk = node->pkt->pkt.public_key;
+ keyid_from_pk (pk, NULL);
+ log_info (_("key %s: skipped subkey\n"),
+ keystr_with_sub (keyid, pk->keyid));
+ }
delete_kbnode( node ); /* the subkey */
/* and all following signature packets */
diff --git a/g10/key-check.c b/g10/key-check.c
index 86b1e769d..c17b12c94 100644
--- a/g10/key-check.c
+++ b/g10/key-check.c
@@ -1,7 +1,7 @@
/* key-check.c - Detect and fix various problems with keys
* Copyright (C) 1998-2010 Free Software Foundation, Inc.
* Copyright (C) 1998-2017 Werner Koch
- * Copyright (C) 2015-2017 g10 Code GmbH
+ * Copyright (C) 2015-2018 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -72,6 +72,13 @@ sig_comparison (const void *av, const void *bv)
a = an->pkt->pkt.signature;
b = bn->pkt->pkt.signature;
+ /* Signatures with a different help counter are not identical for
+ * our purpose. */
+ if (a->help_counter < b->help_counter)
+ return -1;
+ if (a->help_counter > b->help_counter)
+ return 1;
+
if (a->digest_algo < b->digest_algo)
return -1;
if (a->digest_algo > b->digest_algo)
@@ -94,6 +101,125 @@ sig_comparison (const void *av, const void *bv)
}
+static gpg_error_t
+remove_duplicate_sigs (kbnode_t kb, int *dups, int *modified)
+{
+ gpg_error_t err;
+ kbnode_t n;
+ int nsigs;
+ kbnode_t *sigs; /* Allocated array with the signature packet. */
+ int i;
+ int last_i;
+ int block;
+ PKT_signature *sig;
+
+ /* Count the sigs. */
+ for (nsigs = 0, n = kb; n; n = n->next)
+ {
+ if (is_deleted_kbnode (n))
+ continue;
+ else if (n->pkt->pkttype == PKT_SIGNATURE)
+ nsigs ++;
+ }
+
+ if (!nsigs)
+ return 0; /* No signatures at all. */
+
+ /* Add them all to the SIGS array. */
+ sigs = xtrycalloc (nsigs, sizeof *sigs);
+ if (!sigs)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error allocating memory: %s\n"), gpg_strerror (err));
+ return err;
+ }
+
+ block = 0;
+ i = 0;
+ for (n = kb; n; n = n->next)
+ {
+ if (is_deleted_kbnode (n))
+ continue;
+
+ if (n->pkt->pkttype != PKT_SIGNATURE)
+ {
+ switch (n->pkt->pkttype)
+ {
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_SECRET_SUBKEY:
+ case PKT_USER_ID:
+ case PKT_ATTRIBUTE:
+ /* Bump the block number so that we only consider
+ * signatures below the same object as duplicates. */
+ block++;
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ sig = n->pkt->pkt.signature;
+ sig->help_counter = block;
+ sigs[i++] = n;
+ }
+ log_assert (i == nsigs);
+
+ qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison);
+
+ last_i = 0;
+ for (i = 1; i < nsigs; i ++)
+ {
+ log_assert (sigs[last_i]);
+ log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE);
+ log_assert (sigs[i]);
+ log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE);
+
+ if (sig_comparison (&sigs[last_i], &sigs[i]) == 0)
+ {
+ /* They are the same. Kill the latter. */
+ if (DBG_PACKET)
+ {
+ sig = sigs[i]->pkt->pkt.signature;
+
+ log_debug ("Signature appears multiple times, "
+ "deleting duplicate:\n");
+ log_debug (" sig: class 0x%x, issuer: %s,"
+ " timestamp: %s (%lld), digest: %02x %02x\n",
+ sig->sig_class, keystr (sig->keyid),
+ isotimestamp (sig->timestamp),
+ (long long) sig->timestamp,
+ sig->digest_start[0], sig->digest_start[1]);
+ }
+
+ /* Remove sigs[i] from the keyblock. */
+ {
+ kbnode_t z, *prevp;
+ int to_kill = last_i;
+ last_i = i;
+
+ for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next)
+ if (z == sigs[to_kill])
+ break;
+
+ *prevp = sigs[to_kill]->next;
+
+ sigs[to_kill]->next = NULL;
+ release_kbnode (sigs[to_kill]);
+ sigs[to_kill] = NULL;
+
+ ++*dups;
+ *modified = 1;
+ }
+ }
+ else
+ last_i = i;
+ }
+
+ xfree (sigs);
+ return 0;
+}
+
+
/* Perform a few sanity checks on a keyblock is okay and possibly
* repair some damage. Concretely:
*
@@ -133,108 +259,17 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb,
int bad_signature = 0;
int missing_selfsig = 0;
int modified = 0;
+ PKT_signature *sig;
log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY);
pk = kb->pkt->pkt.public_key;
/* First we look for duplicates. */
- {
- int nsigs;
- kbnode_t *sigs;
- int i;
- int last_i;
-
- /* Count the sigs. */
- for (nsigs = 0, n = kb; n; n = n->next)
- {
- if (is_deleted_kbnode (n))
- continue;
- else if (n->pkt->pkttype == PKT_SIGNATURE)
- nsigs ++;
- }
-
- if (!nsigs)
- return 0; /* No signatures at all. */
-
- /* Add them all to the SIGS array. */
- sigs = xtrycalloc (nsigs, sizeof *sigs);
- if (!sigs)
- {
- log_error (_("error allocating memory: %s\n"),
- gpg_strerror (gpg_error_from_syserror ()));
- return 0;
- }
-
- i = 0;
- for (n = kb; n; n = n->next)
- {
- if (is_deleted_kbnode (n))
- continue;
+ if (remove_duplicate_sigs (kb, &dups, &modified))
+ goto leave; /* Error */
- if (n->pkt->pkttype != PKT_SIGNATURE)
- continue;
-
- sigs[i] = n;
- i ++;
- }
- log_assert (i == nsigs);
-
- qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison);
-
- last_i = 0;
- for (i = 1; i < nsigs; i ++)
- {
- log_assert (sigs[last_i]);
- log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE);
- log_assert (sigs[i]);
- log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE);
-
- if (sig_comparison (&sigs[last_i], &sigs[i]) == 0)
- /* They are the same. Kill the latter. */
- {
- if (DBG_PACKET)
- {
- PKT_signature *sig = sigs[i]->pkt->pkt.signature;
-
- log_debug ("Signature appears multiple times, "
- "deleting duplicate:\n");
- log_debug (" sig: class 0x%x, issuer: %s,"
- " timestamp: %s (%lld), digest: %02x %02x\n",
- sig->sig_class, keystr (sig->keyid),
- isotimestamp (sig->timestamp),
- (long long) sig->timestamp,
- sig->digest_start[0], sig->digest_start[1]);
- }
-
- /* Remove sigs[i] from the keyblock. */
- {
- KBNODE z, *prevp;
- int to_kill = last_i;
- last_i = i;
-
- for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next)
- if (z == sigs[to_kill])
- break;
-
- *prevp = sigs[to_kill]->next;
-
- sigs[to_kill]->next = NULL;
- release_kbnode (sigs[to_kill]);
- sigs[to_kill] = NULL;
-
- dups ++;
- modified = 1;
- }
- }
- else
- last_i = i;
- }
-
- xfree (sigs);
- }
-
- /* Make sure the sigs occur after the component (public key, subkey,
- user id) that they sign. */
+ /* Now make sure the sigs occur after the component (aka block)
+ * (public key, subkey, user id) that they sign. */
issuer = NULL;
last_printed_component = NULL;
for (n_prevp = &kb, n = kb;
@@ -244,7 +279,6 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb,
{
PACKET *p;
int processed_current_component;
- PKT_signature *sig;
int rc;
int dump_sig_params = 0;
@@ -573,11 +607,18 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb,
free_public_key (issuer);
issuer = NULL;
+ /* If we reordered signatures we need to de-duplicate again because
+ * a signature can now be a duplicate in another block. */
+ if (reordered)
+ {
+ if (remove_duplicate_sigs (kb, &dups, &modified))
+ goto leave;
+ }
+
/* Identify keys / uids that don't have a self-sig. */
{
int has_selfsig = 0;
PACKET *p;
- PKT_signature *sig;
current_component = NULL;
for (n = kb; n; n = n->next)
@@ -643,6 +684,8 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb,
}
}
+
+ leave:
if (!opt.quiet)
{
char prefix[100];
diff --git a/g10/key-clean.c b/g10/key-clean.c
new file mode 100644
index 000000000..f66a0dbb4
--- /dev/null
+++ b/g10/key-clean.c
@@ -0,0 +1,614 @@
+/* key-clean.c - Functions to clean a keyblock
+ * Copyright (C) 1998-2008, 2010-2011 Free Software Foundation, Inc.
+ * Copyright (C) 2014, 2016-2018 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpg.h"
+#include "keydb.h"
+#include "../common/util.h"
+#include "../common/host2net.h"
+#include "../common/i18n.h"
+#include "options.h"
+#include "packet.h"
+#include "main.h"
+#include "key-clean.h"
+
+
+/*
+ * Mark the signature of the given UID which are used to certify it.
+ * To do this, we first revmove all signatures which are not valid and
+ * from the remain ones we look for the latest one. If this is not a
+ * certification revocation signature we mark the signature by setting
+ * node flag bit 8. Revocations are marked with flag 11, and sigs
+ * from unavailable keys are marked with flag 12. Note that flag bits
+ * 9 and 10 are used for internal purposes.
+ */
+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)
+{
+ kbnode_t node;
+ PKT_signature *sig;
+
+ /* First check all signatures. */
+ for (node=uidnode->next; node; node = node->next)
+ {
+ int rc;
+
+ node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12);
+ if (node->pkt->pkttype == PKT_USER_ID
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ break; /* ready */
+ if (node->pkt->pkttype != PKT_SIGNATURE)
+ continue;
+ sig = node->pkt->pkt.signature;
+ if (main_kid
+ && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1])
+ continue; /* ignore self-signatures if we pass in a main_kid */
+ if (!IS_UID_SIG(sig) && !IS_UID_REV(sig))
+ continue; /* we only look at these signature classes */
+ if(sig->sig_class>=0x11 && sig->sig_class<=0x13 &&
+ sig->sig_class-0x10<opt.min_cert_level)
+ continue; /* treat anything under our min_cert_level as an
+ invalid signature */
+ if (klist && !is_in_klist (klist, sig))
+ continue; /* no need to check it then */
+ if ((rc=check_key_signature (ctrl, keyblock, node, NULL)))
+ {
+ /* we ignore anything that won't verify, but tag the
+ no_pubkey case */
+ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
+ node->flag |= 1<<12;
+ continue;
+ }
+ node->flag |= 1<<9;
+ }
+ /* Reset the remaining flags. */
+ for (; node; node = node->next)
+ node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12);
+
+ /* kbnode flag usage: bit 9 is here set for signatures to consider,
+ * bit 10 will be set by the loop to keep track of keyIDs already
+ * processed, bit 8 will be set for the usable signatures, and bit
+ * 11 will be set for usable revocations. */
+
+ /* For each cert figure out the latest valid one. */
+ for (node=uidnode->next; node; node = node->next)
+ {
+ KBNODE n, signode;
+ u32 kid[2];
+ u32 sigdate;
+
+ if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ break;
+ if ( !(node->flag & (1<<9)) )
+ continue; /* not a node to look at */
+ if ( (node->flag & (1<<10)) )
+ continue; /* signature with a keyID already processed */
+ node->flag |= (1<<10); /* mark this node as processed */
+ sig = node->pkt->pkt.signature;
+ signode = node;
+ sigdate = sig->timestamp;
+ kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1];
+
+ /* Now find the latest and greatest signature */
+ for (n=uidnode->next; n; n = n->next)
+ {
+ if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || n->pkt->pkttype == PKT_SECRET_SUBKEY)
+ break;
+ if ( !(n->flag & (1<<9)) )
+ continue;
+ if ( (n->flag & (1<<10)) )
+ continue; /* shortcut already processed signatures */
+ sig = n->pkt->pkt.signature;
+ if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1])
+ continue;
+ n->flag |= (1<<10); /* mark this node as processed */
+
+ /* If signode is nonrevocable and unexpired and n isn't,
+ then take signode (skip). It doesn't matter which is
+ older: if signode was older then we don't want to take n
+ as signode is nonrevocable. If n was older then we're
+ automatically fine. */
+
+ if(((IS_UID_SIG(signode->pkt->pkt.signature) &&
+ !signode->pkt->pkt.signature->flags.revocable &&
+ (signode->pkt->pkt.signature->expiredate==0 ||
+ signode->pkt->pkt.signature->expiredate>curtime))) &&
+ (!(IS_UID_SIG(n->pkt->pkt.signature) &&
+ !n->pkt->pkt.signature->flags.revocable &&
+ (n->pkt->pkt.signature->expiredate==0 ||
+ n->pkt->pkt.signature->expiredate>curtime))))
+ continue;
+
+ /* If n is nonrevocable and unexpired and signode isn't,
+ then take n. Again, it doesn't matter which is older: if
+ n was older then we don't want to take signode as n is
+ nonrevocable. If signode was older then we're
+ automatically fine. */
+
+ if((!(IS_UID_SIG(signode->pkt->pkt.signature) &&
+ !signode->pkt->pkt.signature->flags.revocable &&
+ (signode->pkt->pkt.signature->expiredate==0 ||
+ signode->pkt->pkt.signature->expiredate>curtime))) &&
+ ((IS_UID_SIG(n->pkt->pkt.signature) &&
+ !n->pkt->pkt.signature->flags.revocable &&
+ (n->pkt->pkt.signature->expiredate==0 ||
+ n->pkt->pkt.signature->expiredate>curtime))))
+ {
+ signode = n;
+ sigdate = sig->timestamp;
+ continue;
+ }
+
+ /* At this point, if it's newer, it goes in as the only
+ remaining possibilities are signode and n are both either
+ revocable or expired or both nonrevocable and unexpired.
+ If the timestamps are equal take the later ordered
+ packet, presuming that the key packets are hopefully in
+ their original order. */
+
+ if (sig->timestamp >= sigdate)
+ {
+ signode = n;
+ sigdate = sig->timestamp;
+ }
+ }
+
+ sig = signode->pkt->pkt.signature;
+ if (IS_UID_SIG (sig))
+ { /* this seems to be a usable one which is not revoked.
+ * Just need to check whether there is an expiration time,
+ * We do the expired certification after finding a suitable
+ * certification, the assumption is that a signator does not
+ * want that after the expiration of his certificate the
+ * system falls back to an older certification which has a
+ * different expiration time */
+ const byte *p;
+ u32 expire;
+
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL );
+ expire = p? sig->timestamp + buf32_to_u32(p) : 0;
+
+ if (expire==0 || expire > curtime )
+ {
+ signode->flag |= (1<<8); /* yeah, found a good cert */
+ if (next_expire && expire && expire < *next_expire)
+ *next_expire = expire;
+ }
+ }
+ else
+ signode->flag |= (1<<11);
+ }
+}
+
+
+static int
+clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode,
+ int noisy, int self_only)
+{
+ int deleted = 0;
+ kbnode_t node;
+ u32 keyid[2];
+
+ log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
+ || keyblock->pkt->pkttype == PKT_SECRET_KEY);
+
+ keyid_from_pk (keyblock->pkt->pkt.public_key, keyid);
+
+ /* Passing in a 0 for current time here means that we'll never weed
+ out an expired sig. This is correct behavior since we want to
+ keep the most recent expired sig in a series. */
+ mark_usable_uid_certs (ctrl, keyblock, uidnode, NULL, NULL, 0, NULL);
+
+ /* What we want to do here is remove signatures that are not
+ considered as part of the trust calculations. Thus, all invalid
+ signatures are out, as are any signatures that aren't the last of
+ a series of uid sigs or revocations It breaks down like this:
+ coming out of mark_usable_uid_certs, if a sig is unflagged, it is
+ not even a candidate. If a sig has flag 9 or 10, that means it
+ was selected as a candidate and vetted. If a sig has flag 8 it
+ is a usable signature. If a sig has flag 11 it is a usable
+ revocation. If a sig has flag 12 it was issued by an unavailable
+ key. "Usable" here means the most recent valid
+ signature/revocation in a series from a particular signer.
+
+ Delete everything that isn't a usable uid sig (which might be
+ expired), a usable revocation, or a sig from an unavailable
+ key. */
+
+ for (node=uidnode->next;
+ node && node->pkt->pkttype==PKT_SIGNATURE;
+ node=node->next)
+ {
+ int keep;
+
+ keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0]
+ && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1;
+
+ /* Keep usable uid sigs ... */
+ if ((node->flag & (1<<8)) && keep)
+ continue;
+
+ /* ... and usable revocations... */
+ if ((node->flag & (1<<11)) && keep)
+ continue;
+
+ /* ... and sigs from unavailable keys. */
+ /* disabled for now since more people seem to want sigs from
+ unavailable keys removed altogether. */
+ /*
+ if(node->flag & (1<<12))
+ continue;
+ */
+
+ /* Everything else we delete */
+
+ /* At this point, if 12 is set, the signing key was unavailable.
+ If 9 or 10 is set, it's superseded. Otherwise, it's
+ invalid. */
+
+ if (noisy)
+ log_info ("removing signature from key %s on user ID \"%s\": %s\n",
+ keystr (node->pkt->pkt.signature->keyid),
+ uidnode->pkt->pkt.user_id->name,
+ node->flag&(1<<12)? "key unavailable":
+ node->flag&(1<<9)? "signature superseded"
+ /* */ :"invalid signature" );
+
+ delete_kbnode (node);
+ deleted++;
+ }
+
+ return deleted;
+}
+
+
+/* This is substantially easier than clean_sigs_from_uid since we just
+ have to establish if the uid has a valid self-sig, is not revoked,
+ and is not expired. Note that this does not take into account
+ whether the uid has a trust path to it - just whether the keyholder
+ themselves has certified the uid. Returns true if the uid was
+ compacted. To "compact" a user ID, we simply remove ALL signatures
+ except the self-sig that caused the user ID to be remove-worthy.
+ We don't actually remove the user ID packet itself since it might
+ be resurrected in a later merge. Note that this function requires
+ that the caller has already done a merge_keys_and_selfsig().
+
+ TODO: change the import code to allow importing a uid with only a
+ revocation if the uid already exists on the keyring. */
+
+static int
+clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy)
+{
+ kbnode_t node;
+ PKT_user_id *uid = uidnode->pkt->pkt.user_id;
+ int deleted = 0;
+
+ log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
+ || keyblock->pkt->pkttype == PKT_SECRET_KEY);
+ log_assert (uidnode->pkt->pkttype==PKT_USER_ID);
+
+ /* Skip valid user IDs, compacted user IDs, and non-self-signed user
+ IDs if --allow-non-selfsigned-uid is set. */
+ if (uid->created
+ || uid->flags.compacted
+ || (!uid->flags.expired && !uid->flags.revoked && opt.allow_non_selfsigned_uid))
+ return 0;
+
+ for (node=uidnode->next;
+ node && node->pkt->pkttype == PKT_SIGNATURE;
+ node=node->next)
+ {
+ if (!node->pkt->pkt.signature->flags.chosen_selfsig)
+ {
+ delete_kbnode (node);
+ deleted = 1;
+ uidnode->pkt->pkt.user_id->flags.compacted = 1;
+ }
+ }
+
+ if (noisy)
+ {
+ const char *reason;
+ char *user = utf8_to_native (uid->name, uid->len, 0);
+
+ if (uid->flags.revoked)
+ reason = _("revoked");
+ else if (uid->flags.expired)
+ reason = _("expired");
+ else
+ reason = _("invalid");
+
+ log_info ("compacting user ID \"%s\" on key %s: %s\n",
+ user, keystr_from_pk (keyblock->pkt->pkt.public_key),
+ reason);
+
+ xfree (user);
+ }
+
+ return deleted;
+}
+
+
+/* Needs to be called after a merge_keys_and_selfsig() */
+void
+clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode,
+ int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned)
+{
+ int dummy = 0;
+
+ log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
+ || keyblock->pkt->pkttype == PKT_SECRET_KEY);
+ log_assert (uidnode->pkt->pkttype==PKT_USER_ID);
+
+ if (!uids_cleaned)
+ uids_cleaned = &dummy;
+
+ if (!sigs_cleaned)
+ sigs_cleaned = &dummy;
+
+ /* Do clean_uid_from_key first since if it fires off, we don't have
+ to bother with the other. */
+ *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy);
+ if (!uidnode->pkt->pkt.user_id->flags.compacted)
+ *sigs_cleaned += clean_sigs_from_uid (ctrl, keyblock, uidnode,
+ noisy, self_only);
+}
+
+
+/* NB: This function marks the deleted nodes only and the caller is
+ * responsible to skip or remove them. Needs to be called after a
+ * merge_keys_and_selfsig(). */
+void
+clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only,
+ int *uids_cleaned, int *sigs_cleaned)
+{
+ kbnode_t node;
+
+ for (node = keyblock->next;
+ node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY);
+ node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_USER_ID)
+ clean_one_uid (ctrl, keyblock, node, noisy, self_only,
+ uids_cleaned, sigs_cleaned);
+ }
+
+ /* Remove bogus subkey binding signatures: The only signatures
+ * allowed are of class 0x18 and 0x28. */
+ log_assert (!node || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY));
+}
+
+
+/* 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;
+}
+
+
+/* Helper for clean_all_subkeys. Here duplicate signatures from a
+ * subkey are removed. This should in general not happen because
+ * import takes care of that. However, sometimes other tools are used
+ * to manage a keyring or key has been imported a long time ago. */
+static int
+clean_one_subkey_dupsigs (ctrl_t ctrl, kbnode_t subkeynode)
+{
+ kbnode_t node;
+ PKT_public_key *pk = subkeynode->pkt->pkt.public_key;
+ int any_choosen = 0;
+ int count = 0;
+
+ (void)ctrl;
+
+ log_assert (subkeynode->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || subkeynode->pkt->pkttype == PKT_SECRET_SUBKEY);
+
+ if (DBG_LOOKUP)
+ log_debug ("\tchecking subkey %08lX for dupsigs\n",
+ (ulong) keyid_from_pk (pk, NULL));
+
+ /* First check that the choosen flag has been set. Note that we
+ * only look at plain signatures so to keep all revocation
+ * signatures which may carry important information. */
+ for (node = subkeynode->next;
+ node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY);
+ node = node->next)
+ {
+ if (!is_deleted_kbnode (node)
+ && node->pkt->pkttype == PKT_SIGNATURE
+ && IS_SUBKEY_SIG (node->pkt->pkt.signature)
+ && node->pkt->pkt.signature->flags.chosen_selfsig)
+ {
+ any_choosen = 1;
+ break;
+ }
+ }
+
+ if (!any_choosen)
+ return 0; /* Ooops no choosen flag set - we can't decide. */
+
+ for (node = subkeynode->next;
+ node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY);
+ node = node->next)
+ {
+ if (!is_deleted_kbnode (node)
+ && node->pkt->pkttype == PKT_SIGNATURE
+ && IS_SUBKEY_SIG (node->pkt->pkt.signature)
+ && !node->pkt->pkt.signature->flags.chosen_selfsig)
+ {
+ delete_kbnode (node);
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+/* 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;
+ int n;
+
+ 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 = first_subkey; node; node = node->next)
+ {
+ if (is_deleted_kbnode (node))
+ continue;
+ if (node->pkt->pkttype == PKT_SIGNATURE
+ && !(IS_SUBKEY_SIG (node->pkt->pkt.signature)
+ || IS_SUBKEY_REV (node->pkt->pkt.signature)))
+ {
+ delete_kbnode (node);
+ if (sigs_cleaned)
+ ++*sigs_cleaned;
+ }
+ }
+
+ /* Do the selected cleaning. */
+ if (clean_level > KEY_CLEAN_NONE)
+ {
+ /* Clean enitre subkeys. */
+ 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;
+ }
+ }
+ }
+
+ /* Clean duplicate signatures from a subkey. */
+ 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)
+ {
+ n = clean_one_subkey_dupsigs (ctrl, node);
+ if (sigs_cleaned)
+ *sigs_cleaned += n;
+ }
+ }
+ }
+}
diff --git a/g10/key-clean.h b/g10/key-clean.h
new file mode 100644
index 000000000..a0fb76950
--- /dev/null
+++ b/g10/key-clean.h
@@ -0,0 +1,52 @@
+/* key-clean.h - Functions to clean a keyblock
+ * Copyright (C) 2018 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef GNUPG_G10_KEY_CLEAN_H
+#define GNUPG_G10_KEY_CLEAN_H
+
+#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);
+
+void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode,
+ int noisy, int self_only,
+ 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, int clean_level,
+ int *subkeys_cleaned, int *sigs_cleaned);
+
+
+#endif /*GNUPG_G10_KEY_CLEAN_H*/
diff --git a/g10/keydb.h b/g10/keydb.h
index bd156a6a3..9748e571e 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -64,6 +64,20 @@ struct kbnode_struct {
#define is_cloned_kbnode(a) ((a)->private_flag & 2)
+/*
+ * A structure to store key identification as well as some stuff
+ * needed for key validation.
+ */
+struct key_item {
+ struct key_item *next;
+ unsigned int ownertrust,min_ownertrust;
+ byte trust_depth;
+ byte trust_value;
+ char *trust_regexp;
+ u32 kid[2];
+};
+
+
/* Bit flags used with build_pk_list. */
enum
{
@@ -133,6 +147,22 @@ enum
};
+/*
+ * Check whether the signature SIG is in the klist K.
+ */
+static inline struct key_item *
+is_in_klist (struct key_item *k, PKT_signature *sig)
+{
+ for (; k; k = k->next)
+ {
+ if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1])
+ return k;
+ }
+ return NULL;
+}
+
+
+
/*-- keydb.c --*/
#define KEYDB_RESOURCE_FLAG_PRIMARY 2 /* The primary resource. */
@@ -283,6 +313,10 @@ void cache_public_key( PKT_public_key *pk );
/* Disable and drop the public key cache. */
void getkey_disable_caches(void);
+/* Return the public key used for signature SIG and store it at PK. */
+gpg_error_t get_pubkey_for_sig (ctrl_t ctrl,
+ PKT_public_key *pk, PKT_signature *sig);
+
/* Return the public key with the key id KEYID and store it at PK. */
int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid);
@@ -291,6 +325,10 @@ int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid);
also only considers primary keys. */
int get_pubkey_fast (PKT_public_key *pk, u32 *keyid);
+/* Return the entire keyblock used to create SIG. This is a
+ * specialized version of get_pubkeyblock. */
+kbnode_t get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig);
+
/* Return the key block for the key with KEYID. */
kbnode_t get_pubkeyblock (ctrl_t ctrl, u32 *keyid);
diff --git a/g10/keyedit.c b/g10/keyedit.c
index 00b4e7280..9716ed9d6 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -49,6 +49,7 @@
#include "../common/host2net.h"
#include "tofu.h"
#include "key-check.h"
+#include "key-clean.h"
#include "keyedit.h"
static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig,
diff --git a/g10/keygen.c b/g10/keygen.c
index 9e9cead07..fbead107b 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -3293,7 +3293,7 @@ parse_key_parameter_string (const char *string, int part,
* part consider this to be the subkey algo. In case a
* SUGGESTED_USE has been given and the usage of the secondary
* part does not match SUGGESTED_USE try again using the primary
- * part. Noet thar when falling back to the primary key we need
+ * part. Note that when falling back to the primary key we need
* to force clearing the cert usage. */
if (secondary)
{
diff --git a/g10/keylist.c b/g10/keylist.c
index 1f501fc97..39b87e49f 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -1107,6 +1107,9 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
PKT_signature *sig = node->pkt->pkt.signature;
int sigrc;
char *sigstr;
+ char *reason_text = NULL;
+ char *reason_comment = NULL;
+ size_t reason_commentlen;
if (listctx->check_sigs)
{
@@ -1143,7 +1146,11 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
if (sig->sig_class == 0x20 || sig->sig_class == 0x28
|| sig->sig_class == 0x30)
- sigstr = "rev";
+ {
+ sigstr = "rev";
+ get_revocation_reason (sig, &reason_text,
+ &reason_comment, &reason_commentlen);
+ }
else if ((sig->sig_class & ~3) == 0x10)
sigstr = "sig";
else if (sig->sig_class == 0x18)
@@ -1205,6 +1212,40 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
&& (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
show_keyserver_url (sig, 3, 0);
+ if (reason_text)
+ {
+ es_fprintf (es_stdout, " %s%s\n",
+ _("reason for revocation: "), reason_text);
+ if (reason_comment)
+ {
+ const byte *s, *s_lf;
+ size_t n, n_lf;
+
+ s = reason_comment;
+ n = reason_commentlen;
+ s_lf = NULL;
+ do
+ {
+ /* We don't want any empty lines, so we skip them. */
+ for (;n && *s == '\n'; s++, n--)
+ ;
+ if (n)
+ {
+ s_lf = memchr (s, '\n', n);
+ n_lf = s_lf? s_lf - s : n;
+ es_fprintf (es_stdout, " %s",
+ _("revocation comment: "));
+ es_write_sanitized (es_stdout, s, n_lf, NULL, NULL);
+ es_putc ('\n', es_stdout);
+ s += n_lf; n -= n_lf;
+ }
+ } while (s_lf);
+ }
+ }
+
+ xfree (reason_text);
+ xfree (reason_comment);
+
/* fixme: check or list other sigs here */
}
}
@@ -1554,10 +1595,19 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
char *siguid;
size_t siguidlen;
char *issuer_fpr = NULL;
+ char *reason_text = NULL;
+ char *reason_comment = NULL;
+ size_t reason_commentlen;
+ int reason_code;
if (sig->sig_class == 0x20 || sig->sig_class == 0x28
|| sig->sig_class == 0x30)
- sigstr = "rev";
+ {
+ sigstr = "rev";
+ reason_code = get_revocation_reason (sig, &reason_text,
+ &reason_comment,
+ &reason_commentlen);
+ }
else if ((sig->sig_class & ~3) == 0x10)
sigstr = "sig";
else if (sig->sig_class == 0x18)
@@ -1651,8 +1701,11 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
else if (siguid)
es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL);
- es_fprintf (es_stdout, ":%02x%c::", sig->sig_class,
+ es_fprintf (es_stdout, ":%02x%c", sig->sig_class,
sig->flags.exportable ? 'x' : 'l');
+ if (reason_text)
+ es_fprintf (es_stdout, ",%02x", reason_code);
+ es_fputs ("::", es_stdout);
if (opt.no_sig_cache && opt.check_sigs && fprokay)
{
@@ -1662,12 +1715,23 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
else if ((issuer_fpr = issuer_fpr_string (sig)))
es_fputs (issuer_fpr, es_stdout);
- es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo);
+ es_fprintf (es_stdout, ":::%d:", sig->digest_algo);
+
+ if (reason_comment)
+ {
+ es_fputs ("::::", es_stdout);
+ es_write_sanitized (es_stdout, reason_comment, reason_commentlen,
+ ":", NULL);
+ es_putc (':', es_stdout);
+ }
+ es_putc ('\n', es_stdout);
if (opt.show_subpackets)
print_subpackets_colon (sig);
/* fixme: check or list other sigs here */
+ xfree (reason_text);
+ xfree (reason_comment);
xfree (siguid);
xfree (issuer_fpr);
}
diff --git a/g10/main.h b/g10/main.h
index 453d1226a..768f7cfb8 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -396,6 +396,9 @@ gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats,
int collapse_uids( KBNODE *keyblock );
+int get_revocation_reason (PKT_signature *sig, char **r_reason,
+ char **r_comment, size_t *r_commentlen);
+
/*-- export.c --*/
struct export_stats_s;
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 835ab6b72..dcf7dee01 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -533,6 +533,14 @@ static void
proc_encrypted (CTX c, PACKET *pkt)
{
int result = 0;
+ int early_plaintext = literals_seen;
+
+ if (early_plaintext)
+ {
+ log_info (_("WARNING: multiple plaintexts seen\n"));
+ write_status_errcode ("decryption.early_plaintext", GPG_ERR_BAD_DATA);
+ /* We fail only later so that we can print some more info first. */
+ }
if (!opt.quiet)
{
@@ -683,6 +691,10 @@ proc_encrypted (CTX c, PACKET *pkt)
if (!result)
result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek );
+ /* Trigger the deferred error. */
+ if (!result && early_plaintext)
+ result = gpg_error (GPG_ERR_BAD_DATA);
+
if (result == -1)
;
else if (!result
@@ -788,7 +800,14 @@ proc_plaintext( CTX c, PACKET *pkt )
if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8))
log_info (_("Note: sender requested \"for-your-eyes-only\"\n"));
else if (opt.verbose)
- log_info (_("original file name='%.*s'\n"), pt->namelen, pt->name);
+ {
+ /* We don't use print_utf8_buffer because that would require a
+ * string change which we don't want in 2.2. It is also not
+ * clear whether the filename is always utf-8 encoded. */
+ char *tmp = make_printable_string (pt->name, pt->namelen, 0);
+ log_info (_("original file name='%.*s'\n"), (int)strlen (tmp), tmp);
+ xfree (tmp);
+ }
free_md_filter_context (&c->mfx);
if (gcry_md_open (&c->mfx.md, 0, 0))
@@ -1681,7 +1700,7 @@ akl_has_wkd_method (void)
/* Return the ISSUER fingerprint buffer and its lenbgth at R_LEN.
* Returns NULL if not available. The returned buffer is valid as
* long as SIG is not modified. */
-static const byte *
+const byte *
issuer_fpr_raw (PKT_signature *sig, size_t *r_len)
{
const byte *p;
@@ -1698,7 +1717,7 @@ issuer_fpr_raw (PKT_signature *sig, size_t *r_len)
}
-/* Return the ISSUER fingerprint string in human readbale format if
+/* Return the ISSUER fingerprint string in human readable format if
* available. Caller must release the string. */
/* FIXME: Move to another file. */
char *
@@ -2064,7 +2083,7 @@ check_sig_and_print (CTX c, kbnode_t node)
* keyblock has already been fetched. Thus we could use the
* fingerprint or PK itself to lookup the entire keyblock. That
* would best be done with a cache. */
- keyblock = get_pubkeyblock (c->ctrl, sig->keyid);
+ keyblock = get_pubkeyblock_for_sig (c->ctrl, sig);
snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX [uncertain] ",
(ulong)sig->keyid[0], (ulong)sig->keyid[1]);
diff --git a/g10/packet.h b/g10/packet.h
index a4157c53f..3f872944b 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -244,6 +244,7 @@ typedef struct
const byte *trust_regexp;
struct revocation_key *revkey;
int numrevkeys;
+ int help_counter; /* Used internally bu some fucntions. */
pka_info_t *pka_info; /* Malloced PKA data or NULL if not
available. See also flags.pka_tried. */
char *signers_uid; /* Malloced value of the SIGNERS_UID
@@ -630,6 +631,7 @@ int proc_signature_packets_by_fd (ctrl_t ctrl,
int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a);
int list_packets( iobuf_t a );
+const byte *issuer_fpr_raw (PKT_signature *sig, size_t *r_len);
char *issuer_fpr_string (PKT_signature *sig);
/*-- parse-packet.c --*/
diff --git a/g10/pkclist.c b/g10/pkclist.c
index 6aec13882..e7484432a 100644
--- a/g10/pkclist.c
+++ b/g10/pkclist.c
@@ -113,7 +113,7 @@ void
show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode)
{
/* Hmmm, this is not so easy because we have to duplicate the code
- * used in the trustbd to calculate the keyflags. We need to find
+ * used in the trustdb to calculate the keyflags. We need to find
* a clean way to check revocation certificates on keys and
* signatures. And there should be no duplicate code. Because we
* enter this function only when the trustdb told us that we have
@@ -548,7 +548,7 @@ check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
unsigned int trustlevel = TRUST_UNKNOWN;
int rc=0;
- rc = get_pubkey (ctrl, pk, sig->keyid );
+ rc = get_pubkey_for_sig (ctrl, pk, sig);
if (rc)
{ /* this should not happen */
log_error("Ooops; the key vanished - can't check the trust\n");
diff --git a/g10/sig-check.c b/g10/sig-check.c
index fc6983993..0ec384347 100644
--- a/g10/sig-check.c
+++ b/g10/sig-check.c
@@ -156,7 +156,7 @@ check_signature2 (ctrl_t ctrl,
log_info(_("WARNING: signature digest conflict in message\n"));
rc = gpg_error (GPG_ERR_GENERAL);
}
- else if (get_pubkey (ctrl, pk, sig->keyid))
+ else if (get_pubkey_for_sig (ctrl, pk, sig))
rc = gpg_error (GPG_ERR_NO_PUBKEY);
else if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION,
pk->pubkey_algo, pk->pkey,
@@ -478,8 +478,17 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig,
sig->sig_class, pk->pubkey_usage);
return rc;
}
- /* Fixme: Should we also check the signing capability here for data
- * signature? */
+
+ /* For data signatures check that the key has sign usage. */
+ if (IS_SIG (sig) && !(pk->pubkey_usage & PUBKEY_USAGE_SIG))
+ {
+ rc = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ if (!opt.quiet)
+ log_info (_("bad data signature from key %s: %s (0x%02x, 0x%x)\n"),
+ keystr_from_pk (pk), gpg_strerror (rc),
+ sig->sig_class, pk->pubkey_usage);
+ return rc;
+ }
/* Make sure the digest algo is enabled (in case of a detached
* signature). */
@@ -917,7 +926,7 @@ check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer,
if (IS_CERT (sig))
signer->req_usage = PUBKEY_USAGE_CERT;
- rc = get_pubkey (ctrl, signer, sig->keyid);
+ rc = get_pubkey_for_sig (ctrl, signer, sig);
if (rc)
{
xfree (signer);
diff --git a/g10/sign.c b/g10/sign.c
index df71ccce1..581a08f5b 100644
--- a/g10/sign.c
+++ b/g10/sign.c
@@ -772,7 +772,7 @@ write_signature_packets (ctrl_t ctrl,
gpg_strerror (rc));
}
else
- xfree (sig);
+ free_seckey_enc (sig);
if (rc)
return rc;
diff --git a/g10/test-stubs.c b/g10/test-stubs.c
index 088aba8c6..a2b0d2906 100644
--- a/g10/test-stubs.c
+++ b/g10/test-stubs.c
@@ -535,3 +535,17 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
return 0;
}
+
+int
+get_revocation_reason (PKT_signature *sig, char **r_reason,
+ char **r_comment, size_t *r_commentlen)
+{
+ (void)sig;
+ (void)r_commentlen;
+
+ if (r_reason)
+ *r_reason = NULL;
+ if (r_comment)
+ *r_comment = NULL;
+ return 0;
+}
diff --git a/g10/trust.c b/g10/trust.c
index 6d4f0e74b..bd1c89458 100644
--- a/g10/trust.c
+++ b/g10/trust.c
@@ -437,391 +437,3 @@ get_validity_string (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid)
return _("revoked");
return trust_value_to_string (trustlevel);
}
-
-
-
-/*
- * Mark the signature of the given UID which are used to certify it.
- * To do this, we first revmove all signatures which are not valid and
- * from the remain ones we look for the latest one. If this is not a
- * certification revocation signature we mark the signature by setting
- * node flag bit 8. Revocations are marked with flag 11, and sigs
- * from unavailable keys are marked with flag 12. Note that flag bits
- * 9 and 10 are used for internal purposes.
- */
-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)
-{
- kbnode_t node;
- PKT_signature *sig;
-
- /* First check all signatures. */
- for (node=uidnode->next; node; node = node->next)
- {
- int rc;
-
- node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12);
- if (node->pkt->pkttype == PKT_USER_ID
- || node->pkt->pkttype == PKT_PUBLIC_SUBKEY
- || node->pkt->pkttype == PKT_SECRET_SUBKEY)
- break; /* ready */
- if (node->pkt->pkttype != PKT_SIGNATURE)
- continue;
- sig = node->pkt->pkt.signature;
- if (main_kid
- && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1])
- continue; /* ignore self-signatures if we pass in a main_kid */
- if (!IS_UID_SIG(sig) && !IS_UID_REV(sig))
- continue; /* we only look at these signature classes */
- if(sig->sig_class>=0x11 && sig->sig_class<=0x13 &&
- sig->sig_class-0x10<opt.min_cert_level)
- continue; /* treat anything under our min_cert_level as an
- invalid signature */
- if (klist && !is_in_klist (klist, sig))
- continue; /* no need to check it then */
- if ((rc=check_key_signature (ctrl, keyblock, node, NULL)))
- {
- /* we ignore anything that won't verify, but tag the
- no_pubkey case */
- if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
- node->flag |= 1<<12;
- continue;
- }
- node->flag |= 1<<9;
- }
- /* Reset the remaining flags. */
- for (; node; node = node->next)
- node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12);
-
- /* kbnode flag usage: bit 9 is here set for signatures to consider,
- * bit 10 will be set by the loop to keep track of keyIDs already
- * processed, bit 8 will be set for the usable signatures, and bit
- * 11 will be set for usable revocations. */
-
- /* For each cert figure out the latest valid one. */
- for (node=uidnode->next; node; node = node->next)
- {
- KBNODE n, signode;
- u32 kid[2];
- u32 sigdate;
-
- if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
- || node->pkt->pkttype == PKT_SECRET_SUBKEY)
- break;
- if ( !(node->flag & (1<<9)) )
- continue; /* not a node to look at */
- if ( (node->flag & (1<<10)) )
- continue; /* signature with a keyID already processed */
- node->flag |= (1<<10); /* mark this node as processed */
- sig = node->pkt->pkt.signature;
- signode = node;
- sigdate = sig->timestamp;
- kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1];
-
- /* Now find the latest and greatest signature */
- for (n=uidnode->next; n; n = n->next)
- {
- if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY
- || n->pkt->pkttype == PKT_SECRET_SUBKEY)
- break;
- if ( !(n->flag & (1<<9)) )
- continue;
- if ( (n->flag & (1<<10)) )
- continue; /* shortcut already processed signatures */
- sig = n->pkt->pkt.signature;
- if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1])
- continue;
- n->flag |= (1<<10); /* mark this node as processed */
-
- /* If signode is nonrevocable and unexpired and n isn't,
- then take signode (skip). It doesn't matter which is
- older: if signode was older then we don't want to take n
- as signode is nonrevocable. If n was older then we're
- automatically fine. */
-
- if(((IS_UID_SIG(signode->pkt->pkt.signature) &&
- !signode->pkt->pkt.signature->flags.revocable &&
- (signode->pkt->pkt.signature->expiredate==0 ||
- signode->pkt->pkt.signature->expiredate>curtime))) &&
- (!(IS_UID_SIG(n->pkt->pkt.signature) &&
- !n->pkt->pkt.signature->flags.revocable &&
- (n->pkt->pkt.signature->expiredate==0 ||
- n->pkt->pkt.signature->expiredate>curtime))))
- continue;
-
- /* If n is nonrevocable and unexpired and signode isn't,
- then take n. Again, it doesn't matter which is older: if
- n was older then we don't want to take signode as n is
- nonrevocable. If signode was older then we're
- automatically fine. */
-
- if((!(IS_UID_SIG(signode->pkt->pkt.signature) &&
- !signode->pkt->pkt.signature->flags.revocable &&
- (signode->pkt->pkt.signature->expiredate==0 ||
- signode->pkt->pkt.signature->expiredate>curtime))) &&
- ((IS_UID_SIG(n->pkt->pkt.signature) &&
- !n->pkt->pkt.signature->flags.revocable &&
- (n->pkt->pkt.signature->expiredate==0 ||
- n->pkt->pkt.signature->expiredate>curtime))))
- {
- signode = n;
- sigdate = sig->timestamp;
- continue;
- }
-
- /* At this point, if it's newer, it goes in as the only
- remaining possibilities are signode and n are both either
- revocable or expired or both nonrevocable and unexpired.
- If the timestamps are equal take the later ordered
- packet, presuming that the key packets are hopefully in
- their original order. */
-
- if (sig->timestamp >= sigdate)
- {
- signode = n;
- sigdate = sig->timestamp;
- }
- }
-
- sig = signode->pkt->pkt.signature;
- if (IS_UID_SIG (sig))
- { /* this seems to be a usable one which is not revoked.
- * Just need to check whether there is an expiration time,
- * We do the expired certification after finding a suitable
- * certification, the assumption is that a signator does not
- * want that after the expiration of his certificate the
- * system falls back to an older certification which has a
- * different expiration time */
- const byte *p;
- u32 expire;
-
- p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL );
- expire = p? sig->timestamp + buf32_to_u32(p) : 0;
-
- if (expire==0 || expire > curtime )
- {
- signode->flag |= (1<<8); /* yeah, found a good cert */
- if (next_expire && expire && expire < *next_expire)
- *next_expire = expire;
- }
- }
- else
- signode->flag |= (1<<11);
- }
-}
-
-
-static int
-clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode,
- int noisy, int self_only)
-{
- int deleted = 0;
- kbnode_t node;
- u32 keyid[2];
-
- log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
- || keyblock->pkt->pkttype == PKT_SECRET_KEY);
-
- keyid_from_pk (keyblock->pkt->pkt.public_key, keyid);
-
- /* Passing in a 0 for current time here means that we'll never weed
- out an expired sig. This is correct behavior since we want to
- keep the most recent expired sig in a series. */
- mark_usable_uid_certs (ctrl, keyblock, uidnode, NULL, NULL, 0, NULL);
-
- /* What we want to do here is remove signatures that are not
- considered as part of the trust calculations. Thus, all invalid
- signatures are out, as are any signatures that aren't the last of
- a series of uid sigs or revocations It breaks down like this:
- coming out of mark_usable_uid_certs, if a sig is unflagged, it is
- not even a candidate. If a sig has flag 9 or 10, that means it
- was selected as a candidate and vetted. If a sig has flag 8 it
- is a usable signature. If a sig has flag 11 it is a usable
- revocation. If a sig has flag 12 it was issued by an unavailable
- key. "Usable" here means the most recent valid
- signature/revocation in a series from a particular signer.
-
- Delete everything that isn't a usable uid sig (which might be
- expired), a usable revocation, or a sig from an unavailable
- key. */
-
- for (node=uidnode->next;
- node && node->pkt->pkttype==PKT_SIGNATURE;
- node=node->next)
- {
- int keep;
-
- keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0]
- && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1;
-
- /* Keep usable uid sigs ... */
- if ((node->flag & (1<<8)) && keep)
- continue;
-
- /* ... and usable revocations... */
- if ((node->flag & (1<<11)) && keep)
- continue;
-
- /* ... and sigs from unavailable keys. */
- /* disabled for now since more people seem to want sigs from
- unavailable keys removed altogether. */
- /*
- if(node->flag & (1<<12))
- continue;
- */
-
- /* Everything else we delete */
-
- /* At this point, if 12 is set, the signing key was unavailable.
- If 9 or 10 is set, it's superseded. Otherwise, it's
- invalid. */
-
- if (noisy)
- log_info ("removing signature from key %s on user ID \"%s\": %s\n",
- keystr (node->pkt->pkt.signature->keyid),
- uidnode->pkt->pkt.user_id->name,
- node->flag&(1<<12)? "key unavailable":
- node->flag&(1<<9)? "signature superseded"
- /* */ :"invalid signature" );
-
- delete_kbnode (node);
- deleted++;
- }
-
- return deleted;
-}
-
-
-/* This is substantially easier than clean_sigs_from_uid since we just
- have to establish if the uid has a valid self-sig, is not revoked,
- and is not expired. Note that this does not take into account
- whether the uid has a trust path to it - just whether the keyholder
- themselves has certified the uid. Returns true if the uid was
- compacted. To "compact" a user ID, we simply remove ALL signatures
- except the self-sig that caused the user ID to be remove-worthy.
- We don't actually remove the user ID packet itself since it might
- be resurrected in a later merge. Note that this function requires
- that the caller has already done a merge_keys_and_selfsig().
-
- TODO: change the import code to allow importing a uid with only a
- revocation if the uid already exists on the keyring. */
-
-static int
-clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy)
-{
- kbnode_t node;
- PKT_user_id *uid = uidnode->pkt->pkt.user_id;
- int deleted = 0;
-
- log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
- || keyblock->pkt->pkttype == PKT_SECRET_KEY);
- log_assert (uidnode->pkt->pkttype==PKT_USER_ID);
-
- /* Skip valid user IDs, compacted user IDs, and non-self-signed user
- IDs if --allow-non-selfsigned-uid is set. */
- if (uid->created
- || uid->flags.compacted
- || (!uid->flags.expired && !uid->flags.revoked && opt.allow_non_selfsigned_uid))
- return 0;
-
- for (node=uidnode->next;
- node && node->pkt->pkttype == PKT_SIGNATURE;
- node=node->next)
- {
- if (!node->pkt->pkt.signature->flags.chosen_selfsig)
- {
- delete_kbnode (node);
- deleted = 1;
- uidnode->pkt->pkt.user_id->flags.compacted = 1;
- }
- }
-
- if (noisy)
- {
- const char *reason;
- char *user = utf8_to_native (uid->name, uid->len, 0);
-
- if (uid->flags.revoked)
- reason = _("revoked");
- else if (uid->flags.expired)
- reason = _("expired");
- else
- reason = _("invalid");
-
- log_info ("compacting user ID \"%s\" on key %s: %s\n",
- user, keystr_from_pk (keyblock->pkt->pkt.public_key),
- reason);
-
- xfree (user);
- }
-
- return deleted;
-}
-
-
-/* Needs to be called after a merge_keys_and_selfsig() */
-void
-clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode,
- int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned)
-{
- int dummy = 0;
-
- log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
- || keyblock->pkt->pkttype == PKT_SECRET_KEY);
- log_assert (uidnode->pkt->pkttype==PKT_USER_ID);
-
- if (!uids_cleaned)
- uids_cleaned = &dummy;
-
- if (!sigs_cleaned)
- sigs_cleaned = &dummy;
-
- /* Do clean_uid_from_key first since if it fires off, we don't have
- to bother with the other. */
- *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy);
- if (!uidnode->pkt->pkt.user_id->flags.compacted)
- *sigs_cleaned += clean_sigs_from_uid (ctrl, keyblock, uidnode,
- noisy, self_only);
-}
-
-
-/* NB: This function marks the deleted nodes only and the caller is
- * responsible to skip or remove them. */
-void
-clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only,
- int *uids_cleaned, int *sigs_cleaned)
-{
- kbnode_t node;
-
- merge_keys_and_selfsig (ctrl, keyblock);
-
- for (node = keyblock->next;
- node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY
- || node->pkt->pkttype == PKT_SECRET_SUBKEY);
- node = node->next)
- {
- if (node->pkt->pkttype == PKT_USER_ID)
- clean_one_uid (ctrl, keyblock, node, noisy, self_only,
- uids_cleaned, sigs_cleaned);
- }
-
- /* Remove bogus subkey binding signatures: The only signatures
- * allowed are of class 0x18 and 0x28. */
- log_assert (!node || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
- || node->pkt->pkttype == PKT_SECRET_SUBKEY));
- for (; node; node = node->next)
- {
- if (is_deleted_kbnode (node))
- continue;
- if (node->pkt->pkttype == PKT_SIGNATURE
- && !(IS_SUBKEY_SIG (node->pkt->pkt.signature)
- || IS_SUBKEY_REV (node->pkt->pkt.signature)))
- {
- delete_kbnode (node);
- if (sigs_cleaned)
- ++*sigs_cleaned;
- }
- }
-}
diff --git a/g10/trustdb.c b/g10/trustdb.c
index 2c2d2394a..8ef6db542 100644
--- a/g10/trustdb.c
+++ b/g10/trustdb.c
@@ -41,6 +41,7 @@
#include "tdbio.h"
#include "trustdb.h"
#include "tofu.h"
+#include "key-clean.h"
typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */
diff --git a/g10/trustdb.h b/g10/trustdb.h
index 4bc4ca971..d52fc53f2 100644
--- a/g10/trustdb.h
+++ b/g10/trustdb.h
@@ -46,36 +46,6 @@
#define NAMEHASH_LEN 20
-/*
- * A structure to store key identification as well as some stuff needed
- * for validation
- */
-struct key_item {
- struct key_item *next;
- unsigned int ownertrust,min_ownertrust;
- byte trust_depth;
- byte trust_value;
- char *trust_regexp;
- u32 kid[2];
-};
-
-
-/*
- * Check whether the signature SIG is in the klist K.
- */
-static inline struct key_item *
-is_in_klist (struct key_item *k, PKT_signature *sig)
-{
- for (; k; k = k->next)
- {
- if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1])
- return k;
- }
- return NULL;
-}
-
-
-
/*-- trust.c --*/
int cache_disabled_value (ctrl_t ctrl, PKT_public_key *pk);
void register_trusted_keyid (u32 *keyid);
@@ -103,17 +73,6 @@ int get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk,
const char *get_validity_string (ctrl_t ctrl,
PKT_public_key *pk, PKT_user_id *uid);
-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);
-
-void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode,
- int noisy, int self_only,
- int *uids_cleaned, int *sigs_cleaned);
-void clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only,
- int *uids_cleaned,int *sigs_cleaned);
-
-
/*-- trustdb.c --*/
void tdb_register_trusted_keyid (u32 *keyid);