g10: Prefer to available card keys for decryption.
[gnupg.git] / common / call-gpg.c
index bcad1d6..c1472e9 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 "i18n.h"
 #include "logging.h"
 #include "membuf.h"
+#include "strlist.h"
 #include "util.h"
 
+
+static GPGRT_INLINE gpg_error_t
+my_error_from_syserror (void)
+{
+  return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+}
+
+static GPGRT_INLINE gpg_error_t
+my_error_from_errno (int e)
+{
+  return gpg_err_make (default_errsource, gpg_err_code_from_errno (e));
+}
+
 \f
 /* Fire up a new GPG.  Handle the server's initial greeting.  Returns
    0 on success and stores the assuan context at R_CTX.  */
 static gpg_error_t
-start_gpg (ctrl_t ctrl, const char *gpg_program,
+start_gpg (ctrl_t ctrl, const char *gpg_program, strlist_t gpg_arguments,
            int input_fd, int output_fd, assuan_context_t *r_ctx)
 {
   gpg_error_t err;
   assuan_context_t ctx = NULL;
   const char *pgmname;
-  const char *argv[10];
-  int no_close_list[5];
+  const char **argv;
+  assuan_fd_t no_close_list[5];
   int i;
   char line[ASSUAN_LINELENGTH];
 
@@ -61,7 +75,7 @@ start_gpg (ctrl_t ctrl, const char *gpg_program,
       return err;
     }
 
-  /* The first time we are used, intialize the gpg_program variable.  */
+  /* The first time we are used, initialize the gpg_program variable.  */
   if ( !gpg_program || !*gpg_program )
     gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
 
@@ -73,18 +87,22 @@ start_gpg (ctrl_t ctrl, const char *gpg_program,
 
   if (fflush (NULL))
     {
-      err = gpg_error_from_syserror ();
+      err = my_error_from_syserror ();
       log_error ("error flushing pending output: %s\n", gpg_strerror (err));
       return err;
     }
 
+  argv = xtrycalloc (strlist_length (gpg_arguments) + 3, sizeof *argv);
+  if (argv == NULL)
+    {
+      err = my_error_from_syserror ();
+      return err;
+    }
   i = 0;
   argv[i++] = pgmname;
   argv[i++] = "--server";
-  argv[i++] = "-z";
-  argv[i++] = "0";
-  argv[i++] = "--trust-model";
-  argv[i++] = "always";
+  for (; gpg_arguments; gpg_arguments = gpg_arguments->next)
+    argv[i++] = gpg_arguments->d;
   argv[i++] = NULL;
 
   i = 0;
@@ -95,7 +113,7 @@ start_gpg (ctrl_t ctrl, const char *gpg_program,
     no_close_list[i++] = assuan_fd_from_posix_fd (input_fd);
   if (output_fd != -1)
     no_close_list[i++] = assuan_fd_from_posix_fd (output_fd);
-  no_close_list[i] = -1;
+  no_close_list[i] = ASSUAN_INVALID_FD;
 
   /* Connect to GPG and perform initial handshaking.  */
   err = assuan_pipe_connect (ctx, gpg_program, argv, no_close_list,
@@ -151,6 +169,7 @@ struct writer_thread_parms
   int fd;
   const void *data;
   size_t datalen;
+  estream_t stream;
   gpg_error_t *err_addr;
 };
 
@@ -159,9 +178,27 @@ struct writer_thread_parms
 static void *
 writer_thread_main (void *arg)
 {
+  gpg_error_t err = 0;
   struct writer_thread_parms *parm = arg;
-  const char *buffer = parm->data;
-  size_t length = parm->datalen;
+  char _buffer[4096];
+  char *buffer;
+  size_t length;
+
+  if (parm->stream)
+    {
+      buffer = _buffer;
+      err = es_read (parm->stream, buffer, sizeof _buffer, &length);
+      if (err)
+        {
+          log_error ("reading stream failed: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+    }
+  else
+    {
+      buffer = (char *) parm->data;
+      length = parm->datalen;
+    }
 
   while (length)
     {
@@ -172,13 +209,33 @@ writer_thread_main (void *arg)
         {
           if (errno == EINTR)
             continue;
-          *parm->err_addr = gpg_error_from_syserror ();
+          err = my_error_from_syserror ();
           break; /* Write error.  */
         }
       length -= nwritten;
-      buffer += nwritten;
+
+      if (parm->stream)
+        {
+          if (length == 0)
+            {
+              err = es_read (parm->stream, buffer, sizeof _buffer, &length);
+              if (err)
+                {
+                  log_error ("reading stream failed: %s\n",
+                             gpg_strerror (err));
+                  break;
+                }
+              if (length == 0)
+                /* We're done.  */
+                break;
+            }
+        }
+      else
+        buffer += nwritten;
     }
 
+ leave:
+  *parm->err_addr = err;
   if (close (parm->fd))
     log_error ("closing writer fd %d failed: %s\n", parm->fd, strerror (errno));
   xfree (parm);
@@ -192,7 +249,7 @@ writer_thread_main (void *arg)
    variable to receive a possible write error after the thread has
    finished.  */
 static gpg_error_t
-start_writer (int fd, const void *data, size_t datalen,
+start_writer (int fd, const void *data, size_t datalen, estream_t stream,
               npth_t *r_thread, gpg_error_t *err_addr)
 {
   gpg_error_t err;
@@ -206,10 +263,11 @@ start_writer (int fd, const void *data, size_t datalen,
 
   parm = xtrymalloc (sizeof *parm);
   if (!parm)
-    return gpg_error_from_syserror ();
+    return my_error_from_syserror ();
   parm->fd = fd;
   parm->data = data;
   parm->datalen = datalen;
+  parm->stream = stream;
   parm->err_addr = err_addr;
 
   npth_attr_init (&tattr);
@@ -218,7 +276,7 @@ start_writer (int fd, const void *data, size_t datalen,
   ret = npth_create (&thread, &tattr, writer_thread_main, parm);
   if (ret)
     {
-      err = gpg_error_from_errno (ret);
+      err = my_error_from_errno (ret);
       log_error ("error spawning writer thread: %s\n", gpg_strerror (err));
     }
   else
@@ -239,6 +297,7 @@ struct reader_thread_parms
 {
   int fd;
   membuf_t *mb;
+  estream_t stream;
   gpg_error_t *err_addr;
 };
 
@@ -247,6 +306,7 @@ struct reader_thread_parms
 static void *
 reader_thread_main (void *arg)
 {
+  gpg_error_t err = 0;
   struct reader_thread_parms *parm = arg;
   char buffer[4096];
   int nread;
@@ -257,13 +317,33 @@ reader_thread_main (void *arg)
         {
           if (errno == EINTR)
             continue;
-          *parm->err_addr = gpg_error_from_syserror ();
+          err = my_error_from_syserror ();
           break;  /* Read error.  */
         }
 
-      put_membuf (parm->mb, buffer, nread);
+      if (parm->stream)
+        {
+          const char *p = buffer;
+          size_t nwritten;
+          while (nread)
+            {
+              err = es_write (parm->stream, p, nread, &nwritten);
+              if (err)
+                {
+                  log_error ("writing stream failed: %s\n",
+                             gpg_strerror (err));
+                  goto leave;
+                }
+              nread -= nwritten;
+              p += nwritten;
+            }
+        }
+      else
+        put_membuf (parm->mb, buffer, nread);
     }
 
+ leave:
+  *parm->err_addr = err;
   if (close (parm->fd))
     log_error ("closing reader fd %d failed: %s\n", parm->fd, strerror (errno));
   xfree (parm);
@@ -276,7 +356,8 @@ reader_thread_main (void *arg)
    is stored at R_TID.  After the thread has finished an error from
    the thread will be stored at ERR_ADDR.  */
 static gpg_error_t
-start_reader (int fd, membuf_t *mb, npth_t *r_thread, gpg_error_t *err_addr)
+start_reader (int fd, membuf_t *mb, estream_t stream,
+              npth_t *r_thread, gpg_error_t *err_addr)
 {
   gpg_error_t err;
   struct reader_thread_parms *parm;
@@ -289,9 +370,10 @@ start_reader (int fd, membuf_t *mb, npth_t *r_thread, gpg_error_t *err_addr)
 
   parm = xtrymalloc (sizeof *parm);
   if (!parm)
-    return gpg_error_from_syserror ();
+    return my_error_from_syserror ();
   parm->fd = fd;
   parm->mb = mb;
+  parm->stream = stream;
   parm->err_addr = err_addr;
 
   npth_attr_init (&tattr);
@@ -300,7 +382,7 @@ start_reader (int fd, membuf_t *mb, npth_t *r_thread, gpg_error_t *err_addr)
   ret = npth_create (&thread, &tattr, reader_thread_main, parm);
   if (ret)
     {
-      err = gpg_error_from_errno (ret);
+      err = my_error_from_errno (ret);
       log_error ("error spawning reader thread: %s\n", gpg_strerror (err));
     }
   else
@@ -321,11 +403,15 @@ start_reader (int fd, membuf_t *mb, npth_t *r_thread, gpg_error_t *err_addr)
 
 
  */
-gpg_error_t
-gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
-                  const void *plain, size_t plainlen,
-                  strlist_t keys,
-                  void **r_ciph, size_t *r_ciphlen)
+static gpg_error_t
+_gpg_encrypt (ctrl_t ctrl,
+              const char *gpg_program,
+              strlist_t gpg_arguments,
+              const void *plain, size_t plainlen,
+              estream_t plain_stream,
+              strlist_t keys,
+              membuf_t *reader_mb,
+              estream_t cipher_stream)
 {
   gpg_error_t err;
   assuan_context_t ctx = NULL;
@@ -334,21 +420,19 @@ gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
   npth_t writer_thread = (npth_t)0;
   npth_t reader_thread = (npth_t)0;
   gpg_error_t writer_err, reader_err;
-  membuf_t reader_mb;
   char line[ASSUAN_LINELENGTH];
   strlist_t sl;
   int ret;
 
-  *r_ciph = NULL;
-  *r_ciphlen = 0;
-
-  /* Init the memory buffer to receive the encrypted stuff.  */
-  init_membuf (&reader_mb, 4096);
+  /* Make sure that either the stream interface xor the buffer
+     interface is used.  */
+  assert ((plain == NULL) != (plain_stream == NULL));
+  assert ((reader_mb == NULL) != (cipher_stream == NULL));
 
   /* Create two pipes.  */
-  err = gnupg_create_outbound_pipe (outbound_fds);
+  err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0);
   if (!err)
-    err = gnupg_create_inbound_pipe (inbound_fds);
+    err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0);
   if (err)
     {
       log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
@@ -356,14 +440,15 @@ gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
     }
 
   /* Start GPG and send the INPUT and OUTPUT commands.  */
-  err = start_gpg (ctrl, gpg_program, outbound_fds[0], inbound_fds[1], &ctx);
+  err = start_gpg (ctrl, gpg_program, gpg_arguments,
+                   outbound_fds[0], inbound_fds[1], &ctx);
   if (err)
     goto leave;
   close (outbound_fds[0]); outbound_fds[0] = -1;
   close (inbound_fds[1]); inbound_fds[1] = -1;
 
   /* Start a writer thread to feed the INPUT command of the server.  */
-  err = start_writer (outbound_fds[1], plain, plainlen,
+  err = start_writer (outbound_fds[1], plain, plainlen, plain_stream,
                       &writer_thread, &writer_err);
   if (err)
     return err;
@@ -371,7 +456,7 @@ gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
 
   /* Start a reader thread to eat from the OUTPUT command of the
      server.  */
-  err = start_reader (inbound_fds[0], &reader_mb,
+  err = start_reader (inbound_fds[0], reader_mb, cipher_stream,
                       &reader_thread, &reader_err);
   if (err)
     return err;
@@ -402,7 +487,7 @@ gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
   ret = npth_join (reader_thread, NULL);
   if (ret)
     {
-      err = gpg_error_from_errno (ret);
+      err = my_error_from_errno (ret);
       log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err));
       goto leave;
     }
@@ -419,7 +504,7 @@ gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
   ret = npth_join (writer_thread, NULL);
   if (ret)
     {
-      err = gpg_error_from_errno (ret);
+      err = my_error_from_errno (ret);
       log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err));
       goto leave;
     }
@@ -431,16 +516,6 @@ gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
       goto leave;
     }
 
-  /* Return the data.  */
-  *r_ciph = get_membuf (&reader_mb, r_ciphlen);
-  if (!*r_ciph)
-    {
-      err = gpg_error_from_syserror ();
-      log_error ("error while storing the data in the reader thread: %s\n",
-                 gpg_strerror (err));
-      goto leave;
-    }
-
  leave:
   /* FIXME: Not valid, as npth_t is an opaque type.  */
   if (reader_thread)
@@ -456,20 +531,73 @@ gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program,
   if (inbound_fds[1] != -1)
     close (inbound_fds[1]);
   release_gpg (ctx);
-  xfree (get_membuf (&reader_mb, NULL));
   return err;
 }
 
+gpg_error_t
+gpg_encrypt_blob (ctrl_t ctrl,
+                  const char *gpg_program,
+                  strlist_t gpg_arguments,
+                  const void *plain, size_t plainlen,
+                  strlist_t keys,
+                  void **r_ciph, size_t *r_ciphlen)
+{
+  gpg_error_t err;
+  membuf_t reader_mb;
+
+  *r_ciph = NULL;
+  *r_ciphlen = 0;
+
+  /* Init the memory buffer to receive the encrypted stuff.  */
+  init_membuf (&reader_mb, 4096);
+
+  err = _gpg_encrypt (ctrl, gpg_program, gpg_arguments,
+                      plain, plainlen, NULL,
+                      keys,
+                      &reader_mb, NULL);
+
+  if (! err)
+    {
+      /* Return the data.  */
+      *r_ciph = get_membuf (&reader_mb, r_ciphlen);
+      if (!*r_ciph)
+        {
+          err = my_error_from_syserror ();
+          log_error ("error while storing the data in the reader thread: %s\n",
+                     gpg_strerror (err));
+        }
+    }
 
+  xfree (get_membuf (&reader_mb, NULL));
+  return err;
+}
+
+gpg_error_t
+gpg_encrypt_stream (ctrl_t ctrl,
+                    const char *gpg_program,
+                    strlist_t gpg_arguments,
+                    estream_t plain_stream,
+                    strlist_t keys,
+                    estream_t cipher_stream)
+{
+  return _gpg_encrypt (ctrl, gpg_program, gpg_arguments,
+                       NULL, 0, plain_stream,
+                       keys,
+                       NULL, cipher_stream);
+}
 \f
 /* Call GPG to decrypt a block of data.
 
 
  */
-gpg_error_t
-gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
-                  const void *ciph, size_t ciphlen,
-                  void **r_plain, size_t *r_plainlen)
+static gpg_error_t
+_gpg_decrypt (ctrl_t ctrl,
+              const char *gpg_program,
+              strlist_t gpg_arguments,
+              const void *ciph, size_t ciphlen,
+              estream_t cipher_stream,
+              membuf_t *reader_mb,
+              estream_t plain_stream)
 {
   gpg_error_t err;
   assuan_context_t ctx = NULL;
@@ -478,19 +606,17 @@ gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
   npth_t writer_thread = (npth_t)0;
   npth_t reader_thread = (npth_t)0;
   gpg_error_t writer_err, reader_err;
-  membuf_t reader_mb;
   int ret;
 
-  *r_plain = NULL;
-  *r_plainlen = 0;
-
-  /* Init the memory buffer to receive the encrypted stuff.  */
-  init_membuf_secure (&reader_mb, 1024);
+  /* Make sure that either the stream interface xor the buffer
+     interface is used.  */
+  assert ((ciph == NULL) != (cipher_stream == NULL));
+  assert ((reader_mb == NULL) != (plain_stream == NULL));
 
   /* Create two pipes.  */
-  err = gnupg_create_outbound_pipe (outbound_fds);
+  err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0);
   if (!err)
-    err = gnupg_create_inbound_pipe (inbound_fds);
+    err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0);
   if (err)
     {
       log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
@@ -498,14 +624,15 @@ gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
     }
 
   /* Start GPG and send the INPUT and OUTPUT commands.  */
-  err = start_gpg (ctrl, gpg_program, outbound_fds[0], inbound_fds[1], &ctx);
+  err = start_gpg (ctrl, gpg_program, gpg_arguments,
+                   outbound_fds[0], inbound_fds[1], &ctx);
   if (err)
     goto leave;
   close (outbound_fds[0]); outbound_fds[0] = -1;
   close (inbound_fds[1]); inbound_fds[1] = -1;
 
   /* Start a writer thread to feed the INPUT command of the server.  */
-  err = start_writer (outbound_fds[1], ciph, ciphlen,
+  err = start_writer (outbound_fds[1], ciph, ciphlen, cipher_stream,
                       &writer_thread, &writer_err);
   if (err)
     return err;
@@ -513,7 +640,7 @@ gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
 
   /* Start a reader thread to eat from the OUTPUT command of the
      server.  */
-  err = start_reader (inbound_fds[0], &reader_mb,
+  err = start_reader (inbound_fds[0], reader_mb, plain_stream,
                       &reader_thread, &reader_err);
   if (err)
     return err;
@@ -532,7 +659,7 @@ gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
   ret = npth_join (reader_thread, NULL);
   if (ret)
     {
-      err = gpg_error_from_errno (ret);
+      err = my_error_from_errno (ret);
       log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err));
       goto leave;
     }
@@ -548,7 +675,7 @@ gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
   ret = npth_join (writer_thread, NULL);
   if (ret)
     {
-      err = gpg_error_from_errno (ret);
+      err = my_error_from_errno (ret);
       log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err));
       goto leave;
     }
@@ -560,16 +687,6 @@ gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
       goto leave;
     }
 
-  /* Return the data.  */
-  *r_plain = get_membuf (&reader_mb, r_plainlen);
-  if (!*r_plain)
-    {
-      err = gpg_error_from_syserror ();
-      log_error ("error while storing the data in the reader thread: %s\n",
-                 gpg_strerror (err));
-      goto leave;
-    }
-
  leave:
   if (reader_thread)
     npth_detach (reader_thread);
@@ -584,6 +701,53 @@ gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program,
   if (inbound_fds[1] != -1)
     close (inbound_fds[1]);
   release_gpg (ctx);
+  return err;
+}
+
+gpg_error_t
+gpg_decrypt_blob (ctrl_t ctrl,
+                  const char *gpg_program,
+                  strlist_t gpg_arguments,
+                  const void *ciph, size_t ciphlen,
+                  void **r_plain, size_t *r_plainlen)
+{
+  gpg_error_t err;
+  membuf_t reader_mb;
+
+  *r_plain = NULL;
+  *r_plainlen = 0;
+
+  /* Init the memory buffer to receive the encrypted stuff.  */
+  init_membuf_secure (&reader_mb, 1024);
+
+  err = _gpg_decrypt (ctrl, gpg_program, gpg_arguments,
+                      ciph, ciphlen, NULL,
+                      &reader_mb, NULL);
+
+  if (! err)
+    {
+      /* Return the data.  */
+      *r_plain = get_membuf (&reader_mb, r_plainlen);
+      if (!*r_plain)
+        {
+          err = my_error_from_syserror ();
+          log_error ("error while storing the data in the reader thread: %s\n",
+                     gpg_strerror (err));
+        }
+    }
+
   xfree (get_membuf (&reader_mb, NULL));
   return err;
 }
+
+gpg_error_t
+gpg_decrypt_stream (ctrl_t ctrl,
+                    const char *gpg_program,
+                    strlist_t gpg_arguments,
+                    estream_t cipher_stream,
+                    estream_t plain_stream)
+{
+  return _gpg_decrypt (ctrl, gpg_program, gpg_arguments,
+                       NULL, 0, cipher_stream,
+                       NULL, plain_stream);
+}