gpg,sm: New option --with-key-screening.
[gnupg.git] / g13 / call-syshelp.c
index 0e227ab..8a50c3f 100644 (file)
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 
 #include "g13.h"
 #include <assuan.h>
-#include "i18n.h"
+#include "../common/i18n.h"
 #include "g13tuple.h"
 #include "keyblob.h"
-#include "membuf.h"
+#include "../common/membuf.h"
 #include "create.h"
+#include "call-syshelp.h"
 
 
 /* Local data for this module.  A pointer to this is stored in the
@@ -44,21 +45,56 @@ struct call_syshelp_s
 };
 
 
-/* Fork off the syshelp tool if this has not already been done.  */
+/* Parameter used with the CREATE command.  */
+struct create_parm_s
+{
+  assuan_context_t ctx;
+  ctrl_t ctrl;
+  membuf_t plaintext;
+  unsigned int expect_plaintext:1;
+  unsigned int got_plaintext:1;
+};
+
+
+/* Parameter used with the MOUNT command.  */
+struct mount_parm_s
+{
+  assuan_context_t ctx;
+  ctrl_t ctrl;
+  const void *keyblob;
+  size_t keybloblen;
+};
+
+
+
+
+\f
+/* Fork off the syshelp tool if this has not already been done.  On
+   success stores the current Assuan context for the syshelp tool at
+   R_CTX.  */
 static gpg_error_t
-start_syshelp (ctrl_t ctrl)
+start_syshelp (ctrl_t ctrl, assuan_context_t *r_ctx)
 {
   gpg_error_t err;
   assuan_context_t ctx;
   assuan_fd_t no_close_list[3];
   int i;
 
-  if (ctrl->syshelp_local->assctx)
+  *r_ctx = NULL;
+
+  if (ctrl->syshelp_local && (*r_ctx = ctrl->syshelp_local->assctx))
     return 0; /* Already set.  */
 
   if (opt.verbose)
     log_info ("starting a new syshelp\n");
 
+  if (!ctrl->syshelp_local)
+    {
+      ctrl->syshelp_local = xtrycalloc (1, sizeof *ctrl->syshelp_local);
+      if (!ctrl->syshelp_local)
+        return gpg_error_from_syserror ();
+    }
+
   if (es_fflush (NULL))
     {
       err = gpg_error_from_syserror ();
@@ -79,38 +115,40 @@ start_syshelp (ctrl_t ctrl)
       return err;
     }
 
-  /* Call userv to start g13-syshelp.  This userv script needs tpo be
-     installed under the name "gnupg-g13-syshelp":
-
-       if ( glob service-user root
-          )
-           reset
-           suppress-args
-           execute /home/wk/b/gnupg/g13/g13-syshelp -v
-       else
-           error Nothing to do for this service-user
-       fi
-       quit
-  */
+  /* Call userv to start g13-syshelp.  This userv script needs to be
+   * installed under the name "gnupg-g13-syshelp":
+   *
+   *   if ( glob service-user root
+   *      )
+   *       reset
+   *       suppress-args
+   *       execute /home/wk/b/gnupg/g13/g13-syshelp -v
+   *   else
+   *       error Nothing to do for this service-user
+   *   fi
+   *   quit
+   */
   {
-    const char *argv[3];
+    const char *argv[4];
 
     argv[0] = "userv";
-    argv[1] = "gnupg-g13-syshelp";
-    argv[2] = NULL;
+    argv[1] = "root";
+    argv[2] = "gnupg-g13-syshelp";
+    argv[3] = NULL;
 
     err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv,
                                no_close_list, NULL, NULL, 0);
   }
   if (err)
     {
-      log_error ("can't connect to '%s' - : %s\n",
-                 "g13-syshelp", gpg_strerror (err));
+      log_error ("can't connect to '%s': %s %s\n",
+                 "g13-syshelp", gpg_strerror (err), gpg_strsource (err));
       log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n");
       assuan_release (ctx);
       return err;
     }
-  ctrl->syshelp_local->assctx = ctx;
+
+  *r_ctx = ctrl->syshelp_local->assctx = ctx;
 
   if (DBG_IPC)
     log_debug ("connection to g13-syshelp established\n");
@@ -129,5 +167,465 @@ call_syshelp_release (ctrl_t ctrl)
     {
       assuan_release (ctrl->syshelp_local->assctx);
       ctrl->syshelp_local->assctx = NULL;
+      xfree (ctrl->syshelp_local);
+      ctrl->syshelp_local = NULL;
     }
 }
+
+
+\f
+/* Staus callback for call_syshelp_find_device.  */
+static gpg_error_t
+finddevice_status_cb (void *opaque, const char *line)
+{
+  char **r_blockdev = opaque;
+  char *p;
+
+  if ((p = has_leading_keyword (line, "BLOCKDEV")) && *p && !*r_blockdev)
+    {
+      *r_blockdev = xtrystrdup (p);
+      if (!*r_blockdev)
+        return gpg_error_from_syserror ();
+    }
+
+  return 0;
+}
+
+
+/* Send the FINDDEVICE command to the syshelper.  On success the name
+ * of the block device is stored at R_BLOCKDEV. */
+gpg_error_t
+call_syshelp_find_device (ctrl_t ctrl, const char *name, char **r_blockdev)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  char *line = NULL;
+  char *blockdev = NULL;  /* The result.  */
+
+  *r_blockdev = NULL;
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  line = xtryasprintf ("FINDDEVICE %s", name);
+  if (!line)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL,
+                         finddevice_status_cb, &blockdev);
+  if (err)
+    goto leave;
+  if (!blockdev)
+    {
+      log_error ("status line for successful FINDDEVICE missing\n");
+      err = gpg_error (GPG_ERR_UNEXPECTED);
+      goto leave;
+    }
+  *r_blockdev = blockdev;
+  blockdev = NULL;
+
+ leave:
+  xfree (blockdev);
+  xfree (line);
+  return err;
+}
+
+
+\f
+static gpg_error_t
+getkeyblob_data_cb (void *opaque, const void *data, size_t datalen)
+{
+  membuf_t *mb = opaque;
+
+  if (data)
+    put_membuf (mb, data, datalen);
+
+  return 0;
+}
+
+
+/* Send the GTEKEYBLOB command to the syshelper.  On success the
+ * encrypted keyblpob is stored at (R_ENCKEYBLOB,R_ENCKEYBLOBLEN).  */
+gpg_error_t
+call_syshelp_get_keyblob (ctrl_t ctrl,
+                          void **r_enckeyblob, size_t *r_enckeybloblen)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  membuf_t mb;
+
+  *r_enckeyblob = NULL;
+  *r_enckeybloblen = 0;
+  init_membuf (&mb, 512);
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  err = assuan_transact (ctx, "GETKEYBLOB",
+                         getkeyblob_data_cb, &mb,
+                         NULL, NULL, NULL, NULL);
+  if (err)
+    goto leave;
+  *r_enckeyblob = get_membuf (&mb, r_enckeybloblen);
+  if (!*r_enckeyblob)
+    err = gpg_error_from_syserror ();
+
+ leave:
+  xfree (get_membuf (&mb, NULL));
+  return err;
+}
+
+
+\f
+/* Send the DEVICE command to the syshelper.  FNAME is the name of the
+   device.  */
+gpg_error_t
+call_syshelp_set_device (ctrl_t ctrl, const char *fname)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  char *line = NULL;
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  line = xtryasprintf ("DEVICE %s", fname);
+  if (!line)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ leave:
+  xfree (line);
+  return err;
+}
+
+
+\f
+static gpg_error_t
+create_status_cb (void *opaque, const char *line)
+{
+  struct create_parm_s *parm = opaque;
+
+  if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS"))
+    parm->expect_plaintext = 1;
+
+  return 0;
+}
+
+
+static gpg_error_t
+create_data_cb (void *opaque, const void *data, size_t datalen)
+{
+  struct create_parm_s *parm = opaque;
+  gpg_error_t err = 0;
+
+  if (!parm->expect_plaintext)
+    {
+      log_error ("status line for data missing\n");
+      err = gpg_error (GPG_ERR_UNEXPECTED);
+    }
+  else if (data)
+    {
+      put_membuf (&parm->plaintext, data, datalen);
+    }
+  else
+    {
+      parm->expect_plaintext = 0;
+      parm->got_plaintext = 1;
+    }
+
+  return err;
+}
+
+
+static gpg_error_t
+create_inq_cb (void *opaque, const char *line)
+{
+  struct create_parm_s *parm = opaque;
+  gpg_error_t err;
+
+  if (has_leading_keyword (line, "ENCKEYBLOB"))
+    {
+      void *plaintext;
+      size_t plaintextlen;
+
+      if (!parm->got_plaintext)
+        err = gpg_error (GPG_ERR_UNEXPECTED);
+      else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen)))
+        err = gpg_error_from_syserror ();
+      else
+        {
+          void *ciphertext;
+          size_t ciphertextlen;
+
+          log_printhex ("plain", plaintext, plaintextlen);
+          err = g13_encrypt_keyblob (parm->ctrl,
+                                     plaintext, plaintextlen,
+                                     &ciphertext, &ciphertextlen);
+          wipememory (plaintext, plaintextlen);
+          xfree (plaintext);
+          if (err)
+            log_error ("error encrypting keyblob: %s\n", gpg_strerror (err));
+          else
+            {
+              err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen);
+              xfree (ciphertext);
+              if (err)
+                log_error ("sending ciphertext to g13-syshelp failed: %s\n",
+                           gpg_strerror (err));
+            }
+        }
+    }
+  else
+    err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+  return err;
+}
+
+
+/* Run the CREATE command on the current device.  CONTTYPES gives the
+   requested content type for the new container.  */
+gpg_error_t
+call_syshelp_run_create (ctrl_t ctrl, int conttype)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  struct create_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  /* tty_get ("waiting for debugger"); */
+  /* tty_kill_prompt (); */
+
+  parm.ctx = ctx;
+  parm.ctrl = ctrl;
+  init_membuf (&parm.plaintext, 512);
+  if (conttype == CONTTYPE_DM_CRYPT)
+    {
+      err = assuan_transact (ctx, "CREATE dm-crypt",
+                             create_data_cb, &parm,
+                             create_inq_cb, &parm,
+                             create_status_cb, &parm);
+    }
+  else
+    {
+      log_error ("invalid backend type %d given\n", conttype);
+      err = GPG_ERR_INTERNAL;
+      goto leave;
+    }
+
+ leave:
+  xfree (get_membuf (&parm.plaintext, NULL));
+  return err;
+}
+
+
+\f
+static gpg_error_t
+mount_status_cb (void *opaque, const char *line)
+{
+  struct mount_parm_s *parm = opaque;
+
+  /* Nothing right now.  */
+  (void)parm;
+  (void)line;
+
+  return 0;
+}
+
+
+/* Inquire callback for MOUNT and RESUME.  */
+static gpg_error_t
+mount_inq_cb (void *opaque, const char *line)
+{
+  struct mount_parm_s *parm = opaque;
+  gpg_error_t err;
+
+  if (has_leading_keyword (line, "KEYBLOB"))
+    {
+      int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL);
+
+      if (setconfidential)
+        assuan_begin_confidential (parm->ctx);
+      err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen);
+      if (setconfidential)
+        assuan_end_confidential (parm->ctx);
+      if (err)
+        log_error ("sending keyblob to g13-syshelp failed: %s\n",
+                   gpg_strerror (err));
+    }
+  else
+    err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+  return err;
+}
+
+
+/*
+ * Run the MOUNT command on the current device.  CONTTYPES gives the
+ * requested content type for the new container.  MOUNTPOINT the
+ * desired mount point or NULL for default.
+ */
+gpg_error_t
+call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
+                        tupledesc_t tuples)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  struct mount_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  /* tty_get ("waiting for debugger"); */
+  /* tty_kill_prompt (); */
+
+  parm.ctx = ctx;
+  parm.ctrl = ctrl;
+  if (conttype == CONTTYPE_DM_CRYPT)
+    {
+      ref_tupledesc (tuples);
+      parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
+      err = assuan_transact (ctx, "MOUNT dm-crypt",
+                             NULL, NULL,
+                             mount_inq_cb, &parm,
+                             mount_status_cb, &parm);
+      unref_tupledesc (tuples);
+    }
+  else
+    {
+      (void)mountpoint; /* Not used.  */
+      log_error ("invalid backend type %d given\n", conttype);
+      err = GPG_ERR_INTERNAL;
+      goto leave;
+    }
+
+ leave:
+  return err;
+}
+
+
+\f
+/*
+ * Run the UMOUNT command on the current device.  CONTTYPES gives the
+ * content type of the container (fixme: Do we really need this?).
+ */
+gpg_error_t
+call_syshelp_run_umount (ctrl_t ctrl, int conttype)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  if (conttype == CONTTYPE_DM_CRYPT)
+    {
+      err = assuan_transact (ctx, "UMOUNT dm-crypt",
+                             NULL, NULL,
+                             NULL, NULL,
+                             NULL, NULL);
+    }
+  else
+    {
+      log_error ("invalid backend type %d given\n", conttype);
+      err = GPG_ERR_INTERNAL;
+      goto leave;
+    }
+
+ leave:
+  return err;
+}
+
+
+\f
+/*
+ * Run the SUSPEND command on the current device.  CONTTYPES gives the
+ * requested content type for the new container.
+ */
+gpg_error_t
+call_syshelp_run_suspend (ctrl_t ctrl, int conttype)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  if (conttype == CONTTYPE_DM_CRYPT)
+    {
+      err = assuan_transact (ctx, "SUSPEND dm-crypt",
+                             NULL, NULL,
+                             NULL, NULL,
+                             NULL, NULL);
+    }
+  else
+    {
+      log_error ("invalid backend type %d given\n", conttype);
+      err = GPG_ERR_INTERNAL;
+      goto leave;
+    }
+
+ leave:
+  return err;
+}
+
+
+\f
+/* Run the RESUME command on the current device.  CONTTYPES gives the
+   requested content type for the container.  */
+gpg_error_t
+call_syshelp_run_resume (ctrl_t ctrl, int conttype, tupledesc_t tuples)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  struct mount_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+
+  err = start_syshelp (ctrl, &ctx);
+  if (err)
+    goto leave;
+
+  /* tty_get ("waiting for debugger"); */
+  /* tty_kill_prompt (); */
+
+  parm.ctx = ctx;
+  parm.ctrl = ctrl;
+  if (conttype == CONTTYPE_DM_CRYPT)
+    {
+      ref_tupledesc (tuples);
+      parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
+      err = assuan_transact (ctx, "RESUME dm-crypt",
+                             NULL, NULL,
+                             mount_inq_cb, &parm,
+                             NULL, NULL);
+      unref_tupledesc (tuples);
+    }
+  else
+    {
+      log_error ("invalid backend type %d given\n", conttype);
+      err = GPG_ERR_INTERNAL;
+      goto leave;
+    }
+
+ leave:
+  return err;
+}