core: New function gpgme_op_tofu_policy
authorWerner Koch <wk@gnupg.org>
Wed, 14 Sep 2016 18:21:19 +0000 (20:21 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 14 Sep 2016 18:21:19 +0000 (20:21 +0200)
* src/gpgme.h.in (gpgme_op_tofu_policy_start): New function.
(gpgme_op_tofu_policy): New function.
* src/libgpgme.vers, src/gpgme.def: Add new functions.
* src/tofupolicy.c: New.
* src/Makefile.am (main_sources): Add that file.
* src/context.h (ctx_op_data_id_t): Add OPDATA_TOFU_POLICY.
* src/engine.c (_gpgme_engine_op_tofu_policy): New.
* src/engine-backend.h (engine_ops): Add funcptr 'tofu_policy'.
Adjust all engine initializations.
* src/engine-gpg.c (gpg_tofu_policy): New.
(_gpgme_engine_ops_gpg): Register this function.

* tests/run-tofu.c: New.
* tests/Makefile.am (noinst_PROGRAMS): Add it.

Signed-off-by: Werner Koch <wk@gnupg.org>
19 files changed:
NEWS
src/Makefile.am
src/context.h
src/engine-assuan.c
src/engine-backend.h
src/engine-g13.c
src/engine-gpg.c
src/engine-gpgconf.c
src/engine-gpgsm.c
src/engine-spawn.c
src/engine-uiserver.c
src/engine.c
src/engine.h
src/gpgme.def
src/gpgme.h.in
src/libgpgme.vers
src/tofupolicy.c [new file with mode: 0644]
tests/Makefile.am
tests/run-tofu.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index fd9b20c..6499f5e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,8 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
  gpgme_op_revuid                NEW.
  gpgme_op_keysign_start         NEW.
  gpgme_op_keysign               NEW.
+ gpgme_op_tofu_policy_start     NEW.
+ gpgme_op_tofu_policy           NEW.
  gpgme_genkey_result_t          EXTENDED: New fields pubkey and seckey.
  gpgme_signature_t              EXTENDED: New field key.
  gpgme_key_t                    EXTENDED: New field fpr.
index 39752b3..c57ec8f 100644 (file)
@@ -84,7 +84,7 @@ main_sources =                                                                \
        op-support.c                                                    \
        encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c    \
        sign.c passphrase.c progress.c                                  \
-       key.c keylist.c keysign.c trust-item.c trustlist.c              \
+       key.c keylist.c keysign.c trust-item.c trustlist.c tofupolicy.c \
        import.c export.c genkey.c delete.c edit.c getauditlog.c        \
        opassuan.c passwd.c spawn.c assuan-support.c                    \
        engine.h engine-backend.h engine.c engine-gpg.c status-table.c  \
index c099d66..4b12c3b 100644 (file)
@@ -38,7 +38,7 @@ typedef enum
     OPDATA_DECRYPT, OPDATA_SIGN, OPDATA_ENCRYPT, OPDATA_PASSPHRASE,
     OPDATA_IMPORT, OPDATA_GENKEY, OPDATA_KEYLIST, OPDATA_EDIT,
     OPDATA_VERIFY, OPDATA_TRUSTLIST, OPDATA_ASSUAN, OPDATA_VFS_MOUNT,
-    OPDATA_PASSWD, OPDATA_EXPORT, OPDATA_KEYSIGN
+    OPDATA_PASSWD, OPDATA_EXPORT, OPDATA_KEYSIGN, OPDATA_TOFU_POLICY
   } ctx_op_data_id_t;
 
 
index 6f11cc0..f5e202a 100644 (file)
@@ -776,6 +776,7 @@ struct engine_ops _gpgme_engine_ops_assuan =
     NULL,               /* keylist */
     NULL,               /* keylist_ext */
     NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     NULL,               /* sign */
     NULL,              /* trustlist */
     NULL,               /* verify */
index ed3e303..ccab0e3 100644 (file)
@@ -102,6 +102,9 @@ struct engine_ops
                             gpgme_key_t key, const char *userid,
                             unsigned long expires, unsigned int flags,
                             gpgme_ctx_t ctx);
+  gpgme_error_t (*tofu_policy) (void *engine,
+                                gpgme_key_t key,
+                                gpgme_tofu_policy_t policy);
   gpgme_error_t (*sign) (void *engine, gpgme_data_t in, gpgme_data_t out,
                         gpgme_sig_mode_t mode, int use_armor,
                         int use_textmode, int include_certs,
index 0da00f7..313e2ad 100644 (file)
@@ -793,6 +793,7 @@ struct engine_ops _gpgme_engine_ops_g13 =
     NULL,               /* keylist */
     NULL,               /* keylist_ext */
     NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     NULL,               /* sign */
     NULL,              /* trustlist */
     NULL,               /* verify */
index ac85c4d..9a0dab0 100644 (file)
@@ -2733,6 +2733,46 @@ gpg_keysign (void *engine, gpgme_key_t key, const char *userid,
 
 
 static gpgme_error_t
+gpg_tofu_policy (void *engine, gpgme_key_t key, gpgme_tofu_policy_t policy)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t err;
+  const char *policystr = NULL;
+
+  if (!key || !key->fpr)
+    return gpg_error (GPG_ERR_INV_ARG);
+
+  switch (policy)
+    {
+    case GPGME_TOFU_POLICY_NONE:                           break;
+    case GPGME_TOFU_POLICY_AUTO:    policystr = "auto";    break;
+    case GPGME_TOFU_POLICY_GOOD:    policystr = "good";    break;
+    case GPGME_TOFU_POLICY_BAD:     policystr = "bad";     break;
+    case GPGME_TOFU_POLICY_ASK:     policystr = "ask";     break;
+    case GPGME_TOFU_POLICY_UNKNOWN: policystr = "unknown"; break;
+    }
+  if (!policystr)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!have_gpg_version (gpg, "2.1.10"))
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+  err = add_arg (gpg, "--tofu-policy");
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    err = add_arg (gpg, policystr);
+  if (!err)
+    err = add_arg (gpg, key->fpr);
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
+}
+
+
+static gpgme_error_t
 gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
          gpgme_sig_mode_t mode, int use_armor, int use_textmode,
          int include_certs, gpgme_ctx_t ctx /* FIXME */)
@@ -2906,6 +2946,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_keylist,
     gpg_keylist_ext,
     gpg_keysign,
+    gpg_tofu_policy,    /* tofu_policy */
     gpg_sign,
     gpg_trustlist,
     gpg_verify,
index 8be76cb..90f32c7 100644 (file)
@@ -958,6 +958,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
     NULL,              /* keylist */
     NULL,              /* keylist_ext */
     NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     NULL,              /* sign */
     NULL,              /* trustlist */
     NULL,              /* verify */
index aae9d28..5fcfbf1 100644 (file)
@@ -2075,6 +2075,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     gpgsm_keylist,
     gpgsm_keylist_ext,
     NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     gpgsm_sign,
     NULL,              /* trustlist */
     gpgsm_verify,
index 82dbc0b..df90cb2 100644 (file)
@@ -461,6 +461,7 @@ struct engine_ops _gpgme_engine_ops_spawn =
     NULL,              /* keylist */
     NULL,              /* keylist_ext */
     NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     NULL,              /* sign */
     NULL,              /* trustlist */
     NULL,              /* verify */
index 827c347..318d32e 100644 (file)
@@ -1365,6 +1365,7 @@ struct engine_ops _gpgme_engine_ops_uiserver =
     NULL,              /* keylist */
     NULL,              /* keylist_ext */
     NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     uiserver_sign,
     NULL,              /* trustlist */
     uiserver_verify,
index 47bb23c..a1173a0 100644 (file)
@@ -811,6 +811,20 @@ _gpgme_engine_op_keysign (engine_t engine, gpgme_key_t key, const char *userid,
 
 
 gpgme_error_t
+_gpgme_engine_op_tofu_policy (engine_t engine,
+                              gpgme_key_t key,  gpgme_tofu_policy_t policy)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->tofu_policy)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->tofu_policy) (engine->engine, key, policy);
+}
+
+
+gpgme_error_t
 _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
                          gpgme_key_t *keyarray)
 {
index 6f33835..4ce2bed 100644 (file)
@@ -126,6 +126,9 @@ gpgme_error_t _gpgme_engine_op_keysign (engine_t engine,
                                         unsigned long expires,
                                         unsigned int flags,
                                         gpgme_ctx_t ctx);
+gpgme_error_t _gpgme_engine_op_tofu_policy (engine_t engine,
+                                            gpgme_key_t key,
+                                            gpgme_tofu_policy_t policy);
 gpgme_error_t _gpgme_engine_op_import (engine_t engine,
                                       gpgme_data_t keydata,
                                        gpgme_key_t *keyarray);
index f987b38..7882af6 100644 (file)
@@ -239,6 +239,8 @@ EXPORTS
     gpgme_op_revuid                       @179
     gpgme_op_keysign_start                @180
     gpgme_op_keysign                      @181
+    gpgme_op_tofu_policy_start            @182
+    gpgme_op_tofu_policy                  @183
 
 ; END
 
index 121e2ce..5ed0890 100644 (file)
@@ -1968,6 +1968,16 @@ gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key,
                                  gpgme_data_t out);
 
 
+/* Set the Tofu policy of KEY to POLCIY.  */
+gpgme_error_t gpgme_op_tofu_policy_start (gpgme_ctx_t ctx,
+                                          gpgme_key_t key,
+                                          gpgme_tofu_policy_t policy);
+gpgme_error_t gpgme_op_tofu_policy       (gpgme_ctx_t ctx,
+                                          gpgme_key_t key,
+                                          gpgme_tofu_policy_t policy);
+
+
+
 \f
 /*
  * Key listing
index d86eee8..d635b6b 100644 (file)
@@ -113,6 +113,8 @@ GPGME_1.1 {
     gpgme_op_revuid;
     gpgme_op_keysign_start;
     gpgme_op_keysign;
+    gpgme_op_tofu_policy_start;
+    gpgme_op_tofu_policy;
 };
 
 
diff --git a/src/tofupolicy.c b/src/tofupolicy.c
new file mode 100644 (file)
index 0000000..799779e
--- /dev/null
@@ -0,0 +1,184 @@
+/* tofupolicy.c - Tofu policy helpers.
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+typedef struct
+{
+  /* The error code from a FAILURE status line or 0.  */
+  gpg_error_t failure_code;
+
+  /* The error code from an ERROR status line or 0.  */
+  gpg_error_t error_code;
+
+} *op_data_t;
+
+
+\f
+/* Parse an error status line.  Return the error location and the
+   error code.  The function may modify ARGS. */
+static char *
+parse_error (char *args, gpg_error_t *r_err)
+{
+  char *where = strchr (args, ' ');
+  char *which;
+
+  if (where)
+    {
+      *where = '\0';
+      which = where + 1;
+
+      where = strchr (which, ' ');
+      if (where)
+       *where = '\0';
+
+      where = args;
+    }
+  else
+    {
+      *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE);
+      return NULL;
+    }
+
+  *r_err = atoi (which);
+
+  return where;
+}
+
+
+static gpgme_error_t
+tofu_policy_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+  gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+  gpgme_error_t err;
+  void *hook;
+  op_data_t opd;
+  char *loc;
+
+  err = _gpgme_op_data_lookup (ctx, OPDATA_TOFU_POLICY, &hook, -1, NULL);
+  opd = hook;
+  if (err)
+    return err;
+
+  switch (code)
+    {
+    case GPGME_STATUS_ERROR:
+      loc = parse_error (args, &err);
+      if (!loc)
+        return err;
+      if (!opd->error_code)
+        opd->error_code = err;
+      break;
+
+    case GPGME_STATUS_FAILURE:
+      opd->failure_code = _gpgme_parse_failure (args);
+      break;
+
+    case GPGME_STATUS_EOF:
+      if (opd->error_code)
+        err = opd->error_code;
+      else if (opd->failure_code)
+        err = opd->failure_code;
+      break;
+
+    default:
+      break;
+    }
+
+  return err;
+}
+
+
+/* Set the TOFU policy for KEY to POLICY.  */
+static gpgme_error_t
+tofu_policy_start (gpgme_ctx_t ctx, int synchronous,
+                   gpgme_key_t key, gpgme_tofu_policy_t policy)
+{
+  gpgme_error_t err;
+  void *hook;
+  op_data_t opd;
+
+  if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
+    return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+  if (!key)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  err = _gpgme_op_reset (ctx, synchronous);
+  if (err)
+    return err;
+
+  err = _gpgme_op_data_lookup (ctx, OPDATA_TOFU_POLICY, &hook,
+                               sizeof (*opd), NULL);
+  opd = hook;
+  if (err)
+    return err;
+
+  _gpgme_engine_set_status_handler (ctx->engine, tofu_policy_status_handler,
+                                    ctx);
+
+  return _gpgme_engine_op_tofu_policy (ctx->engine, key, policy);
+}
+
+
+
+/* Set the TOFU policy of KEY to POLCIY.  This is the asynchronous
+ * variant.  */
+gpgme_error_t
+gpgme_op_tofu_policy_start (gpgme_ctx_t ctx,
+                            gpgme_key_t key, gpgme_tofu_policy_t policy)
+{
+  gpg_error_t err;
+  TRACE_BEG2 (DEBUG_CTX, "gpgme_op_tofu_policy_start", ctx,
+             "key=%p, policy=%u", key, (unsigned int)policy);
+
+  if (!ctx)
+    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+  err = tofu_policy_start (ctx, 0, key, policy);
+  return TRACE_ERR (err);
+}
+
+
+/* This is the synchronous variant of gpgme_op_tofu_policy_start.  */
+gpgme_error_t
+gpgme_op_tofu_policy (gpgme_ctx_t ctx,
+                      gpgme_key_t key, gpgme_tofu_policy_t policy)
+{
+  gpgme_error_t err;
+  TRACE_BEG2 (DEBUG_CTX, "gpgme_op_tofu_policy", ctx,
+             "key=%p, policy=%u", key, (unsigned int)policy);
+
+  if (!ctx)
+    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+  err = tofu_policy_start (ctx, 1, key, policy);
+  if (!err)
+    err = _gpgme_wait_one (ctx);
+  return TRACE_ERR (err);
+}
index 1370efd..c71914f 100644 (file)
@@ -33,7 +33,7 @@ noinst_HEADERS = run-support.h
 
 noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign \
                  run-verify run-encrypt run-identify run-decrypt run-genkey \
-                 run-keysign
+                 run-keysign run-tofu
 
 
 if RUN_GPG_TESTS
diff --git a/tests/run-tofu.c b/tests/run-tofu.c
new file mode 100644 (file)
index 0000000..ff55789
--- /dev/null
@@ -0,0 +1,178 @@
+/* run-tofu.c  - Test tool for Tofu functions
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* We need to include config.h so that we know whether we are building
+   with large file system (LFS) support. */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <gpgme.h>
+
+#define PGM "run-tofu"
+
+#include "run-support.h"
+
+
+static int verbose;
+
+
+static gpg_error_t
+status_cb (void *opaque, const char *keyword, const char *value)
+{
+  (void)opaque;
+  fprintf (stderr, "status_cb: %s %s\n", nonnull(keyword), nonnull(value));
+  return 0;
+}
+
+
+
+static gpgme_tofu_policy_t
+parse_policy_string (const char *string)
+{
+  gpgme_tofu_policy_t policy;
+
+  if (!strcmp (string, "auto"))
+    policy = GPGME_TOFU_POLICY_AUTO;
+  else if (!strcmp (string, "good"))
+    policy = GPGME_TOFU_POLICY_GOOD;
+  else if (!strcmp (string, "bad"))
+    policy = GPGME_TOFU_POLICY_BAD;
+  else if (!strcmp (string, "ask"))
+    policy = GPGME_TOFU_POLICY_ASK;
+  else if (!strcmp (string, "unknown"))
+    policy = GPGME_TOFU_POLICY_UNKNOWN;
+  else
+    {
+      fprintf (stderr, PGM ": invalid policy value '%s'\n", string);
+      exit (1);
+    }
+
+  return policy;
+}
+
+
+
+static int
+show_usage (int ex)
+{
+  fputs ("usage: " PGM " [options] FPR\n\n"
+         "Options:\n"
+         "  --policy NAME    Set tofu policy for key to NAME\n"
+         "  --verbose        run in verbose mode\n"
+         "  --status         print status lines from the backend\n"
+         , stderr);
+  exit (ex);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+  gpgme_error_t err;
+  gpgme_ctx_t ctx;
+  gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
+  int print_status = 0;
+  gpgme_key_t thekey;
+  const char *fpr;
+  const char *policystr = NULL;
+  gpgme_tofu_policy_t policy;
+
+  if (argc)
+    { argc--; argv++; }
+
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--help"))
+        show_usage (0);
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--status"))
+        {
+          print_status = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--policy"))
+        {
+          argc--; argv++;
+          if (!argc)
+            show_usage (1);
+          policystr = *argv;
+          argc--; argv++;
+        }
+      else if (!strncmp (*argv, "--", 2))
+        show_usage (1);
+    }
+
+  if (argc != 1)
+    show_usage (1);
+  fpr = argv[0];
+
+  init_gpgme (protocol);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+  gpgme_set_protocol (ctx, protocol);
+  gpgme_set_armor (ctx, 1);
+  if (print_status)
+    {
+      gpgme_set_status_cb (ctx, status_cb, NULL);
+      gpgme_set_ctx_flag (ctx, "full-status", "1");
+    }
+
+  err = gpgme_get_key (ctx, fpr, &thekey, 0);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error getting key '%s': %s\n",
+               fpr, gpg_strerror (err));
+      exit (1);
+    }
+
+  if (policystr)
+    {
+      policy = parse_policy_string (policystr);
+
+      err = gpgme_op_tofu_policy (ctx, thekey, policy);
+      if (err)
+        {
+          fprintf (stderr, PGM ": gpgme_op_tofu_polciy failed: %s\n",
+                   gpg_strerror (err));
+          exit (1);
+        }
+    }
+
+  gpgme_key_unref (thekey);
+  gpgme_release (ctx);
+  return 0;
+}