Improve some comments.
[gnupg.git] / g13 / server.c
index 900dbc9..0c4563e 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 <assuan.h>
 #include "i18n.h"
 #include "keyblob.h"
-#include "./server.h"
-#include "./mount.h"
+#include "server.h"
+#include "create.h"
+#include "mount.h"
+#include "suspend.h"
+#include "../common/server-help.h"
+#include "../common/call-gpg.h"
 
 
+/* The filepointer for status message used in non-server mode */
+static FILE *statusfp;
+
 /* Local data for this server module.  A pointer to this is stored in
    the CTRL object of each connection.  */
-struct server_local_s 
+struct server_local_s
 {
   /* The Assuan contect we are working on.  */
   assuan_context_t assuan_ctx;
 
   char *containername;  /* Malloced active containername.  */
-
 };
 
 
@@ -54,44 +60,13 @@ static int command_has_option (const char *cmd, const char *cmdopt);
 
 \f
 /*
-   Helper functions. 
+   Helper functions.
  */
 
 /* Set an error and a description.  */
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
 
-/* Skip over options.  Blanks after the options are also removed.  */
-static char *
-skip_options (const char *line)
-{
-  while (spacep (line))
-    line++;
-  while ( *line == '-' && line[1] == '-' )
-    {
-      while (*line && !spacep (line))
-        line++;
-      while (spacep (line))
-        line++;
-    }
-  return (char*)line;
-}
-
-
-/* Check whether the option NAME appears in LINE.  */
-/* static int */
-/* has_option (const char *line, const char *name) */
-/* { */
-/*   const char *s; */
-/*   int n = strlen (name); */
-
-/*   s = strstr (line, name); */
-/*   if (s && s >= skip_options (line)) */
-/*     return 0; */
-/*   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */
-/* } */
-
-
 /* Helper to print a message while leaving a command.  */
 static gpg_error_t
 leave_cmd (assuan_context_t ctx, gpg_error_t err)
@@ -167,12 +142,6 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
     {
       err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
     }
-  else if (!strcmp (key, "enable-audit-log"))
-    {
-      /* This is not yet used.  */
-      /* int i = *value? atoi (value) : 0; */
-      /* ctrl->server_local->enable_audit_log = i; */
-    }
   else if (!strcmp (key, "allow-pinentry-notify"))
     {
       ; /* We always allow it.  */
@@ -185,29 +154,33 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
 
 
 /* The handler for an Assuan RESET command.  */
-static void
-reset_notify (assuan_context_t ctx)
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
 
+  (void)line;
+
   xfree (ctrl->server_local->containername);
   ctrl->server_local->containername = NULL;
 
+  FREE_STRLIST (ctrl->recipients);
+
   assuan_close_input_fd (ctx);
   assuan_close_output_fd (ctx);
+  return 0;
 }
 
 
-
-/* OPEN [options] <filename>
-
-   Open the container FILENAME.  FILENAME must be percent-plus
-   escaped.  A quick check to see whether this is a suitable G13
-   container file is done.  However no cryptographic check or any
-   other check is done.  This command is used to define the target for
-   further commands.  The filename is reset with the RESET command,
-   another OPEN or the CREATE command.
- */
+static const char hlp_open[] =
+  "OPEN [<options>] <filename>\n"
+  "\n"
+  "Open the container FILENAME.  FILENAME must be percent-plus\n"
+  "escaped.  A quick check to see whether this is a suitable G13\n"
+  "container file is done.  However no cryptographic check or any\n"
+  "other check is done.  This command is used to define the target for\n"
+  "further commands.  The filename is reset with the RESET command,\n"
+  "another OPEN or the CREATE command.";
 static gpg_error_t
 cmd_open (assuan_context_t ctx, char *line)
 {
@@ -252,19 +225,19 @@ cmd_open (assuan_context_t ctx, char *line)
   ctrl->server_local->containername = xtrystrdup (line);
   if (!ctrl->server_local->containername)
     err = gpg_error_from_syserror ();
-  
-  
+
+
  leave:
   return leave_cmd (ctx, err);
 }
 
 
-/* MOUNT [options] [<mountpoint>]
-
-   Mount the currently open file onto MOUNTPOINT.  If MOUNTPOINT is
-   not given the system picks an unused mountpoint.  MOUNTPOINT must
-   be percent-plus escaped to allow for arbitrary names.
- */
+static const char hlp_mount[] =
+  "MOUNT [options] [<mountpoint>]\n"
+  "\n"
+  "Mount the currently open file onto MOUNTPOINT.  If MOUNTPOINT is not\n"
+  "given the system picks an unused mountpoint.  MOUNTPOINT must\n"
+  "be percent-plus escaped to allow for arbitrary names.";
 static gpg_error_t
 cmd_mount (assuan_context_t ctx, char *line)
 {
@@ -302,7 +275,7 @@ cmd_mount (assuan_context_t ctx, char *line)
     }
 
   /* Perform the mount.  */
-  err = g13_mount_container (ctrl, ctrl->server_local->containername, 
+  err = g13_mount_container (ctrl, ctrl->server_local->containername,
                              *line? line : NULL);
 
  leave:
@@ -310,11 +283,12 @@ cmd_mount (assuan_context_t ctx, char *line)
 }
 
 
-/* UMOUNT [options] [<mountpoint>]
-
-   Unmount the currently open file or the one opened at MOUNTPOINT.
-   MOUNTPOINT must be percent-plus escaped.
- */
+static const char hlp_umount[] =
+  "UMOUNT [options] [<mountpoint>]\n"
+  "\n"
+  "Unmount the currently open file or the one opened at MOUNTPOINT.\n"
+  "MOUNTPOINT must be percent-plus escaped.  On success the mountpoint\n"
+  "is returned via a \"MOUNTPOINT\" status line.";
 static gpg_error_t
 cmd_umount (assuan_context_t ctx, char *line)
 {
@@ -346,7 +320,7 @@ cmd_umount (assuan_context_t ctx, char *line)
     }
 
   /* Perform the unmount.  */
-  err = g13_umount_container (ctrl, ctrl->server_local->containername, 
+  err = g13_umount_container (ctrl, ctrl->server_local->containername,
                               *line? line : NULL);
 
  leave:
@@ -354,37 +328,81 @@ cmd_umount (assuan_context_t ctx, char *line)
 }
 
 
+static const char hlp_suspend[] =
+  "SUSPEND\n"
+  "\n"
+  "Suspend the currently set device.";
+static gpg_error_t
+cmd_suspend (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+
+  line = skip_options (line);
+  if (*line)
+    {
+      err = gpg_error (GPG_ERR_ASS_SYNTAX);
+      goto leave;
+    }
+
+  /* Perform the suspend operation.  */
+  err = g13_suspend_container (ctrl, ctrl->server_local->containername);
+
+ leave:
+  return leave_cmd (ctx, err);
+}
 
-/* RECIPIENT <userID>
 
-   FIXME - description. 
-   All RECIPIENT commands are cumulative until a RESET or an
-   successful CREATE command.
- */
+static const char hlp_resume[] =
+  "RESUME\n"
+  "\n"
+  "Resume the currently set device.";
 static gpg_error_t
-cmd_recipient (assuan_context_t ctx, char *line)
+cmd_resume (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err;
 
-  (void)ctrl;
-  err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-  /* err = gpgsm_add_to_certlist (ctrl, line, 0, */
-  /*                              &ctrl->server_local->recplist, 0); */
-  /* if (err) */
-  /*   { */
-  /*     gpgsm_status2 (ctrl, STATUS_INV_RECP, */
-  /*                    get_inv_recpsgnr_code (rc), line, NULL); */
-  /*   } */
+  line = skip_options (line);
+  if (*line)
+    {
+      err = gpg_error (GPG_ERR_ASS_SYNTAX);
+      goto leave;
+    }
 
+  /* Perform the suspend operation.  */
+  err = g13_resume_container (ctrl, ctrl->server_local->containername);
+
+ leave:
   return leave_cmd (ctx, err);
 }
 
 
-/* SIGNER <userID>
+static const char hlp_recipient[] =
+  "RECIPIENT <userID>\n"
+  "\n"
+  "Add USERID to the list of recipients to be used for the next CREATE\n"
+  "command.  All recipient commands are cumulative until a RESET or an\n"
+  "successful create command.";
+static gpg_error_t
+cmd_recipient (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
 
-   FIXME - description. 
- */
+  line = skip_options (line);
+
+  if (!add_to_strlist_try (&ctrl->recipients, line))
+    err = gpg_error_from_syserror ();
+
+  return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_signer[] =
+  "SIGNER <userID>\n"
+  "\n"
+  "Not yet implemented.";
 static gpg_error_t
 cmd_signer (assuan_context_t ctx, char *line)
 {
@@ -392,47 +410,81 @@ cmd_signer (assuan_context_t ctx, char *line)
   gpg_error_t err;
 
   (void)ctrl;
+  (void)line;
 
   err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
   return leave_cmd (ctx, err);
 }
 
 
-/* CREATE [options] filename
-
-   Create a new container.  On success the OPEN command is done
-   implictly for the new container.
- */
+static const char hlp_create[] =
+  "CREATE [options] <filename>\n"
+  "\n"
+  "Create a new container.  On success the OPEN command is \n"
+  "implictly done for the new container.";
 static gpg_error_t
 cmd_create (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err;
-
-  (void)ctrl;
+  char *p, *pend;
+  size_t len;
 
   /* First we close the active container.  */
   xfree (ctrl->server_local->containername);
   ctrl->server_local->containername = NULL;
 
+  /* Parse the line.  */
+  line = skip_options (line);
+  for (p=line; *p && !spacep (p); p++)
+    ;
+  pend = p;
+  while (spacep(p))
+    p++;
+  if (*p || pend == line)
+    {
+      err = gpg_error (GPG_ERR_ASS_SYNTAX);
+      goto leave;
+    }
+  *pend = 0;
 
+  /* Unescape the line and check for embedded Nul bytes.  */
+  len = percent_plus_unescape_inplace (line, 0);
+  line[len] = 0;
+  if (!len || memchr (line, 0, len))
+    {
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
 
-  err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-  return leave_cmd (ctx, err);
-}
+  /* Create container.  */
+  err = g13_create_container (ctrl, line);
 
+  if (!err)
+    {
+      FREE_STRLIST (ctrl->recipients);
 
-/* GETINFO <what>
+      /* Store the filename.  */
+      ctrl->server_local->containername = xtrystrdup (line);
+      if (!ctrl->server_local->containername)
+        err = gpg_error_from_syserror ();
 
-   Multipurpose function to return a variety of information.
-   Supported values for WHAT are:
+    }
+ leave:
+  return leave_cmd (ctx, err);
+}
 
-     version     - Return the version of the program.
-     pid         - Return the process id of the server.
-     cmd_has_option CMD OPT
-                 - Returns OK if the command CMD implements the option OPT.
 
- */
+static const char hlp_getinfo[] =
+  "GETINFO <what>\n"
+  "\n"
+  "Multipurpose function to return a variety of information.\n"
+  "Supported values for WHAT are:\n"
+  "\n"
+  "  version     - Return the version of the program.\n"
+  "  pid         - Return the process id of the server.\n"
+  "  cmd_has_option CMD OPT\n"
+  "              - Return OK if the command CMD implements the option OPT.";
 static gpg_error_t
 cmd_getinfo (assuan_context_t ctx, char *line)
 {
@@ -496,7 +548,7 @@ command_has_option (const char *cmd, const char *cmdopt)
 {
   (void)cmd;
   (void)cmdopt;
-      
+
   return 0;
 }
 
@@ -507,17 +559,20 @@ register_commands (assuan_context_t ctx)
 {
   static struct {
     const char *name;
-    gpg_error_t (*handler)(assuan_context_t, char *line);
+    assuan_handler_t handler;
+    const char * const help;
   } table[] =  {
-    { "OPEN",          cmd_open },
-    { "MOUNT",         cmd_mount },
-    { "UMOUNT",        cmd_umount },
-    { "RECIPIENT",     cmd_recipient },
-    { "SIGNER",        cmd_signer },
-    { "CREATE",        cmd_create },
-    { "INPUT",         NULL }, 
-    { "OUTPUT",        NULL }, 
-    { "GETINFO",       cmd_getinfo },
+    { "OPEN",          cmd_open,   hlp_open },
+    { "MOUNT",         cmd_mount,  hlp_mount},
+    { "UMOUNT",        cmd_umount, hlp_umount },
+    { "SUSPEND",       cmd_suspend, hlp_suspend },
+    { "RESUME",        cmd_resume,  hlp_resume },
+    { "RECIPIENT",     cmd_recipient, hlp_recipient },
+    { "SIGNER",        cmd_signer, hlp_signer },
+    { "CREATE",        cmd_create, hlp_create },
+    { "INPUT",         NULL },
+    { "OUTPUT",        NULL },
+    { "GETINFO",       cmd_getinfo,hlp_getinfo },
     { NULL }
   };
   gpg_error_t err;
@@ -525,10 +580,11 @@ register_commands (assuan_context_t ctx)
 
   for (i=0; table[i].name; i++)
     {
-      err = assuan_register_command (ctx, table[i].name, table[i].handler);
+      err = assuan_register_command (ctx, table[i].name, table[i].handler,
+                                     table[i].help);
       if (err)
         return err;
-    } 
+    }
   return 0;
 }
 
@@ -540,7 +596,7 @@ gpg_error_t
 g13_server (ctrl_t ctrl)
 {
   gpg_error_t err;
-  int filedes[2];
+  assuan_fd_t filedes[2];
   assuan_context_t ctx = NULL;
   static const char hello[] = ("GNU Privacy Guard's G13 server "
                                PACKAGE_VERSION " ready");
@@ -548,8 +604,8 @@ g13_server (ctrl_t ctrl)
   /* We use a pipe based server so that we can work from scripts.
      assuan_init_pipe_server will automagically detect when we are
      called with a socketpair and ignore FIELDES in this case. */
-  filedes[0] = 0;
-  filedes[1] = 1;
+  filedes[0] = assuan_fdopen (0);
+  filedes[1] = assuan_fdopen (1);
   err = assuan_new (&ctx);
   if (err)
     {
@@ -577,16 +633,13 @@ g13_server (ctrl_t ctrl)
 
   if (opt.verbose || opt.debug)
     {
-      char *tmp = NULL;
-      const char *s1 = getenv ("GPG_AGENT_INFO");
+      char *tmp;
 
       tmp = xtryasprintf ("Home: %s\n"
                           "Config: %s\n"
-                          "AgentInfo: %s\n"
                           "%s",
-                          opt.homedir,
+                          gnupg_homedir (),
                           opt.config_filename,
-                          s1?s1:"[not set]",
                           hello);
       if (tmp)
         {
@@ -608,9 +661,6 @@ g13_server (ctrl_t ctrl)
     }
   ctrl->server_local->assuan_ctx = ctx;
 
-  if (DBG_ASSUAN)
-    assuan_set_log_stream (ctx, log_get_stream ());
-
   while ( !(err = assuan_accept (ctx)) )
     {
       err = assuan_process (ctx);
@@ -621,8 +671,9 @@ g13_server (ctrl_t ctrl)
     err = 0;
   else
     log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
-  
+
  leave:
+  reset_notify (ctx, NULL);  /* Release all items hold by SERVER_LOCAL.  */
   if (ctrl->server_local)
     {
       xfree (ctrl->server_local);
@@ -634,99 +685,81 @@ g13_server (ctrl_t ctrl)
 }
 
 
+/* Send a status line with status ID NO.  The arguments are a list of
+   strings terminated by a NULL argument.  */
+gpg_error_t
+g13_status (ctrl_t ctrl, int no, ...)
+{
+  gpg_error_t err = 0;
+  va_list arg_ptr;
+  const char *text;
+
+  va_start (arg_ptr, no);
+
+  if (ctrl->no_server && ctrl->status_fd == -1)
+    ; /* No status wanted. */
+  else if (ctrl->no_server)
+    {
+      if (!statusfp)
+        {
+          if (ctrl->status_fd == 1)
+            statusfp = stdout;
+          else if (ctrl->status_fd == 2)
+            statusfp = stderr;
+          else
+            statusfp = fdopen (ctrl->status_fd, "w");
+
+          if (!statusfp)
+            {
+              log_fatal ("can't open fd %d for status output: %s\n",
+                         ctrl->status_fd, strerror(errno));
+            }
+        }
+
+      fputs ("[GNUPG:] ", statusfp);
+      fputs (get_status_string (no), statusfp);
+
+      while ( (text = va_arg (arg_ptr, const char*) ))
+        {
+          putc ( ' ', statusfp );
+          for (; *text; text++)
+            {
+              if (*text == '\n')
+                fputs ( "\\n", statusfp );
+              else if (*text == '\r')
+                fputs ( "\\r", statusfp );
+              else
+                putc ( *(const byte *)text,  statusfp );
+            }
+        }
+      putc ('\n', statusfp);
+      fflush (statusfp);
+    }
+  else
+    {
+      assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+      char buf[950], *p;
+      size_t n;
 
-/* gpg_error_t */
-/* gpgsm_status2 (ctrl_t ctrl, int no, ...) */
-/* { */
-/*   gpg_error_t err = 0; */
-/*   va_list arg_ptr; */
-/*   const char *text; */
-
-/*   va_start (arg_ptr, no); */
-
-/*   if (ctrl->no_server && ctrl->status_fd == -1) */
-/*     ; /\* No status wanted. *\/ */
-/*   else if (ctrl->no_server) */
-/*     { */
-/*       if (!statusfp) */
-/*         { */
-/*           if (ctrl->status_fd == 1) */
-/*             statusfp = stdout; */
-/*           else if (ctrl->status_fd == 2) */
-/*             statusfp = stderr; */
-/*           else */
-/*             statusfp = fdopen (ctrl->status_fd, "w"); */
-      
-/*           if (!statusfp) */
-/*             { */
-/*               log_fatal ("can't open fd %d for status output: %s\n", */
-/*                          ctrl->status_fd, strerror(errno)); */
-/*             } */
-/*         } */
-      
-/*       fputs ("[GNUPG:] ", statusfp); */
-/*       fputs (get_status_string (no), statusfp); */
-    
-/*       while ( (text = va_arg (arg_ptr, const char*) )) */
-/*         { */
-/*           putc ( ' ', statusfp ); */
-/*           for (; *text; text++)  */
-/*             { */
-/*               if (*text == '\n') */
-/*                 fputs ( "\\n", statusfp ); */
-/*               else if (*text == '\r') */
-/*                 fputs ( "\\r", statusfp ); */
-/*               else  */
-/*                 putc ( *(const byte *)text,  statusfp ); */
-/*             } */
-/*         } */
-/*       putc ('\n', statusfp); */
-/*       fflush (statusfp); */
-/*     } */
-/*   else  */
-/*     { */
-/*       assuan_context_t ctx = ctrl->server_local->assuan_ctx; */
-/*       char buf[950], *p; */
-/*       size_t n; */
-
-/*       p = buf;  */
-/*       n = 0; */
-/*       while ( (text = va_arg (arg_ptr, const char *)) ) */
-/*         { */
-/*           if (n) */
-/*             { */
-/*               *p++ = ' '; */
-/*               n++; */
-/*             } */
-/*           for ( ; *text && n < DIM (buf)-2; n++) */
-/*             *p++ = *text++; */
-/*         } */
-/*       *p = 0; */
-/*       err = assuan_write_status (ctx, get_status_string (no), buf); */
-/*     } */
-
-/*   va_end (arg_ptr); */
-/*   return err; */
-/* } */
-
-/* gpg_error_t */
-/* gpgsm_status (ctrl_t ctrl, int no, const char *text) */
-/* { */
-/*   return gpgsm_status2 (ctrl, no, text, NULL); */
-/* } */
-
-/* gpg_error_t */
-/* gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, */
-/*                             gpg_err_code_t ec) */
-/* { */
-/*   char buf[30]; */
-
-/*   sprintf (buf, "%u", (unsigned int)ec); */
-/*   if (text) */
-/*     return gpgsm_status2 (ctrl, no, text, buf, NULL); */
-/*   else */
-/*     return gpgsm_status2 (ctrl, no, buf, NULL); */
-/* } */
+      p = buf;
+      n = 0;
+      while ( (text = va_arg (arg_ptr, const char *)) )
+        {
+          if (n)
+            {
+              *p++ = ' ';
+              n++;
+            }
+          for ( ; *text && n < DIM (buf)-2; n++)
+            *p++ = *text++;
+        }
+      *p = 0;
+      err = assuan_write_status (ctx, get_status_string (no), buf);
+    }
+
+  va_end (arg_ptr);
+  return err;
+}
 
 
 /* Helper to notify the client about Pinentry events.  Returns an gpg
@@ -740,5 +773,26 @@ g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
 }
 
 
+/*
+ * Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result
+ * at (R_KEYBLOB, R_KEYBLOBLEN).  Returns 0 on success or an error
+ * code.  On error R_KEYBLOB is set to NULL.
+ *
+ * This actually does not belong here but for that simple wrapper it
+ * does not make sense to add another source file.  Note that we do
+ * not want to have this in keyblob.c, because that code is also used
+ * by the syshelp.
+ */
+gpg_error_t
+g13_keyblob_decrypt (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen,
+                     void **r_keyblob, size_t *r_keybloblen)
+{
+  gpg_error_t err;
 
+  /* FIXME:  For now we only implement OpenPGP.  */
+  err = gpg_decrypt_blob (ctrl, opt.gpg_program, opt.gpg_arguments,
+                          enckeyblob, enckeybloblen,
+                          r_keyblob, r_keybloblen);
 
+  return err;
+}