diff options
| -rw-r--r-- | NEWS | 9 | ||||
| -rw-r--r-- | src/gpgme.h.in | 61 | ||||
| -rw-r--r-- | src/status-table.c | 3 | ||||
| -rw-r--r-- | src/verify.c | 196 | ||||
| -rw-r--r-- | tests/run-verify.c | 39 | 
5 files changed, 306 insertions, 2 deletions
| @@ -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] 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 diff --git a/tests/run-verify.c b/tests/run-verify.c index b7be3203..df8cbf65 100644 --- a/tests/run-verify.c +++ b/tests/run-verify.c @@ -94,9 +94,23 @@ 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) | 
