common: Change log_clock to printf style.
[gnupg.git] / g13 / sh-cmd.c
index 4ef37c1..b57369d 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>
@@ -27,7 +27,7 @@
 
 #include "g13-syshelp.h"
 #include <assuan.h>
-#include "i18n.h"
+#include "../common/i18n.h"
 #include "keyblob.h"
 
 
@@ -35,7 +35,7 @@
    the CTRL object of each connection.  */
 struct server_local_s
 {
-  /* The Assuan contect we are working on.  */
+  /* The Assuan context we are working on.  */
   assuan_context_t assuan_ctx;
 
   /* The malloced name of the device.  */
@@ -157,11 +157,89 @@ reset_notify (assuan_context_t ctx, char *line)
 }
 
 
+static const char hlp_finddevice[] =
+  "FINDDEVICE <name>\n"
+  "\n"
+  "Find the device matching NAME.  NAME be any identifier from\n"
+  "g13tab permissible for the user.  The corresponding block\n"
+  "device is returned using a status line.";
+static gpg_error_t
+cmd_finddevice (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  tab_item_t ti;
+  const char *s;
+  const char *name;
+
+  name = skip_options (line);
+
+  /* Are we allowed to use the given device?  We check several names:
+   *  1. The full block device
+   *  2. The label
+   *  3. The final part of the block device if NAME does not have a slash.
+   *  4. The mountpoint
+   */
+  for (ti=ctrl->client.tab; ti; ti = ti->next)
+    if (!strcmp (name, ti->blockdev))
+      break;
+  if (!ti)
+    {
+      for (ti=ctrl->client.tab; ti; ti = ti->next)
+        if (ti->label && !strcmp (name, ti->label))
+          break;
+    }
+  if (!ti && !strchr (name, '/'))
+    {
+      for (ti=ctrl->client.tab; ti; ti = ti->next)
+        {
+          s = strrchr (ti->blockdev, '/');
+          if (s && s[1] && !strcmp (name, s+1))
+            break;
+        }
+    }
+  if (!ti)
+    {
+      for (ti=ctrl->client.tab; ti; ti = ti->next)
+        if (ti->mountpoint && !strcmp (name, ti->mountpoint))
+          break;
+    }
+
+  if (!ti)
+    {
+      err = set_error (GPG_ERR_NOT_FOUND, "device not configured for user");
+      goto leave;
+    }
+
+  /* Check whether we have permissions to open the device.  */
+  {
+    estream_t fp = es_fopen (ti->blockdev, "rb");
+    if (!fp)
+      {
+        err = gpg_error_from_syserror ();
+        log_error ("error opening '%s': %s\n",
+                   ti->blockdev, gpg_strerror (err));
+        goto leave;
+      }
+    es_fclose (fp);
+  }
+
+  err = g13_status (ctrl, STATUS_BLOCKDEV, ti->blockdev, NULL);
+  if (err)
+    return err;
+
+ leave:
+  return leave_cmd (ctx, err);
+}
+
+
 static const char hlp_device[] =
   "DEVICE <name>\n"
   "\n"
   "Set the device used by further commands.\n"
-  "A device name or a PARTUUID string may be used.";
+  "A device name or a PARTUUID string may be used.\n"
+  "Access to that device (by the g13 system) is locked\n"
+  "until a new DEVICE command or end of this process\n";
 static gpg_error_t
 cmd_device (assuan_context_t ctx, char *line)
 {
@@ -170,9 +248,15 @@ cmd_device (assuan_context_t ctx, char *line)
   tab_item_t ti;
   estream_t fp = NULL;
 
-  /* strcpy (line, "/dev/sdb1"); /\* FIXME *\/ */
   line = skip_options (line);
 
+/* # warning hardwired to /dev/sdb1 ! */
+/*   if (strcmp (line, "/dev/sdb1")) */
+/*     { */
+/*       err = gpg_error (GPG_ERR_ENOENT); */
+/*       goto leave; */
+/*     } */
+
   /* Always close an open device stream of this session. */
   xfree (ctrl->server_local->devicename);
   ctrl->server_local->devicename = NULL;
@@ -185,7 +269,7 @@ cmd_device (assuan_context_t ctx, char *line)
       break;
   if (!ti)
     {
-      set_error (GPG_ERR_EACCES, "device not configured for user");
+      err = set_error (GPG_ERR_EACCES, "device not configured for user");
       goto leave;
     }
 
@@ -213,6 +297,8 @@ cmd_device (assuan_context_t ctx, char *line)
   fp = NULL;
   ctrl->devti = ti;
 
+  /* Fixme: Take some kind of lock.  */
+
  leave:
   es_fclose (fp);
   if (err)
@@ -235,9 +321,9 @@ cmd_create (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err = 0;
+  estream_t fp = NULL;
 
   line = skip_options (line);
-
   if (strcmp (line, "dm-crypt"))
     {
       err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
@@ -255,22 +341,312 @@ cmd_create (assuan_context_t ctx, char *line)
   err = sh_is_empty_partition (ctrl->server_local->devicename);
   if (err)
     {
-      assuan_set_error (ctx, err, "Partition is not empty");
+      if (gpg_err_code (err) == GPG_ERR_FALSE)
+        err = gpg_error (GPG_ERR_CONFLICT);
+      err = assuan_set_error (ctx, err, "Partition is not empty");
+      goto leave;
+    }
+
+  /* We need a writeable stream to create the container.  */
+  fp = es_fopen (ctrl->server_local->devicename, "r+b");
+  if (!fp)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error opening '%s': %s\n",
+                 ctrl->server_local->devicename, gpg_strerror (err));
+      goto leave;
+    }
+  if (es_setvbuf (fp, NULL, _IONBF, 0))
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error setting '%s' to _IONBF: %s\n",
+                 ctrl->server_local->devicename, gpg_strerror (err));
       goto leave;
     }
 
   err = sh_dmcrypt_create_container (ctrl,
                                      ctrl->server_local->devicename,
-                                     ctrl->server_local->devicefp);
+                                     fp);
+  if (es_fclose (fp))
+    {
+      gpg_error_t err2 = gpg_error_from_syserror ();
+      log_error ("error closing '%s': %s\n",
+                 ctrl->server_local->devicename, gpg_strerror (err2));
+      if (!err)
+        err = err2;
+    }
+  fp = NULL;
+
+ leave:
+  es_fclose (fp);
+  return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_getkeyblob[] =
+  "GETKEYBLOB\n"
+  "\n"
+  "Return the encrypted keyblob of the current device.";
+static gpg_error_t
+cmd_getkeyblob (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  void *enckeyblob = NULL;
+  size_t enckeybloblen;
+
+  line = skip_options (line);
+
+  if (!ctrl->server_local->devicename
+      || !ctrl->server_local->devicefp
+      || !ctrl->devti)
+    {
+      err = set_error (GPG_ERR_ENOENT, "No device has been set");
+      goto leave;
+    }
+
+  err = sh_is_empty_partition (ctrl->server_local->devicename);
+  if (!err)
+    {
+      err = gpg_error (GPG_ERR_ENODEV);
+      assuan_set_error (ctx, err, "Partition is empty");
+      goto leave;
+    }
+  err = 0;
+
+  err = g13_keyblob_read (ctrl->server_local->devicename,
+                          &enckeyblob, &enckeybloblen);
+  if (err)
+    goto leave;
+
+  err = assuan_send_data (ctx, enckeyblob, enckeybloblen);
+  if (!err)
+    err = assuan_send_data (ctx, NULL, 0); /* Flush  */
+
+ leave:
+  xfree (enckeyblob);
+  return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_mount[] =
+  "MOUNT <type>\n"
+  "\n"
+  "Mount an encrypted partition on the current device.\n"
+  "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_mount (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  unsigned char *keyblob = NULL;
+  size_t keybloblen;
+  tupledesc_t tuples = NULL;
+
+  line = skip_options (line);
+
+  if (strcmp (line, "dm-crypt"))
+    {
+      err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+      goto leave;
+    }
+
+  if (!ctrl->server_local->devicename
+      || !ctrl->server_local->devicefp
+      || !ctrl->devti)
+    {
+      err = set_error (GPG_ERR_ENOENT, "No device has been set");
+      goto leave;
+    }
+
+  err = sh_is_empty_partition (ctrl->server_local->devicename);
+  if (!err)
+    {
+      err = gpg_error (GPG_ERR_ENODEV);
+      assuan_set_error (ctx, err, "Partition is empty");
+      goto leave;
+    }
+  err = 0;
+
+  /* We expect that the client already decrypted the keyblob.
+   * Eventually we should move reading of the keyblob to here and ask
+   * the client to decrypt it.  */
+  assuan_begin_confidential (ctx);
+  err = assuan_inquire (ctx, "KEYBLOB",
+                        &keyblob, &keybloblen, 4 * 1024);
+  assuan_end_confidential (ctx);
+  if (err)
+    {
+      log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+  err = create_tupledesc (&tuples, keyblob, keybloblen);
+  if (!err)
+    keyblob = NULL;
+  else
+    {
+      if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+        log_error ("unknown keyblob version received\n");
+      goto leave;
+    }
+
+  err = sh_dmcrypt_mount_container (ctrl,
+                                    ctrl->server_local->devicename,
+                                    tuples);
+
+ leave:
+  destroy_tupledesc (tuples);
+  return leave_cmd (ctx, err);
+}
 
 
+static const char hlp_umount[] =
+  "UMOUNT <type>\n"
+  "\n"
+  "Unmount an encrypted partition and wipe the key.\n"
+  "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_umount (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
 
+  line = skip_options (line);
+
+  if (strcmp (line, "dm-crypt"))
+    {
+      err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+      goto leave;
+    }
+
+  if (!ctrl->server_local->devicename
+      || !ctrl->server_local->devicefp
+      || !ctrl->devti)
+    {
+      err = set_error (GPG_ERR_ENOENT, "No device has been set");
+      goto leave;
+    }
+
+  err = sh_dmcrypt_umount_container (ctrl, ctrl->server_local->devicename);
 
  leave:
   return leave_cmd (ctx, err);
 }
 
 
+static const char hlp_suspend[] =
+  "SUSPEND <type>\n"
+  "\n"
+  "Suspend an encrypted partition and wipe the key.\n"
+  "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_suspend (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+
+  line = skip_options (line);
+
+  if (strcmp (line, "dm-crypt"))
+    {
+      err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+      goto leave;
+    }
+
+  if (!ctrl->server_local->devicename
+      || !ctrl->server_local->devicefp
+      || !ctrl->devti)
+    {
+      err = set_error (GPG_ERR_ENOENT, "No device has been set");
+      goto leave;
+    }
+
+  err = sh_is_empty_partition (ctrl->server_local->devicename);
+  if (!err)
+    {
+      err = gpg_error (GPG_ERR_ENODEV);
+      assuan_set_error (ctx, err, "Partition is empty");
+      goto leave;
+    }
+  err = 0;
+
+  err = sh_dmcrypt_suspend_container (ctrl, ctrl->server_local->devicename);
+
+ leave:
+  return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_resume[] =
+  "RESUME <type>\n"
+  "\n"
+  "Resume an encrypted partition and set the key.\n"
+  "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_resume (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  unsigned char *keyblob = NULL;
+  size_t keybloblen;
+  tupledesc_t tuples = NULL;
+
+  line = skip_options (line);
+
+  if (strcmp (line, "dm-crypt"))
+    {
+      err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+      goto leave;
+    }
+
+  if (!ctrl->server_local->devicename
+      || !ctrl->server_local->devicefp
+      || !ctrl->devti)
+    {
+      err = set_error (GPG_ERR_ENOENT, "No device has been set");
+      goto leave;
+    }
+
+  err = sh_is_empty_partition (ctrl->server_local->devicename);
+  if (!err)
+    {
+      err = gpg_error (GPG_ERR_ENODEV);
+      assuan_set_error (ctx, err, "Partition is empty");
+      goto leave;
+    }
+  err = 0;
+
+  /* We expect that the client already decrypted the keyblob.
+   * Eventually we should move reading of the keyblob to here and ask
+   * the client to decrypt it.  */
+  assuan_begin_confidential (ctx);
+  err = assuan_inquire (ctx, "KEYBLOB",
+                        &keyblob, &keybloblen, 4 * 1024);
+  assuan_end_confidential (ctx);
+  if (err)
+    {
+      log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+  err = create_tupledesc (&tuples, keyblob, keybloblen);
+  if (!err)
+    keyblob = NULL;
+  else
+    {
+      if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+        log_error ("unknown keyblob version received\n");
+      goto leave;
+    }
+
+  err = sh_dmcrypt_resume_container (ctrl,
+                                     ctrl->server_local->devicename,
+                                     tuples);
+
+ leave:
+  destroy_tupledesc (tuples);
+  return leave_cmd (ctx, err);
+}
+
 
 static const char hlp_getinfo[] =
   "GETINFO <what>\n"
@@ -366,8 +742,14 @@ register_commands (assuan_context_t ctx, int fail_all)
     assuan_handler_t handler;
     const char * const help;
   } table[] =  {
+    { "FINDDEVICE",    cmd_finddevice, hlp_finddevice },
     { "DEVICE",        cmd_device, hlp_device },
     { "CREATE",        cmd_create, hlp_create },
+    { "GETKEYBLOB",    cmd_getkeyblob,  hlp_getkeyblob },
+    { "MOUNT",         cmd_mount,  hlp_mount  },
+    { "UMOUNT",        cmd_umount, hlp_umount  },
+    { "SUSPEND",       cmd_suspend,hlp_suspend},
+    { "RESUME",        cmd_resume, hlp_resume },
     { "INPUT",         NULL },
     { "OUTPUT",        NULL },
     { "GETINFO",       cmd_getinfo, hlp_getinfo },