doc: Typo fixes.
[gnupg.git] / g10 / server.c
index d0e801b..b89f0be 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 <ctype.h>
 #include <unistd.h>
 
-#include <assuan.h>
 
 #include "gpg.h"
+#include <assuan.h>
 #include "util.h"
 #include "i18n.h"
 #include "options.h"
+#include "../common/server-help.h"
 #include "../common/sysutils.h"
+#include "status.h"
 
 
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
 
 /* Data used to associate an Assuan context with local server data.  */
-struct server_local_s 
+struct server_local_s
 {
   /* Our current Assuan context. */
-  assuan_context_t assuan_ctx;  
+  assuan_context_t assuan_ctx;
   /* File descriptor as set by the MESSAGE command. */
-  gnupg_fd_t message_fd;               
+  gnupg_fd_t message_fd;
+
+  /* List of prepared recipients.  */
+  pk_list_t recplist;
+
+  /* Set if pinentry notifications should be passed back to the
+     client. */
+  int allow_pinentry_notify;
 };
 
 
 \f
 /* Helper to close the message fd if it is open. */
-static void 
+static void
 close_message_fd (ctrl_t ctrl)
 {
   if (ctrl->server_local->message_fd != GNUPG_INVALID_FD)
     {
       assuan_sock_close (ctrl->server_local->message_fd);
       ctrl->server_local->message_fd = GNUPG_INVALID_FD;
-    } 
+    }
 }
 
-
 \f
 /* Called by libassuan for Assuan options.  See the Assuan manual for
    details. */
-static int
+static gpg_error_t
 option_handler (assuan_context_t ctx, const char *key, const char *value)
 {
-/*   ctrl_t ctrl = assuan_get_pointer (ctx); */
+  ctrl_t ctrl = assuan_get_pointer (ctx);
 
-  (void)ctx;
   (void)value;
 
   /* Fixme: Implement the tty and locale args. */
@@ -98,6 +105,10 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
     {
       /* This is for now a dummy option. */
     }
+  else if (!strcmp (key, "allow-pinentry-notify"))
+    {
+      ctrl->server_local->allow_pinentry_notify = 1;
+    }
   else
     return gpg_error (GPG_ERR_UNKNOWN_OPTION);
 
@@ -106,20 +117,26 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
 
 
 /* Called by libassuan for RESET commands. */
-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;
+
+  release_pk_list (ctrl->server_local->recplist);
+  ctrl->server_local->recplist = NULL;
+
   close_message_fd (ctrl);
   assuan_close_input_fd (ctx);
   assuan_close_output_fd (ctx);
+  return 0;
 }
 
 
 /* Called by libassuan for INPUT commands. */
-static void
-input_notify (assuan_context_t ctx, const char *line)
+static gpg_error_t
+input_notify (assuan_context_t ctx, char *line)
 {
 /*   ctrl_t ctrl = assuan_get_pointer (ctx); */
 
@@ -135,15 +152,16 @@ input_notify (assuan_context_t ctx, const char *line)
     {
       /* FIXME (autodetect encoding) */
     }
+  return 0;
 }
 
 
 /* Called by libassuan for OUTPUT commands. */
-static void
-output_notify (assuan_context_t ctx, const char *line)
+static gpg_error_t
+output_notify (assuan_context_t ctx, char *line)
 {
 /*   ctrl_t ctrl = assuan_get_pointer (ctx); */
-  
+
   (void)ctx;
 
   if (strstr (line, "--armor"))
@@ -152,12 +170,14 @@ output_notify (assuan_context_t ctx, const char *line)
     {
       /* FIXME */
     }
+  return 0;
 }
 
 
 
 \f
-/*  RECIPIENT <userID>
+/*  RECIPIENT [--hidden] <userID>
+    RECIPIENT [--hidden] --file <filename>
 
    Set the recipient for the encryption.  <userID> should be the
    internal representation of the key; the server may accept any other
@@ -168,12 +188,30 @@ output_notify (assuan_context_t ctx, const char *line)
    encrypt at all if not all recipients are valid, the client has to
    take care of this.  All RECIPIENT commands are cumulative until a
    RESET or an successful ENCRYPT command.  */
-static int 
+static gpg_error_t
 cmd_recipient (assuan_context_t ctx, char *line)
 {
-  (void)ctx;
-  (void)line;
-  return gpg_error (GPG_ERR_NOT_SUPPORTED);
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  int hidden, file;
+
+  hidden = has_option (line,"--hidden");
+  file = has_option (line,"--file");
+  line = skip_options (line);
+
+  /* FIXME: Expand groups
+  if (opt.grouplist)
+    remusr = expand_group (rcpts);
+  else
+    remusr = rcpts;
+  */
+
+  err = find_and_check_key (ctrl, line, PUBKEY_USAGE_ENC, hidden, file,
+                            &ctrl->server_local->recplist);
+
+  if (err)
+    log_error ("command '%s' failed: %s\n", "RECIPIENT", gpg_strerror (err));
+  return err;
 }
 
 
@@ -188,12 +226,12 @@ cmd_recipient (assuan_context_t ctx, char *line)
    then not be done for this key.  If the policy is not to sign at all
    if not all signer keys are valid, the client has to take care of
    this.  All SIGNER commands are cumulative until a RESET but they
-   are *not* reset by an SIGN command becuase it can be expected that
+   are *not* reset by an SIGN command because it can be expected that
    set of signers are used for more than one sign operation.
 
    Note that this command returns an INV_RECP status which is a bit
    strange, but they are very similar.  */
-static int 
+static gpg_error_t
 cmd_signer (assuan_context_t ctx, char *line)
 {
   (void)ctx;
@@ -203,39 +241,116 @@ cmd_signer (assuan_context_t ctx, char *line)
 
 
 \f
-/*  ENCRYPT 
+/*  ENCRYPT
 
    Do the actual encryption process.  Takes the plaintext from the
-   INPUT command, writes to the ciphertext to the file descriptor set
-   with the OUTPUT command, take the recipients form all the
-   recipients set so far.  If this command fails the clients should
-   try to delete all output currently done or otherwise mark it as
-   invalid.  GPG does ensure that there won't be any security problem
-   with leftover data on the output in this case.
-
-   This command should in general not fail, as all necessary checks
-   have been done while setting the recipients.  The input and output
-   pipes are closed.  */
-static int 
+   INPUT command, writes the ciphertext to the file descriptor set
+   with the OUTPUT command, take the recipients from all the
+   recipients set so far with RECIPIENTS.
+
+   If this command fails the clients should try to delete all output
+   currently done or otherwise mark it as invalid.  GPG does ensure
+   that there won't be any security problem with leftover data on the
+   output in this case.
+
+   In most cases this command won't fail because most necessary checks
+   have been done while setting the recipients.  However some checks
+   can only be done right here and thus error may occur anyway (for
+   example, no recipients at all).
+
+   The input, output and message pipes are closed after this
+   command.  */
+static gpg_error_t
 cmd_encrypt (assuan_context_t ctx, char *line)
 {
-  (void)ctx;
-  (void)line;
-  return gpg_error (GPG_ERR_NOT_SUPPORTED);
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  int inp_fd, out_fd;
+
+  (void)line; /* LINE is not used.  */
+
+  if ( !ctrl->server_local->recplist )
+    {
+      write_status_text (STATUS_NO_RECP, "0");
+      err = gpg_error (GPG_ERR_NO_USER_ID);
+      goto leave;
+    }
+
+  inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+  if (inp_fd == -1)
+    {
+      err = set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+      goto leave;
+    }
+  out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+  if (out_fd == -1)
+    {
+      err = set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+      goto leave;
+    }
+
+
+  /* FIXME: GPGSM does this here: Add all encrypt-to marked recipients
+     from the default list. */
+
+  /* fixme: err = ctrl->audit? 0 : start_audit_session (ctrl);*/
+
+  err = encrypt_crypt (ctrl, inp_fd, NULL, NULL, 0,
+                       ctrl->server_local->recplist,
+                       out_fd);
+
+ leave:
+  /* Release the recipient list on success.  */
+  if (!err)
+    {
+      release_pk_list (ctrl->server_local->recplist);
+      ctrl->server_local->recplist = NULL;
+    }
+
+  /* Close and reset the fds. */
+  close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+
+  if (err)
+    log_error ("command '%s' failed: %s\n", "ENCRYPT", gpg_strerror (err));
+  return err;
 }
 
 
 \f
 /*  DECRYPT
 
-   This performs the decrypt operation after doing some checks on the
-   internal state (e.g. that only needed data has been set).   */
-static int 
+    This performs the decrypt operation.  */
+static gpg_error_t
 cmd_decrypt (assuan_context_t ctx, char *line)
 {
-  (void)ctx;
-  (void)line;
-  return gpg_error (GPG_ERR_NOT_SUPPORTED);
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  int inp_fd, out_fd;
+
+  (void)line; /* LINE is not used.  */
+
+  inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+  if (inp_fd == -1)
+    return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+  out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+  if (out_fd == -1)
+    return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+
+  glo_ctrl.lasterr = 0;
+  err = decrypt_message_fd (ctrl, inp_fd, out_fd);
+  if (!err)
+    err = glo_ctrl.lasterr;
+
+  /* Close and reset the fds. */
+  close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+
+  if (err)
+    log_error ("command '%s' failed: %s\n", "DECRYPT", gpg_strerror (err));
+  return err;
 }
 
 
@@ -245,18 +360,26 @@ cmd_decrypt (assuan_context_t ctx, char *line)
    This does a verify operation on the message send to the input-FD.
    The result is written out using status lines.  If an output FD was
    given, the signed text will be written to that.
-  
+
    If the signature is a detached one, the server will inquire about
    the signed material and the client must provide it.
  */
-static int 
+static gpg_error_t
 cmd_verify (assuan_context_t ctx, char *line)
 {
   int rc;
+#ifdef HAVE_W32_SYSTEM
+  (void)ctx;
+  (void)line;
+  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#else
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gnupg_fd_t fd = assuan_get_input_fd (ctx);
   gnupg_fd_t out_fd = assuan_get_output_fd (ctx);
-  FILE *out_fp = NULL;
+  estream_t out_fp = NULL;
+
+  /* FIXME: Revamp this code it is nearly to 3 years old and was only
+     intended as a quick test.  */
 
   (void)line;
 
@@ -265,27 +388,33 @@ cmd_verify (assuan_context_t ctx, char *line)
 
   if (out_fd != GNUPG_INVALID_FD)
     {
-      out_fp = fdopen ( dup (FD2INT (out_fd)), "w");
+      es_syshd_t syshd;
+
+#ifdef HAVE_W32_SYSTEM
+      syshd.type = ES_SYSHD_HANDLE;
+      syshd.u.handle = out_fd;
+#else
+      syshd.type = ES_SYSHD_FD;
+      syshd.u.fd = out_fd;
+#endif
+      out_fp = es_sysopen_nc (&syshd, "w");
       if (!out_fp)
-        return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+        return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
     }
 
-  log_debug ("WARNING: The server mode work "
-             "in progress and not ready for use\n");
+  log_debug ("WARNING: The server mode is WORK "
+             "IN PROGRESS and not ready for use\n");
 
-  /* Need to dup it because it might get closed and libassuan won't
-     know about it then. */
-  rc = gpg_verify (ctrl,
-                   dup ( FD2INT (fd)), 
-                   dup ( FD2INT (ctrl->server_local->message_fd)),
-                   out_fp);
+  rc = gpg_verify (ctrl, fd, ctrl->server_local->message_fd, out_fp);
 
-  if (out_fp)
-    fclose (out_fp);
+  es_fclose (out_fp);
   close_message_fd (ctrl);
   assuan_close_input_fd (ctx);
   assuan_close_output_fd (ctx);
+#endif
 
+  if (rc)
+    log_error ("command '%s' failed: %s\n", "VERIFY", gpg_strerror (rc));
   return rc;
 }
 
@@ -296,7 +425,7 @@ cmd_verify (assuan_context_t ctx, char *line)
    Sign the data set with the INPUT command and write it to the sink
    set by OUTPUT.  With "--detached" specified, a detached signature
    is created.  */
-static int 
+static gpg_error_t
 cmd_sign (assuan_context_t ctx, char *line)
 {
   (void)ctx;
@@ -310,7 +439,7 @@ cmd_sign (assuan_context_t ctx, char *line)
 
   Import keys as read from the input-fd, return status message for
   each imported one.  The import checks the validity of the key.  */
-static int 
+static gpg_error_t
 cmd_import (assuan_context_t ctx, char *line)
 {
   (void)ctx;
@@ -330,7 +459,7 @@ cmd_import (assuan_context_t ctx, char *line)
    Recall that in general the output format is set with the OUTPUT
    command.
  */
-static int 
+static gpg_error_t
 cmd_export (assuan_context_t ctx, char *line)
 {
   (void)ctx;
@@ -344,7 +473,7 @@ cmd_export (assuan_context_t ctx, char *line)
 
     Fixme
 */
-static int 
+static gpg_error_t
 cmd_delkeys (assuan_context_t ctx, char *line)
 {
   (void)ctx;
@@ -358,7 +487,7 @@ cmd_delkeys (assuan_context_t ctx, char *line)
 
    Set the file descriptor to read a message which is used with
    detached signatures.  */
-static int 
+static gpg_error_t
 cmd_message (assuan_context_t ctx, char *line)
 {
   int rc;
@@ -381,7 +510,7 @@ cmd_message (assuan_context_t ctx, char *line)
 
    fixme
 */
-static int 
+static gpg_error_t
 do_listkeys (assuan_context_t ctx, char *line, int mode)
 {
   (void)ctx;
@@ -392,14 +521,14 @@ do_listkeys (assuan_context_t ctx, char *line, int mode)
 }
 
 
-static int 
+static gpg_error_t
 cmd_listkeys (assuan_context_t ctx, char *line)
 {
   return do_listkeys (ctx, line, 3);
 }
 
 
-static int 
+static gpg_error_t
 cmd_listsecretkeys (assuan_context_t ctx, char *line)
 {
   return do_listkeys (ctx, line, 2);
@@ -412,7 +541,7 @@ cmd_listsecretkeys (assuan_context_t ctx, char *line)
    Read the parameters in native format from the input fd and create a
    new OpenPGP key.
  */
-static int 
+static gpg_error_t
 cmd_genkey (assuan_context_t ctx, char *line)
 {
   (void)ctx;
@@ -430,7 +559,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
      pid         - Return the process id of the server.
 
  */
-static int
+static gpg_error_t
 cmd_getinfo (assuan_context_t ctx, char *line)
 {
   int rc;
@@ -452,16 +581,37 @@ cmd_getinfo (assuan_context_t ctx, char *line)
   return rc;
 }
 
+static const char hlp_passwd[] =
+  "PASSWD <userID>\n"
+  "\n"
+  "Change the passphrase of the secret key for USERID.";
+static gpg_error_t
+cmd_passwd (assuan_context_t ctx, char *line)
+{
+  /* ctrl_t ctrl = assuan_get_pointer (ctx); */
+  gpg_error_t err;
+
+  (void)ctx;
+  (void)line;
+  /* line = skip_options (line); */
+
+  err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+  return err;
+}
+
+
 
 \f
 /* Helper to register our commands with libassuan. */
 static int
 register_commands (assuan_context_t ctx)
 {
-  static struct 
+  static struct
   {
     const char *name;
-    int (*handler)(assuan_context_t, char *line);
+    assuan_handler_t handler;
+    const char * const help;
   } table[] = {
     { "RECIPIENT",     cmd_recipient },
     { "SIGNER",        cmd_signer    },
@@ -471,24 +621,26 @@ register_commands (assuan_context_t ctx)
     { "SIGN",          cmd_sign      },
     { "IMPORT",        cmd_import    },
     { "EXPORT",        cmd_export    },
-    { "INPUT",         NULL          }, 
-    { "OUTPUT",        NULL          }, 
+    { "INPUT",         NULL          },
+    { "OUTPUT",        NULL          },
     { "MESSAGE",       cmd_message   },
     { "LISTKEYS",      cmd_listkeys  },
     { "LISTSECRETKEYS",cmd_listsecretkeys },
     { "GENKEY",        cmd_genkey    },
     { "DELKEYS",       cmd_delkeys   },
     { "GETINFO",       cmd_getinfo   },
+    { "PASSWD",        cmd_passwd,  hlp_passwd},
     { NULL }
   };
   int i, rc;
 
   for (i=0; table[i].name; i++)
     {
-      rc = assuan_register_command (ctx, table[i].name, table[i].handler);
+      rc = assuan_register_command (ctx, table[i].name,
+                                    table[i].handler, table[i].help);
       if (rc)
         return rc;
-    } 
+    }
   return 0;
 }
 
@@ -501,17 +653,33 @@ int
 gpg_server (ctrl_t ctrl)
 {
   int rc;
+#ifndef HAVE_W32_SYSTEM
   int filedes[2];
-  assuan_context_t ctx;
+#endif
+  assuan_context_t ctx = NULL;
   static const char hello[] = ("GNU Privacy Guard's OpenPGP server "
                                VERSION " ready");
 
   /* 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 FILEDES in this case.  */
-  filedes[0] = 0;
-  filedes[1] = 1;
-  rc = assuan_init_pipe_server (&ctx, filedes);
+#ifndef HAVE_W32_SYSTEM
+  filedes[0] = assuan_fdopen (0);
+  filedes[1] = assuan_fdopen (1);
+#endif
+  rc = assuan_new (&ctx);
+  if (rc)
+    {
+      log_error ("failed to allocate the assuan context: %s\n",
+                gpg_strerror (rc));
+      goto leave;
+    }
+
+#ifdef HAVE_W32_SYSTEM
+  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#else
+  rc = assuan_init_pipe_server (ctx, filedes);
+#endif
   if (rc)
     {
       log_error ("failed to initialize the server: %s\n", gpg_strerror (rc));
@@ -529,21 +697,18 @@ gpg_server (ctrl_t ctrl)
   assuan_set_pointer (ctx, ctrl);
   if (opt.verbose || opt.debug)
     {
-      char *tmp = NULL;
-      const char *s1 = getenv ("GPG_AGENT_INFO");
-
-      if (asprintf (&tmp,
-                    "Home: %s\n"
-                    "Config: %s\n"
-                    "AgentInfo: %s\n"
-                    "%s",
-                    opt.homedir,
-                    "fixme: need config filename",
-                    s1?s1:"[not set]",
-                    hello) > 0)
+      char *tmp;
+
+      tmp = xtryasprintf ("Home: %s\n"
+                          "Config: %s\n"
+                          "%s",
+                          gnupg_homedir (),
+                          "fixme: need config filename",
+                          hello);
+      if (tmp)
         {
           assuan_set_hello_line (ctx, tmp);
-          free (tmp);
+          xfree (tmp);
         }
     }
   else
@@ -562,9 +727,6 @@ gpg_server (ctrl_t ctrl)
   ctrl->server_local->assuan_ctx = ctx;
   ctrl->server_local->message_fd = GNUPG_INVALID_FD;
 
-  if (DBG_ASSUAN)
-    assuan_set_log_stream (ctx, log_get_stream ());
-
   for (;;)
     {
       rc = assuan_accept (ctx);
@@ -578,7 +740,7 @@ gpg_server (ctrl_t ctrl)
           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
           break;
         }
-      
+
       rc = assuan_process (ctx);
       if (rc)
         {
@@ -588,9 +750,53 @@ gpg_server (ctrl_t ctrl)
     }
 
  leave:
-  xfree (ctrl->server_local);
-  ctrl->server_local = NULL;
-  assuan_deinit_server (ctx);
+  if (ctrl->server_local)
+    {
+      release_pk_list (ctrl->server_local->recplist);
+
+      xfree (ctrl->server_local);
+      ctrl->server_local = NULL;
+    }
+  assuan_release (ctx);
   return rc;
 }
 
+
+/* Helper to notify the client about Pinentry events.  Because that
+   might disturb some older clients, this is only done when enabled
+   via an option.  If it is not enabled we tell Windows to allow
+   setting the foreground window right here.  Returns an gpg error
+   code. */
+gpg_error_t
+gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
+{
+  if (opt.verbose)
+    {
+      char *linecopy = xtrystrdup (line);
+      char *fields[4];
+
+      if (linecopy
+          && split_fields (linecopy, fields, DIM (fields)) >= 4
+          && !strcmp (fields[0], "PINENTRY_LAUNCHED"))
+        log_info (_("pinentry launched (pid %s, flavor %s, version %s)\n"),
+                  fields[1], fields[2], fields[3]);
+
+      xfree (linecopy);
+    }
+
+  if (!ctrl || !ctrl->server_local
+      || !ctrl->server_local->allow_pinentry_notify)
+    {
+      gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
+      /* Client might be interested in that event - send as status line.  */
+      if (!strncmp (line, "PINENTRY_LAUNCHED", 17)
+          && (line[17]==' '||!line[17]))
+        {
+          for (line += 17; *line && spacep (line); line++)
+            ;
+          write_status_text (STATUS_PINENTRY_LAUNCHED, line);
+        }
+      return 0;
+    }
+  return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
+}