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:
|
* Interface changes relative to the 1.6.0 release:
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
gpgme_pubkey_algo_string NEW.
|
gpgme_pubkey_algo_string NEW.
|
||||||
gpgme_set_ctx_flag NEW.
|
|
||||||
GPGME_PK_EDDSA 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]
|
Noteworthy changes in version 1.6.0 (2015-08-26) [C25/A14/R0]
|
||||||
|
@ -371,6 +371,19 @@ typedef enum
|
|||||||
gpgme_validity_t;
|
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. */
|
/* The available protocols. */
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -533,7 +546,10 @@ typedef enum
|
|||||||
GPGME_STATUS_KEY_NOT_CREATED = 91,
|
GPGME_STATUS_KEY_NOT_CREATED = 91,
|
||||||
GPGME_STATUS_INQUIRE_MAXLEN = 92,
|
GPGME_STATUS_INQUIRE_MAXLEN = 92,
|
||||||
GPGME_STATUS_FAILURE = 93,
|
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;
|
gpgme_status_code_t;
|
||||||
|
|
||||||
@ -1533,6 +1549,46 @@ typedef enum
|
|||||||
}
|
}
|
||||||
gpgme_sigsum_t;
|
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
|
||||||
{
|
{
|
||||||
struct _gpgme_signature *next;
|
struct _gpgme_signature *next;
|
||||||
@ -1578,6 +1634,9 @@ struct _gpgme_signature
|
|||||||
|
|
||||||
/* The mailbox from the PKA information or NULL. */
|
/* The mailbox from the PKA information or NULL. */
|
||||||
char *pka_address;
|
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;
|
typedef struct _gpgme_signature *gpgme_signature_t;
|
||||||
|
|
||||||
|
@ -124,6 +124,9 @@ static struct status_table_s status_table[] =
|
|||||||
{ "SIG_SUBPACKET", GPGME_STATUS_SIG_SUBPACKET },
|
{ "SIG_SUBPACKET", GPGME_STATUS_SIG_SUBPACKET },
|
||||||
{ "SIGEXPIRED", GPGME_STATUS_SIGEXPIRED },
|
{ "SIGEXPIRED", GPGME_STATUS_SIGEXPIRED },
|
||||||
{ "SUCCESS", GPGME_STATUS_SUCCESS },
|
{ "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 },
|
{ "TRUNCATED", GPGME_STATUS_TRUNCATED },
|
||||||
{ "TRUST_FULLY", GPGME_STATUS_TRUST_FULLY },
|
{ "TRUST_FULLY", GPGME_STATUS_TRUST_FULLY },
|
||||||
{ "TRUST_MARGINAL", GPGME_STATUS_TRUST_MARGINAL },
|
{ "TRUST_MARGINAL", GPGME_STATUS_TRUST_MARGINAL },
|
||||||
|
196
src/verify.c
196
src/verify.c
@ -26,6 +26,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include "gpgme.h"
|
#include "gpgme.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
@ -48,6 +49,22 @@ typedef struct
|
|||||||
} *op_data_t;
|
} *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
|
static void
|
||||||
release_op_data (void *hook)
|
release_op_data (void *hook)
|
||||||
{
|
{
|
||||||
@ -71,6 +88,7 @@ release_op_data (void *hook)
|
|||||||
free (sig->fpr);
|
free (sig->fpr);
|
||||||
if (sig->pka_address)
|
if (sig->pka_address)
|
||||||
free (sig->pka_address);
|
free (sig->pka_address);
|
||||||
|
release_tofu_info (sig->tofu);
|
||||||
free (sig);
|
free (sig);
|
||||||
sig = next;
|
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
|
/* Parse an error status line and if SET_STATUS is true update the
|
||||||
result status as appropriate. With SET_STATUS being false, only
|
result status as appropriate. With SET_STATUS being false, only
|
||||||
check for an error. */
|
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);
|
sig->pka_address = strdup (args);
|
||||||
break;
|
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:
|
case GPGME_STATUS_ERROR:
|
||||||
opd->only_newsig_seen = 0;
|
opd->only_newsig_seen = 0;
|
||||||
/* Some error stati are informational, so we don't return an
|
/* 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
|
static void
|
||||||
print_result (gpgme_verify_result_t result)
|
print_result (gpgme_verify_result_t result)
|
||||||
{
|
{
|
||||||
gpgme_signature_t sig;
|
gpgme_signature_t sig;
|
||||||
|
gpgme_tofu_info_t ti;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
printf ("Original file name: %s\n", nonnull(result->file_name));
|
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",
|
printf (" notations .: %s\n",
|
||||||
sig->notations? "yes":"no");
|
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_status_cb (ctx, status_cb, NULL);
|
||||||
gpgme_set_ctx_flag (ctx, "full-status", "1");
|
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);
|
err = gpgme_data_new_from_stream (&sig, fp_sig);
|
||||||
if (err)
|
if (err)
|
||||||
|
Loading…
Reference in New Issue
Block a user