aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2022-11-25 15:04:54 +0000
committerWerner Koch <[email protected]>2022-11-25 15:04:54 +0000
commit1324dc3490b02c4ff818655db1474c594f04e4ec (patch)
treeb9c72c1068de3d8e2d7238e2d9a7f3c1e1a82651
parentdirmngr: Silence ocsp debug output. (diff)
downloadgnupg-1324dc3490b02c4ff818655db1474c594f04e4ec.tar.gz
gnupg-1324dc3490b02c4ff818655db1474c594f04e4ec.zip
gpg: New option --list-filter
* g10/gpg.c (oListFilter): New. (opts): Add --list-filter. (main): Parse oListFilter. * g10/keylist.c: Include init.h and recsel.h. (struct list_filter_s, list_filter): New. (release_list_filter): New. (cleanup_keylist_globals): New. (parse_and_set_list_filter): New. (list_keyblock): Implement --list-filter type "select". * g10/import.c (impex_filter_getval): Add scope support and new property names "key-size", "algostr", "origin", "lastupd", and "url". -- This option is pretty useful to select keys based on their properties. The scope thing can be sued to limit a selection to just the primary key or to subkeys. For example: gpg -k --list-filter 'select=revoked-f && sub/algostr=ed25519' Lists all non-revoked keys with an ed25519 (signing)-subkey.
-rw-r--r--doc/gpg.texi41
-rw-r--r--g10/gpg.c7
-rw-r--r--g10/gpgv.c8
-rw-r--r--g10/import.c71
-rw-r--r--g10/keylist.c82
-rw-r--r--g10/main.h1
-rw-r--r--g10/test-stubs.c8
7 files changed, 204 insertions, 14 deletions
diff --git a/doc/gpg.texi b/doc/gpg.texi
index 25065f8e4..ed75a613a 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -1338,6 +1338,13 @@ Assume "yes" on most questions. Should not be used in an option file.
Assume "no" on most questions. Should not be used in an option file.
+@item --list-filter @{select=@var{expr}@}
+@opindex list-filter
+A list filter can be used to output only certain keys during key
+listsin command. For the availbale property names, see the description
+of @option{--import-filter}.
+
+
@item --list-options @var{parameters}
@opindex list-options
This is a space or comma delimited string that gives options used when
@@ -2550,11 +2557,21 @@ The available filter types are:
Self-signatures are not considered.
Currently only implemented for --import-filter.
+ @item select
+ This filter is only implemented by @option{--list-filter}. All
+ property names may be used.
+
@end table
For the syntax of the expression see the chapter "FILTER EXPRESSIONS".
The property names for the expressions depend on the actual filter
-type and are indicated in the following table.
+type and are indicated in the following table. Note that all property
+names may also be used by @option{--list-filter}.
+
+Property names may be prefix with a scope delimited by a slash. Valid
+scopes are "pub" for public and secret primary keys, "sub" for public
+and secret subkeys, "uid" for for user-ID packets, and "sig" for
+signature packets. Invalid scopes are currently ignored.
The available properties are:
@@ -2567,10 +2584,18 @@ The available properties are:
The addr-spec part of a user id with mailbox or the empty string.
(keep-uid)
+ @item algostr
+ A string with the key algorithm description. For example "rsa3072"
+ or "ed25519".
+
@item key_algo
A number with the public key algorithm of a key or subkey packet.
(drop-subkey)
+ @item key_size
+ A number with the effective key size of a key or subkey packet.
+ (drop-subkey)
+
@item key_created
@itemx key_created_d
The first is the timestamp a public key or subkey packet was
@@ -2593,7 +2618,7 @@ The available properties are:
been revoked.
@item disabled
- Boolean indicating whether a primary key is disabled. (not used)
+ Boolean indicating whether a primary key is disabled.
@item secret
Boolean indicating whether a key or subkey is a secret one.
@@ -2616,6 +2641,18 @@ The available properties are:
@item sig_digest_algo
A number with the digest algorithm of a signature packet. (drop-sig)
+ @item origin
+ A string with the key origin or a question mark. For example the
+ string ``wkd'' is used if a key originated from a Web Key Directory
+ lookup.
+
+ @item lastupd
+ The timestamp the key was last updated from a keyserver or the Web
+ Key Directory.
+
+ @item url
+ A string with the the URL associated wit the last key lookup.
+
@end table
@item --export-options @var{parameters}
diff --git a/g10/gpg.c b/g10/gpg.c
index 1514254b9..68c0454ee 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -327,6 +327,7 @@ enum cmd_and_opt_values
oExportOptions,
oExportFilter,
oListOptions,
+ oListFilter,
oVerifyOptions,
oTempDir,
oExecPath,
@@ -794,6 +795,7 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_header ("Keylist", N_("Options controlling key listings")),
ARGPARSE_s_s (oListOptions, "list-options", "@"),
+ ARGPARSE_s_s (oListFilter, "list-filter", "@"),
ARGPARSE_s_n (oFullTimestrings, "full-timestrings", "@"),
ARGPARSE_s_n (oShowPhotos, "show-photos", "@"),
ARGPARSE_s_n (oNoShowPhotos, "no-show-photos", "@"),
@@ -3357,6 +3359,11 @@ main (int argc, char **argv)
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
+ case oListFilter:
+ rc = parse_and_set_list_filter (pargs.r.ret_str);
+ if (rc)
+ log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
+ break;
case oListOptions:
if(!parse_list_options(pargs.r.ret_str))
{
diff --git a/g10/gpgv.c b/g10/gpgv.c
index 3bb99dc6c..ceded4af9 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -812,3 +812,11 @@ get_revocation_reason (PKT_signature *sig, char **r_reason,
*r_comment = NULL;
return 0;
}
+
+const char *
+impex_filter_getval (void *cookie, const char *propname)
+{
+ (void)cookie;
+ (void)propname;
+ return NULL;
+}
diff --git a/g10/import.c b/g10/import.c
index c3ef89323..9fab46ca6 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1430,7 +1430,8 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock)
}
-/* Helper for apply_*_filter in import.c and export.c. */
+/* Helper for apply_*_filter in import.c and export.c and also used by
+ * keylist.c. */
const char *
impex_filter_getval (void *cookie, const char *propname)
{
@@ -1440,11 +1441,32 @@ impex_filter_getval (void *cookie, const char *propname)
kbnode_t node = parm->node;
static char numbuf[20];
const char *result;
+ const char *s;
+ enum { scpNone = 0, scpPub, scpSub, scpUid, scpSig} scope = 0;
log_assert (ctrl && ctrl->magic == SERVER_CONTROL_MAGIC);
- if (node->pkt->pkttype == PKT_USER_ID
- || node->pkt->pkttype == PKT_ATTRIBUTE)
+ /* We allow a prefix delimited by a slash to limit the scope of the
+ * keyword. Note that "pub" also includes "sec" and "sub" includes
+ * "ssb". */
+ if ((s=strchr (propname, '/')) && s != propname)
+ {
+ size_t n = s - propname;
+ if (!strncmp (propname, "pub", n))
+ scope = scpPub;
+ else if (!strncmp (propname, "sub", n))
+ scope = scpSub;
+ else if (!strncmp (propname, "uid", n))
+ scope = scpUid;
+ else if (!strncmp (propname, "sig", n))
+ scope = scpSig;
+
+ propname = s + 1;
+ }
+
+ if ((node->pkt->pkttype == PKT_USER_ID
+ || node->pkt->pkttype == PKT_ATTRIBUTE)
+ && (!scope || scope == scpUid))
{
PKT_user_id *uid = node->pkt->pkt.user_id;
@@ -1473,7 +1495,8 @@ impex_filter_getval (void *cookie, const char *propname)
else
result = NULL;
}
- else if (node->pkt->pkttype == PKT_SIGNATURE)
+ else if (node->pkt->pkttype == PKT_SIGNATURE
+ && (!scope || scope == scpSig))
{
PKT_signature *sig = node->pkt->pkt.signature;
@@ -1503,10 +1526,12 @@ impex_filter_getval (void *cookie, const char *propname)
else
result = NULL;
}
- else if (node->pkt->pkttype == PKT_PUBLIC_KEY
- || node->pkt->pkttype == PKT_SECRET_KEY
- || node->pkt->pkttype == PKT_PUBLIC_SUBKEY
- || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ else if (((node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_SECRET_KEY)
+ && (!scope || scope == scpPub))
+ || ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ && (!scope || scope == scpSub)))
{
PKT_public_key *pk = node->pkt->pkt.public_key;
@@ -1520,6 +1545,16 @@ impex_filter_getval (void *cookie, const char *propname)
snprintf (numbuf, sizeof numbuf, "%d", pk->pubkey_algo);
result = numbuf;
}
+ else if (!strcmp (propname, "key_size"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%u", nbits_from_pk (pk));
+ result = numbuf;
+ }
+ else if (!strcmp (propname, "algostr"))
+ {
+ pubkey_string (pk, parm->hexfpr, sizeof parm->hexfpr);
+ result = parm->hexfpr;
+ }
else if (!strcmp (propname, "key_created"))
{
snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->timestamp);
@@ -1556,6 +1591,26 @@ impex_filter_getval (void *cookie, const char *propname)
hexfingerprint (pk, parm->hexfpr, sizeof parm->hexfpr);
result = parm->hexfpr;
}
+ else if (!strcmp (propname, "origin"))
+ {
+ result = key_origin_string (pk->keyorg);
+ }
+ else if (!strcmp (propname, "lastupd"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->keyupdate);
+ result = numbuf;
+ }
+ else if (!strcmp (propname, "url"))
+ {
+ if (pk->updateurl && *pk->updateurl)
+ {
+ /* Fixme: This might get truncated. */
+ mem2str (parm->hexfpr, pk->updateurl, sizeof parm->hexfpr);
+ result = parm->hexfpr;
+ }
+ else
+ result = "";
+ }
else
result = NULL;
}
diff --git a/g10/keylist.c b/g10/keylist.c
index f8e4ff32a..1ced732a4 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -44,6 +44,8 @@
#include "../common/mbox-util.h"
#include "../common/zb32.h"
#include "tofu.h"
+#include "../common/init.h"
+#include "../common/recsel.h"
#include "../common/compliance.h"
#include "../common/pkscreening.h"
@@ -64,16 +66,26 @@ struct keylist_context
int no_validity; /* Do not show validity. */
};
-
-static void list_keyblock (ctrl_t ctrl,
- kbnode_t keyblock, int secret, int has_secret,
- int fpr, struct keylist_context *listctx);
+/* An object and a global instance to store selectors created from
+ * --list-filter select=EXPR.
+ */
+struct list_filter_s
+{
+ recsel_expr_t selkey;
+};
+struct list_filter_s list_filter;
/* The stream used to write attribute packets to. */
static estream_t attrib_fp;
+
+
+static void list_keyblock (ctrl_t ctrl,
+ kbnode_t keyblock, int secret, int has_secret,
+ int fpr, struct keylist_context *listctx);
+
/* Release resources from a keylist context. */
static void
keylist_context_release (struct keylist_context *listctx)
@@ -82,6 +94,49 @@ keylist_context_release (struct keylist_context *listctx)
}
+static void
+release_list_filter (struct list_filter_s *filt)
+{
+ recsel_release (filt->selkey);
+ filt->selkey = NULL;
+}
+
+
+static void
+cleanup_keylist_globals (void)
+{
+ release_list_filter (&list_filter);
+}
+
+
+/* Parse and set an list filter from string. STRING has the format
+ * "NAME=EXPR" with NAME being the name of the filter. Spaces before
+ * and after NAME are not allowed. If this function is all called
+ * several times all expressions for the same NAME are concatenated.
+ * Supported filter names are:
+ *
+ * - select :: If the expression evaluates to true for a certain key
+ * this key will be listed. The expression may use any
+ * variable defined for the export and import filters.
+ *
+ */
+gpg_error_t
+parse_and_set_list_filter (const char *string)
+{
+ gpg_error_t err;
+
+ /* Auto register the cleanup function. */
+ register_mem_cleanup_func (cleanup_keylist_globals);
+
+ if (!strncmp (string, "select=", 7))
+ err = recsel_parse_expr (&list_filter.selkey, string+7);
+ else
+ err = gpg_error (GPG_ERR_INV_NAME);
+
+ return err;
+}
+
+
/* List the keys. If list is NULL, all available keys are listed.
* With LOCATE_MODE set the locate algorithm is used to find a key; if
* in addition NO_LOCAL is set the locate does not look into the local
@@ -2163,6 +2218,7 @@ reorder_keyblock (KBNODE keyblock)
do_reorder_keyblock (keyblock, 0);
}
+
static void
list_keyblock (ctrl_t ctrl,
KBNODE keyblock, int secret, int has_secret, int fpr,
@@ -2170,6 +2226,24 @@ list_keyblock (ctrl_t ctrl,
{
reorder_keyblock (keyblock);
+ if (list_filter.selkey)
+ {
+ int selected = 0;
+ struct impex_filter_parm_s parm;
+ parm.ctrl = ctrl;
+
+ for (parm.node = keyblock; parm.node; parm.node = parm.node->next)
+ {
+ if (recsel_select (list_filter.selkey, impex_filter_getval, &parm))
+ {
+ selected = 1;
+ break;
+ }
+ }
+ if (!selected)
+ return; /* Skip this one. */
+ }
+
if (opt.with_colons)
list_keyblock_colon (ctrl, keyblock, secret, has_secret);
else if ((opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
diff --git a/g10/main.h b/g10/main.h
index 2f14f374b..6d5ba77ac 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -464,6 +464,7 @@ void release_revocation_reason_info (struct revocation_reason_info *reason);
void public_key_list (ctrl_t ctrl, strlist_t list,
int locate_mode, int no_local);
void secret_key_list (ctrl_t ctrl, strlist_t list );
+gpg_error_t parse_and_set_list_filter (const char *string);
void print_subpackets_colon(PKT_signature *sig);
void reorder_keyblock (KBNODE keyblock);
void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret,
diff --git a/g10/test-stubs.c b/g10/test-stubs.c
index cfe33b1d0..6ae0f4eb7 100644
--- a/g10/test-stubs.c
+++ b/g10/test-stubs.c
@@ -572,3 +572,11 @@ get_revocation_reason (PKT_signature *sig, char **r_reason,
*r_comment = NULL;
return 0;
}
+
+const char *
+impex_filter_getval (void *cookie, const char *propname)
+{
+ (void)cookie;
+ (void)propname;
+ return NULL;
+}