core: Add GPGME_KEYLIST_MODE_WITH_TOFU.

* src/gpgme.h.in (GPGME_KEYLIST_MODE_WITH_TOFU): New.
* src/engine-gpg.c (gpg_keylist_build_options): Use that.
* src/keylist.c: Include limits.h.
(parse_tfs_record): New.
(keylist_colon_handler): Support TFS record.
* tests/run-keylist.c: Include time.h.
(isotimestr): New.
(main): Add option --tofu.  Print TOFU info.
* tests/run-verify.c: Include time.h.
(isotimestr): New.
(print_result): Use isotimestr for TOFU dates.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-08-25 11:38:03 +02:00
parent 38798fee5b
commit 9ee103957e
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
7 changed files with 186 additions and 15 deletions

1
NEWS
View File

@ -26,6 +26,7 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_STATUS_TOFU_STATS NEW. GPGME_STATUS_TOFU_STATS NEW.
GPGME_STATUS_TOFU_STATS_LONG NEW. GPGME_STATUS_TOFU_STATS_LONG NEW.
GPGME_STATUS_NOTATION_FLAGS NEW. GPGME_STATUS_NOTATION_FLAGS NEW.
GPGME_KEYLIST_MODE_WITH_TOFU NEW.
GPGME_DATA_TYPE_PGP_ENCRYPTED NEW. GPGME_DATA_TYPE_PGP_ENCRYPTED NEW.
GPGME_DATA_TYPE_PGP_SIGNATURE NEW. GPGME_DATA_TYPE_PGP_SIGNATURE NEW.
GPGME_DATA_ENCODING_MIME NEW. GPGME_DATA_ENCODING_MIME NEW.

View File

@ -2683,6 +2683,11 @@ signature notations on key signatures should be included in the listed
keys. This only works if @code{GPGME_KEYLIST_MODE_SIGS} is also keys. This only works if @code{GPGME_KEYLIST_MODE_SIGS} is also
enabled. enabled.
@item GPGME_KEYLIST_MODE_WITH_TOFU
The @code{GPGME_KEYLIST_MODE_WITH_TOFU} symbol specifies that
information pertaining to the TOFU trust model should be included in
the listed keys.
@item GPGME_KEYLIST_MODE_WITH_SECRET @item GPGME_KEYLIST_MODE_WITH_SECRET
The @code{GPGME_KEYLIST_MODE_WITH_SECRET} returns information about The @code{GPGME_KEYLIST_MODE_WITH_SECRET} returns information about
the presence of a corresponding secret key in a public key listing. A the presence of a corresponding secret key in a public key listing. A

View File

@ -2338,8 +2338,13 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
err = add_arg (gpg, "--with-fingerprint"); err = add_arg (gpg, "--with-fingerprint");
} }
if (!err && (mode & GPGME_KEYLIST_MODE_WITH_TOFU)
&& have_gpg_version (gpg, "2.1.16"))
err = add_arg (gpg, "--with-tofu-info");
if (!err && (mode & GPGME_KEYLIST_MODE_WITH_SECRET)) if (!err && (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
err = add_arg (gpg, "--with-secret"); err = add_arg (gpg, "--with-secret");
if (!err if (!err
&& (mode & GPGME_KEYLIST_MODE_SIGS) && (mode & GPGME_KEYLIST_MODE_SIGS)
&& (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS)) && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
@ -2348,6 +2353,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
if (!err) if (!err)
err = add_arg (gpg, "show-sig-subpackets=\"20,26\""); err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
} }
if (!err) if (!err)
{ {
if ( (mode & GPGME_KEYLIST_MODE_EXTERN) ) if ( (mode & GPGME_KEYLIST_MODE_EXTERN) )
@ -2379,6 +2385,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
? "--check-sigs" : "--list-keys")); ? "--check-sigs" : "--list-keys"));
} }
} }
if (!err) if (!err)
err = add_arg (gpg, "--"); err = add_arg (gpg, "--");

View File

@ -411,6 +411,7 @@ gpgme_protocol_t;
#define GPGME_KEYLIST_MODE_SIGS 4 #define GPGME_KEYLIST_MODE_SIGS 4
#define GPGME_KEYLIST_MODE_SIG_NOTATIONS 8 #define GPGME_KEYLIST_MODE_SIG_NOTATIONS 8
#define GPGME_KEYLIST_MODE_WITH_SECRET 16 #define GPGME_KEYLIST_MODE_WITH_SECRET 16
#define GPGME_KEYLIST_MODE_WITH_TOFU 32
#define GPGME_KEYLIST_MODE_EPHEMERAL 128 #define GPGME_KEYLIST_MODE_EPHEMERAL 128
#define GPGME_KEYLIST_MODE_VALIDATE 256 #define GPGME_KEYLIST_MODE_VALIDATE 256
@ -843,7 +844,7 @@ struct _gpgme_user_id
* NULL is stored. */ * NULL is stored. */
char *address; char *address;
/* The malloced tofo information or NULL. */ /* The malloced TOFU information or NULL. */
gpgme_tofu_info_t tofu; gpgme_tofu_info_t tofu;
}; };
typedef struct _gpgme_user_id *gpgme_user_id_t; typedef struct _gpgme_user_id *gpgme_user_id_t;

View File

@ -33,6 +33,7 @@
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
/* Suppress warning for accessing deprecated member "class". */ /* Suppress warning for accessing deprecated member "class". */
#define _GPGME_IN_GPGME #define _GPGME_IN_GPGME
@ -403,6 +404,84 @@ parse_sec_field15 (gpgme_key_t key, gpgme_subkey_t subkey, char *field)
} }
/* Parse a tfs record. */
static gpg_error_t
parse_tfs_record (gpgme_user_id_t uid, char **field, int nfield)
{
gpg_error_t err;
gpgme_tofu_info_t ti;
unsigned long uval;
/* We add only the first TOFU record in case future versions emit
* several. */
if (uid->tofu)
return 0;
/* Check that we have enough fields and that the version is supported. */
if (nfield < 8 || atoi(field[1]) != 1)
return trace_gpg_error (GPG_ERR_INV_ENGINE);
ti = calloc (1, sizeof *ti);
if (!ti)
return gpg_error_from_syserror ();
/* Note that we allow a value of up to 7 which is what we can store
* in the ti->validity. */
err = _gpgme_strtoul_field (field[2], &uval);
if (err || uval > 7)
goto inv_engine;
ti->validity = uval;
/* Parse the sign-count. */
err = _gpgme_strtoul_field (field[3], &uval);
if (err)
goto inv_engine;
if (uval > USHRT_MAX)
uval = USHRT_MAX;
ti->signcount = uval;
/* Parse the encr-count. */
err = _gpgme_strtoul_field (field[4], &uval);
if (err)
goto inv_engine;
if (uval > USHRT_MAX)
uval = USHRT_MAX;
ti->encrcount = uval;
/* Parse the policy. */
if (!strcmp (field[5], "none"))
ti->policy = GPGME_TOFU_POLICY_NONE;
else if (!strcmp (field[5], "auto"))
ti->policy = GPGME_TOFU_POLICY_AUTO;
else if (!strcmp (field[5], "good"))
ti->policy = GPGME_TOFU_POLICY_GOOD;
else if (!strcmp (field[5], "bad"))
ti->policy = GPGME_TOFU_POLICY_BAD;
else if (!strcmp (field[5], "ask"))
ti->policy = GPGME_TOFU_POLICY_ASK;
else /* "unknown" and invalid policy strings. */
ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
/* Parse first and last seen timestamps. */
err = _gpgme_strtoul_field (field[6], &uval);
if (err)
goto inv_engine;
ti->firstseen = uval;
err = _gpgme_strtoul_field (field[7], &uval);
if (err)
goto inv_engine;
ti->lastseen = uval;
/* Ready. */
uid->tofu = ti;
return 0;
inv_engine:
free (ti);
return trace_gpg_error (GPG_ERR_INV_ENGINE);
}
/* We have read an entire key into tmp_key and should now finish it. /* We have read an entire key into tmp_key and should now finish it.
It is assumed that this releases tmp_key. */ It is assumed that this releases tmp_key. */
static void static void
@ -426,7 +505,7 @@ keylist_colon_handler (void *priv, char *line)
gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
enum enum
{ {
RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_GRP, RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP,
RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK
} }
rectype = RT_NONE; rectype = RT_NONE;
@ -483,6 +562,8 @@ keylist_colon_handler (void *priv, char *line)
rectype = RT_GRP; rectype = RT_GRP;
else if (!strcmp (field[0], "uid") && key) else if (!strcmp (field[0], "uid") && key)
rectype = RT_UID; rectype = RT_UID;
else if (!strcmp (field[0], "tfs") && key)
rectype = RT_TFS;
else if (!strcmp (field[0], "sub") && key) else if (!strcmp (field[0], "sub") && key)
rectype = RT_SUB; rectype = RT_SUB;
else if (!strcmp (field[0], "ssb") && key) else if (!strcmp (field[0], "ssb") && key)
@ -492,10 +573,10 @@ keylist_colon_handler (void *priv, char *line)
else else
rectype = RT_NONE; rectype = RT_NONE;
/* Only look at signatures immediately following a user ID. For /* Only look at signature and trust info records immediately
this, clear the user ID pointer when encountering anything but a following a user ID. For this, clear the user ID pointer when
signature. */ encountering anything but a signature or trust record. */
if (rectype != RT_SIG && rectype != RT_REV) if (rectype != RT_SIG && rectype != RT_REV && rectype != RT_TFS)
opd->tmp_uid = NULL; opd->tmp_uid = NULL;
/* Only look at subpackets immediately following a signature. For /* Only look at subpackets immediately following a signature. For
@ -695,6 +776,15 @@ keylist_colon_handler (void *priv, char *line)
} }
break; break;
case RT_TFS:
if (opd->tmp_uid)
{
err = parse_tfs_record (opd->tmp_uid, field, fields);
if (err)
return err;
}
break;
case RT_FPR: case RT_FPR:
/* Field 10 has the fingerprint (take only the first one). */ /* Field 10 has the fingerprint (take only the first one). */
if (fields >= 10 && field[9] && *field[9]) if (fields >= 10 && field[9] && *field[9])

View File

@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <gpgme.h> #include <gpgme.h>
@ -49,6 +50,7 @@ show_usage (int ex)
" --local use GPGME_KEYLIST_MODE_LOCAL\n" " --local use GPGME_KEYLIST_MODE_LOCAL\n"
" --extern use GPGME_KEYLIST_MODE_EXTERN\n" " --extern use GPGME_KEYLIST_MODE_EXTERN\n"
" --sigs use GPGME_KEYLIST_MODE_SIGS\n" " --sigs use GPGME_KEYLIST_MODE_SIGS\n"
" --tofu use GPGME_KEYLIST_MODE_TOFU\n"
" --sig-notations use GPGME_KEYLIST_MODE_SIG_NOTATIONS\n" " --sig-notations use GPGME_KEYLIST_MODE_SIG_NOTATIONS\n"
" --ephemeral use GPGME_KEYLIST_MODE_EPHEMERAL\n" " --ephemeral use GPGME_KEYLIST_MODE_EPHEMERAL\n"
" --validate use GPGME_KEYLIST_MODE_VALIDATE\n" " --validate use GPGME_KEYLIST_MODE_VALIDATE\n"
@ -60,6 +62,26 @@ show_usage (int ex)
} }
static const char *
isotimestr (unsigned long value)
{
time_t t;
static char buffer[25+5];
struct tm *tp;
if (!value)
return "none";
t = value;
tp = gmtime (&t);
snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec);
return buffer;
}
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
@ -120,6 +142,11 @@ main (int argc, char **argv)
mode |= GPGME_KEYLIST_MODE_EXTERN; mode |= GPGME_KEYLIST_MODE_EXTERN;
argc--; argv++; argc--; argv++;
} }
else if (!strcmp (*argv, "--tofu"))
{
mode |= GPGME_KEYLIST_MODE_WITH_TOFU;
argc--; argv++;
}
else if (!strcmp (*argv, "--sigs")) else if (!strcmp (*argv, "--sigs"))
{ {
mode |= GPGME_KEYLIST_MODE_SIGS; mode |= GPGME_KEYLIST_MODE_SIGS;
@ -181,6 +208,7 @@ main (int argc, char **argv)
while (!(err = gpgme_op_keylist_next (ctx, &key))) while (!(err = gpgme_op_keylist_next (ctx, &key)))
{ {
gpgme_user_id_t uid; gpgme_user_id_t uid;
gpgme_tofu_info_t ti;
int nuids; int nuids;
int nsub; int nsub;
@ -233,23 +261,41 @@ main (int argc, char **argv)
for (nuids=0, uid=key->uids; uid; uid = uid->next, nuids++) for (nuids=0, uid=key->uids; uid; uid = uid->next, nuids++)
{ {
printf ("userid %d: %s\n", nuids, nonnull(uid->uid)); printf ("userid %d: %s\n", nuids, nonnull(uid->uid));
printf (" mbox %d: %s\n", nuids, nonnull(uid->address)); printf (" mbox: %s\n", nonnull(uid->address));
if (uid->email && uid->email != uid->address) if (uid->email && uid->email != uid->address)
printf (" email %d: %s\n", nuids, uid->email); printf (" email: %s\n", uid->email);
if (uid->name) if (uid->name)
printf (" name %d: %s\n", nuids, uid->name); printf (" name: %s\n", uid->name);
if (uid->comment) if (uid->comment)
printf (" cmmnt %d: %s\n", nuids, uid->comment); printf (" cmmnt: %s\n", uid->comment);
printf (" valid %d: %s\n", nuids, printf (" valid: %s\n",
uid->validity == GPGME_VALIDITY_UNKNOWN? "unknown": uid->validity == GPGME_VALIDITY_UNKNOWN? "unknown":
uid->validity == GPGME_VALIDITY_UNDEFINED? "undefined": uid->validity == GPGME_VALIDITY_UNDEFINED? "undefined":
uid->validity == GPGME_VALIDITY_NEVER? "never": uid->validity == GPGME_VALIDITY_NEVER? "never":
uid->validity == GPGME_VALIDITY_MARGINAL? "marginal": uid->validity == GPGME_VALIDITY_MARGINAL? "marginal":
uid->validity == GPGME_VALIDITY_FULL? "full": uid->validity == GPGME_VALIDITY_FULL? "full":
uid->validity == GPGME_VALIDITY_ULTIMATE? "ultimate": "[?]"); uid->validity == GPGME_VALIDITY_ULTIMATE? "ultimate": "[?]");
if ((ti = uid->tofu))
{
printf (" tofu: %u (%s)\n", ti->validity,
ti->validity == 0? "conflict" :
ti->validity == 1? "no history" :
ti->validity == 2? "little history" :
ti->validity == 3? "enough history" :
ti->validity == 4? "lot of history" : "?");
printf (" policy: %u (%s)\n", ti->policy,
ti->policy == GPGME_TOFU_POLICY_NONE? "none" :
ti->policy == GPGME_TOFU_POLICY_AUTO? "auto" :
ti->policy == GPGME_TOFU_POLICY_GOOD? "good" :
ti->policy == GPGME_TOFU_POLICY_UNKNOWN? "unknown" :
ti->policy == GPGME_TOFU_POLICY_BAD? "bad" :
ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?");
printf (" nsigs: %hu\n", ti->signcount);
printf (" nencr: %hu\n", ti->encrcount);
printf (" first: %s\n", isotimestr (ti->firstseen));
printf (" last: %s\n", isotimestr (ti->lastseen));
}
} }
putchar ('\n'); putchar ('\n');

View File

@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <gpgme.h> #include <gpgme.h>
@ -36,6 +37,26 @@
static int verbose; static int verbose;
static const char *
isotimestr (unsigned long value)
{
time_t t;
static char buffer[25+5];
struct tm *tp;
if (!value)
return "none";
t = value;
tp = gmtime (&t);
snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec);
return buffer;
}
static gpg_error_t static gpg_error_t
status_cb (void *opaque, const char *keyword, const char *value) status_cb (void *opaque, const char *keyword, const char *value)
{ {
@ -177,8 +198,8 @@ print_result (gpgme_verify_result_t result)
ti->policy == GPGME_TOFU_POLICY_BAD? "bad" : ti->policy == GPGME_TOFU_POLICY_BAD? "bad" :
ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?"); ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?");
printf (" sigcount : %hu\n", ti->signcount); printf (" sigcount : %hu\n", ti->signcount);
printf (" firstseen: %u\n", ti->firstseen); printf (" firstseen: %s\n", isotimestr (ti->firstseen));
printf (" lastseen : %u\n", ti->lastseen); printf (" lastseen : %s\n", isotimestr (ti->lastseen));
printf (" desc ....: "); printf (" desc ....: ");
print_description (nonnull (ti->description), 15); print_description (nonnull (ti->description), 15);
} }