api: Return Tofu info for signatures.
* src/gpgme.h.in (gpgme_tofu_policy_t): New. (gpgme_status_code_t): Add status codes for TOFU. (struct _gpgme_tofu_info, gpgme_tofu_info_t): New. (struct _gpgme_signature): Add field 'tofu'. * src/status-table.c (status_table): Add new codes. * src/verify.c: Include limits.h. (release_tofu_info): New. (release_op_data): Call that. (parse_tofu_user): New. (parse_tofu_stats): New. (parse_tofu_stats_long): New. (_gpgme_verify_status_handler): Handle TOFU status lines. * tests/run-verify.c (print_description): New. (print_result): print tofu info. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
dac2c5441d
commit
10df06ee8f
9
NEWS
9
NEWS
@ -6,8 +6,15 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
|
||||
* Interface changes relative to the 1.6.0 release:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
gpgme_pubkey_algo_string NEW.
|
||||
gpgme_set_ctx_flag NEW.
|
||||
GPGME_PK_EDDSA NEW.
|
||||
gpgme_set_ctx_flag NEW.
|
||||
gpgme_signature_t EXTENDED: New field tofu.
|
||||
gpgme_tofu_policy_t NEW.
|
||||
gpgme_tofu_info_t NEW.
|
||||
GPGME_STATUS_KEY_CONSIDERED NEW.
|
||||
GPGME_STATUS_TOFU_USER NEW.
|
||||
GPGME_STATUS_TOFU_STATS NEW.
|
||||
GPGME_STATUS_TOFU_STATS_LONG NEW.
|
||||
|
||||
|
||||
Noteworthy changes in version 1.6.0 (2015-08-26) [C25/A14/R0]
|
||||
|
@ -371,6 +371,19 @@ typedef enum
|
||||
gpgme_validity_t;
|
||||
|
||||
|
||||
/* The TOFU policies. */
|
||||
typedef enum
|
||||
{
|
||||
GPGME_TOFU_POLICY_NONE = 0,
|
||||
GPGME_TOFU_POLICY_AUTO = 1,
|
||||
GPGME_TOFU_POLICY_GOOD = 2,
|
||||
GPGME_TOFU_POLICY_UNKNOWN = 3,
|
||||
GPGME_TOFU_POLICY_BAD = 4,
|
||||
GPGME_TOFU_POLICY_ASK = 5
|
||||
}
|
||||
gpgme_tofu_policy_t;
|
||||
|
||||
|
||||
/* The available protocols. */
|
||||
typedef enum
|
||||
{
|
||||
@ -533,7 +546,10 @@ typedef enum
|
||||
GPGME_STATUS_KEY_NOT_CREATED = 91,
|
||||
GPGME_STATUS_INQUIRE_MAXLEN = 92,
|
||||
GPGME_STATUS_FAILURE = 93,
|
||||
GPGME_STATUS_KEY_CONSIDERED = 94
|
||||
GPGME_STATUS_KEY_CONSIDERED = 94,
|
||||
GPGME_STATUS_TOFU_USER = 95,
|
||||
GPGME_STATUS_TOFU_STATS = 96,
|
||||
GPGME_STATUS_TOFU_STATS_LONG = 97
|
||||
}
|
||||
gpgme_status_code_t;
|
||||
|
||||
@ -1533,6 +1549,46 @@ typedef enum
|
||||
}
|
||||
gpgme_sigsum_t;
|
||||
|
||||
|
||||
struct _gpgme_tofu_info
|
||||
{
|
||||
struct _gpgme_tofu_info *next;
|
||||
|
||||
/* The mail address (addr-spec from RFC5322) of the tofu binding. */
|
||||
char *address;
|
||||
|
||||
/* The fingerprint of the primary key. */
|
||||
char *fpr;
|
||||
|
||||
/* The TOFU validity:
|
||||
* 0 := conflict
|
||||
* 1 := key without history
|
||||
* 2 := key with too little history
|
||||
* 3 := key with enough history for basic trust
|
||||
* 4 := key with a lot of history
|
||||
*/
|
||||
unsigned int validity : 3;
|
||||
|
||||
/* The TOFU policy (gpgme_tofu_policy_t). */
|
||||
unsigned int policy : 4;
|
||||
|
||||
unsigned int _rfu : 25;
|
||||
|
||||
/* Number of signatures seen for this binding. Capped at USHRT_MAX. */
|
||||
unsigned short signcount;
|
||||
unsigned short reserved;
|
||||
|
||||
/* Number of seconds since the first and the most recently seen
|
||||
* message was verified. */
|
||||
unsigned int firstseen;
|
||||
unsigned int lastseen;
|
||||
|
||||
/* If non-NULL a human readable string summarizing the TOFU data. */
|
||||
char *description;
|
||||
};
|
||||
typedef struct _gpgme_tofu_info *gpgme_tofu_info_t;
|
||||
|
||||
|
||||
struct _gpgme_signature
|
||||
{
|
||||
struct _gpgme_signature *next;
|
||||
@ -1578,6 +1634,9 @@ struct _gpgme_signature
|
||||
|
||||
/* The mailbox from the PKA information or NULL. */
|
||||
char *pka_address;
|
||||
|
||||
/* If non-NULL, TOFU info for this signature are available. */
|
||||
gpgme_tofu_info_t tofu;
|
||||
};
|
||||
typedef struct _gpgme_signature *gpgme_signature_t;
|
||||
|
||||
|
@ -124,6 +124,9 @@ static struct status_table_s status_table[] =
|
||||
{ "SIG_SUBPACKET", GPGME_STATUS_SIG_SUBPACKET },
|
||||
{ "SIGEXPIRED", GPGME_STATUS_SIGEXPIRED },
|
||||
{ "SUCCESS", GPGME_STATUS_SUCCESS },
|
||||
{ "TOFU_STATS", GPGME_STATUS_TOFU_STATS },
|
||||
{ "TOFU_STATS_LONG", GPGME_STATUS_TOFU_STATS_LONG },
|
||||
{ "TOFU_USER", GPGME_STATUS_TOFU_USER },
|
||||
{ "TRUNCATED", GPGME_STATUS_TRUNCATED },
|
||||
{ "TRUST_FULLY", GPGME_STATUS_TRUST_FULLY },
|
||||
{ "TRUST_MARGINAL", GPGME_STATUS_TRUST_MARGINAL },
|
||||
|
196
src/verify.c
196
src/verify.c
@ -26,6 +26,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "gpgme.h"
|
||||
#include "debug.h"
|
||||
@ -48,6 +49,22 @@ typedef struct
|
||||
} *op_data_t;
|
||||
|
||||
|
||||
static void
|
||||
release_tofu_info (gpgme_tofu_info_t t)
|
||||
{
|
||||
while (t)
|
||||
{
|
||||
gpgme_tofu_info_t t2 = t->next;
|
||||
|
||||
free (t->address);
|
||||
free (t->fpr);
|
||||
free (t->description);
|
||||
free (t);
|
||||
t = t2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
release_op_data (void *hook)
|
||||
{
|
||||
@ -71,6 +88,7 @@ release_op_data (void *hook)
|
||||
free (sig->fpr);
|
||||
if (sig->pka_address)
|
||||
free (sig->pka_address);
|
||||
release_tofu_info (sig->tofu);
|
||||
free (sig);
|
||||
sig = next;
|
||||
}
|
||||
@ -635,6 +653,169 @@ parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
|
||||
}
|
||||
|
||||
|
||||
/* Parse a TOFU_USER line and put the info into SIG. */
|
||||
static gpgme_error_t
|
||||
parse_tofu_user (gpgme_signature_t sig, char *args)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *tail;
|
||||
gpgme_tofu_info_t ti, ti2;
|
||||
|
||||
tail = strchr (args, ' ');
|
||||
if (!tail || tail == args)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No fingerprint. */
|
||||
*tail++ = 0;
|
||||
|
||||
ti = calloc (1, sizeof *ti);
|
||||
if (!ti)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
ti->fpr = strdup (args);
|
||||
if (!ti->fpr)
|
||||
{
|
||||
free (ti);
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
args = tail;
|
||||
tail = strchr (args, ' ');
|
||||
if (tail == args)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No addr-spec. */
|
||||
if (tail)
|
||||
*tail = 0;
|
||||
|
||||
err = _gpgme_decode_percent_string (args, &ti->address, 0, 0);
|
||||
if (err)
|
||||
{
|
||||
free (ti);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Append to the tofu info list. */
|
||||
if (!sig->tofu)
|
||||
sig->tofu = ti;
|
||||
else
|
||||
{
|
||||
for (ti2 = sig->tofu; ti2->next; ti2 = ti2->next)
|
||||
;
|
||||
ti2->next = ti;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Parse a TOFU_STATS line and store it in the last tofu info of SIG.
|
||||
*
|
||||
* TOFU_STATS <validity> <sign-count> 0 [<policy> [<tm1> <tm2>]]
|
||||
*/
|
||||
static gpgme_error_t
|
||||
parse_tofu_stats (gpgme_signature_t sig, char *args)
|
||||
{
|
||||
gpgme_error_t err;
|
||||
gpgme_tofu_info_t ti;
|
||||
char *field[6];
|
||||
int nfields;
|
||||
unsigned long uval;
|
||||
|
||||
if (!sig->tofu)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */
|
||||
for (ti = sig->tofu; ti->next; ti = ti->next)
|
||||
;
|
||||
if (ti->firstseen || ti->signcount || ti->validity || ti->policy)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already seen. */
|
||||
|
||||
nfields = _gpgme_split_fields (args, field, DIM (field));
|
||||
if (nfields < 3)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required args missing. */
|
||||
|
||||
/* 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[0], &uval);
|
||||
if (err || uval > 7)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE);
|
||||
ti->validity = uval;
|
||||
|
||||
/* Parse the sign-count. */
|
||||
err = _gpgme_strtoul_field (field[1], &uval);
|
||||
if (err)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE);
|
||||
if (uval > USHRT_MAX)
|
||||
uval = USHRT_MAX;
|
||||
ti->signcount = uval;
|
||||
|
||||
/* We skip the 0, which is RFU. */
|
||||
|
||||
if (nfields == 3)
|
||||
return 0; /* All mandatory fields parsed. */
|
||||
|
||||
/* Parse the policy. */
|
||||
if (!strcmp (field[3], "none"))
|
||||
ti->policy = GPGME_TOFU_POLICY_NONE;
|
||||
else if (!strcmp (field[3], "auto"))
|
||||
ti->policy = GPGME_TOFU_POLICY_AUTO;
|
||||
else if (!strcmp (field[3], "good"))
|
||||
ti->policy = GPGME_TOFU_POLICY_GOOD;
|
||||
else if (!strcmp (field[3], "bad"))
|
||||
ti->policy = GPGME_TOFU_POLICY_BAD;
|
||||
else if (!strcmp (field[3], "ask"))
|
||||
ti->policy = GPGME_TOFU_POLICY_ASK;
|
||||
else /* "unknown" and invalid policy strings. */
|
||||
ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
|
||||
|
||||
if (nfields == 4)
|
||||
return 0; /* No more optional fields. */
|
||||
|
||||
/* Parse first and last seen (none or both are required). */
|
||||
if (nfields < 6)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* "tm2" missing. */
|
||||
err = _gpgme_strtoul_field (field[4], &uval);
|
||||
if (err)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE);
|
||||
if (uval > UINT_MAX)
|
||||
uval = UINT_MAX;
|
||||
ti->firstseen = uval;
|
||||
err = _gpgme_strtoul_field (field[5], &uval);
|
||||
if (err)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE);
|
||||
if (uval > UINT_MAX)
|
||||
uval = UINT_MAX;
|
||||
ti->lastseen = uval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Parse a TOFU_STATS_LONG line and store it in the last tofu info of SIG. */
|
||||
static gpgme_error_t
|
||||
parse_tofu_stats_long (gpgme_signature_t sig, char *args, int raw)
|
||||
{
|
||||
gpgme_error_t err;
|
||||
gpgme_tofu_info_t ti;
|
||||
char *p;
|
||||
|
||||
if (!sig->tofu)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */
|
||||
for (ti = sig->tofu; ti->next; ti = ti->next)
|
||||
;
|
||||
if (ti->description)
|
||||
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already seen. */
|
||||
|
||||
err = _gpgme_decode_percent_string (args, &ti->description, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Remove the non-breaking spaces. */
|
||||
if (!raw)
|
||||
{
|
||||
for (p = ti->description; *p; p++)
|
||||
if (*p == '~')
|
||||
*p = ' ';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Parse an error status line and if SET_STATUS is true update the
|
||||
result status as appropriate. With SET_STATUS being false, only
|
||||
check for an error. */
|
||||
@ -766,6 +947,21 @@ _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
|
||||
sig->pka_address = strdup (args);
|
||||
break;
|
||||
|
||||
case GPGME_STATUS_TOFU_USER:
|
||||
opd->only_newsig_seen = 0;
|
||||
return sig ? parse_tofu_user (sig, args)
|
||||
/* */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
|
||||
|
||||
case GPGME_STATUS_TOFU_STATS:
|
||||
opd->only_newsig_seen = 0;
|
||||
return sig ? parse_tofu_stats (sig, args)
|
||||
/* */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
|
||||
|
||||
case GPGME_STATUS_TOFU_STATS_LONG:
|
||||
opd->only_newsig_seen = 0;
|
||||
return sig ? parse_tofu_stats_long (sig, args, ctx->raw_description)
|
||||
/* */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
|
||||
|
||||
case GPGME_STATUS_ERROR:
|
||||
opd->only_newsig_seen = 0;
|
||||
/* Some error stati are informational, so we don't return an
|
||||
|
@ -93,10 +93,24 @@ print_validity (gpgme_validity_t val)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_description (const char *text, int indent)
|
||||
{
|
||||
for (; *text; text++)
|
||||
{
|
||||
putchar (*text);
|
||||
if (*text == '\n')
|
||||
printf ("%*s", indent, "");
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_result (gpgme_verify_result_t result)
|
||||
{
|
||||
gpgme_signature_t sig;
|
||||
gpgme_tofu_info_t ti;
|
||||
int count = 0;
|
||||
|
||||
printf ("Original file name: %s\n", nonnull(result->file_name));
|
||||
@ -126,6 +140,30 @@ print_result (gpgme_verify_result_t result)
|
||||
);
|
||||
printf (" notations .: %s\n",
|
||||
sig->notations? "yes":"no");
|
||||
for (ti = sig->tofu; ti; ti = ti->next)
|
||||
{
|
||||
printf (" tofu addr .: %s\n", ti->address);
|
||||
if (!sig->fpr || strcmp (sig->fpr, ti->fpr))
|
||||
printf (" WARNING .: fpr mismatch (%s)\n", ti->fpr);
|
||||
printf (" validity : %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 (" sigcount : %hu\n", ti->signcount);
|
||||
printf (" firstseen: %u\n", ti->firstseen);
|
||||
printf (" lastseen : %u\n", ti->lastseen);
|
||||
printf (" desc ....: ");
|
||||
print_description (nonnull (ti->description), 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,6 +268,7 @@ main (int argc, char **argv)
|
||||
gpgme_set_status_cb (ctx, status_cb, NULL);
|
||||
gpgme_set_ctx_flag (ctx, "full-status", "1");
|
||||
}
|
||||
/* gpgme_set_ctx_flag (ctx, "raw-description", "1"); */
|
||||
|
||||
err = gpgme_data_new_from_stream (&sig, fp_sig);
|
||||
if (err)
|
||||
|
Loading…
Reference in New Issue
Block a user