diff options
author | Werner Koch <[email protected]> | 2016-05-21 08:29:49 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2016-05-21 08:32:51 +0000 |
commit | 10df06ee8f9192309bf124872438f7c32457e1c6 (patch) | |
tree | fa06a7511fea47cf784f9ac0e003bf04ecb0c27c /src | |
parent | api: Add new context flag "raw-description". (diff) | |
download | gpgme-10df06ee8f9192309bf124872438f7c32457e1c6.tar.gz gpgme-10df06ee8f9192309bf124872438f7c32457e1c6.zip |
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 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/gpgme.h.in | 61 | ||||
-rw-r--r-- | src/status-table.c | 3 | ||||
-rw-r--r-- | src/verify.c | 196 |
3 files changed, 259 insertions, 1 deletions
diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 5f7896de..335ed6b5 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -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; diff --git a/src/status-table.c b/src/status-table.c index e70cb8bd..5850a361 100644 --- a/src/status-table.c +++ b/src/status-table.c @@ -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 }, diff --git a/src/verify.c b/src/verify.c index 4781d995..e6c9665f 100644 --- a/src/verify.c +++ b/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" @@ -49,6 +50,22 @@ typedef struct 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) { op_data_t opd = (op_data_t) 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 |