core: New API gpgme_op_set_uid_flag.
[gpgme.git] / src / engine-gpg.c
index 025657a..6e4b833 100644 (file)
@@ -16,7 +16,7 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with this program; if not, see <http://www.gnu.org/licenses/>.
+   License along with this program; if not, see <https://www.gnu.org/licenses/>.
 */
 
 #if HAVE_CONFIG_H
@@ -74,6 +74,10 @@ struct fd_data_map_s
 };
 
 
+/* NB.: R_LINE is allocated an gpgrt function and thus gpgrt_free
+ * shall be used to release it.  This takes care of custom memory
+ * allocators and avoids problems on Windows with different runtimes
+ * used for libgpg-error/gpgrt and gpgme.  */
 typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline);
 
 struct engine_gpg
@@ -139,6 +143,9 @@ struct engine_gpg
 
   struct gpgme_io_cbs io_cbs;
   gpgme_pinentry_mode_t pinentry_mode;
+
+  /* NULL or the data object fed to --override_session_key-fd.  */
+  gpgme_data_t override_session_key;
 };
 
 typedef struct engine_gpg *engine_gpg_t;
@@ -441,6 +448,8 @@ gpg_release (void *engine)
   if (gpg->cmd.keyword)
     free (gpg->cmd.keyword);
 
+  gpgme_data_release (gpg->override_session_key);
+
   free (gpg);
 }
 
@@ -454,6 +463,7 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,
   char *dft_display = NULL;
   char dft_ttyname[64];
   char *dft_ttytype = NULL;
+  char *env_tty = NULL;
 
   gpg = calloc (1, sizeof *gpg);
   if (!gpg)
@@ -543,6 +553,8 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,
     rc = add_arg (gpg, "utf8");
   if (!rc)
     rc = add_arg (gpg, "--enable-progress-filter");
+  if (!rc && have_gpg_version (gpg, "2.1.11"))
+    rc = add_arg (gpg, "--exit-on-status-write-error");
   if (rc)
     goto leave;
 
@@ -560,11 +572,20 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,
        goto leave;
     }
 
-  if (isatty (1))
+  rc = _gpgme_getenv ("GPG_TTY", &env_tty);
+  if (isatty (1) || env_tty || rc)
     {
-      int err;
+      int err = 0;
 
-      err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+      if (rc)
+        goto leave;
+      else if (env_tty)
+        {
+          snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
+          free (env_tty);
+        }
+      else
+        err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
 
       /* Even though isatty() returns 1, ttyname_r() may fail in many
         ways, e.g., when /dev/pts is not accessible under chroot.  */
@@ -1329,7 +1350,7 @@ read_colon_line (engine_gpg_t gpg)
                         }
                       while (linep && *linep);
 
-                      free (line);
+                      gpgrt_free (line);
                     }
                   else
                     gpg->colon.fnc (gpg->colon.fnc_value, buffer);
@@ -1538,13 +1559,45 @@ add_input_size_hint (engine_gpg_t gpg, gpgme_data_t data)
 
 
 static gpgme_error_t
-gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain,
+             int export_session_key, const char *override_session_key)
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
 
   err = add_arg (gpg, "--decrypt");
 
+  if (!err && export_session_key)
+    err = add_arg (gpg, "--show-session-key");
+
+  if (!err && override_session_key && *override_session_key)
+    {
+      if (have_gpg_version (gpg, "2.1.16"))
+        {
+          gpgme_data_release (gpg->override_session_key);
+          TRACE2 (DEBUG_ENGINE, "override", gpg, "seskey='%s' len=%zu\n",
+                  override_session_key,
+                  strlen (override_session_key));
+
+          err = gpgme_data_new_from_mem (&gpg->override_session_key,
+                                         override_session_key,
+                                         strlen (override_session_key), 1);
+          if (!err)
+            {
+              err = add_arg (gpg, "--override-session-key-fd");
+              if (!err)
+                err = add_data (gpg, gpg->override_session_key, -2, 0);
+            }
+        }
+      else
+        {
+          /* Using that option may leak the session key via ps(1).  */
+          err = add_arg (gpg, "--override-session-key");
+          if (!err)
+            err = add_arg (gpg, override_session_key);
+        }
+    }
+
   /* Tell the gpg object about the data.  */
   if (!err)
     err = add_arg (gpg, "--output");
@@ -1634,6 +1687,23 @@ append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 
 
 static gpgme_error_t
+append_args_from_sender (engine_gpg_t gpg, gpgme_ctx_t ctx)
+{
+  gpgme_error_t err;
+
+  if (ctx->sender && have_gpg_version (gpg, "2.1.15"))
+    {
+      err = add_arg (gpg, "--sender");
+      if (!err)
+        err = add_arg (gpg, ctx->sender);
+    }
+  else
+    err = 0;
+  return err;
+}
+
+
+static gpgme_error_t
 append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 {
   gpgme_error_t err = 0;
@@ -1790,6 +1860,9 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
   if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
     err = add_arg (gpg, "--compress-algo=none");
 
+  if (!err && (flags & GPGME_ENCRYPT_THROW_KEYIDS))
+    err = add_arg (gpg, "--throw-keyids");
+
   if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME
       && have_gpg_version (gpg, "2.1.14"))
     err = add_arg (gpg, "--mimemode");
@@ -1859,6 +1932,9 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
   if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
     err = add_arg (gpg, "--compress-algo=none");
 
+  if (!err && (flags & GPGME_ENCRYPT_THROW_KEYIDS))
+    err = add_arg (gpg, "--throw-keyids");
+
   if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME
       && have_gpg_version (gpg, "2.1.14"))
     err = add_arg (gpg, "--mimemode");
@@ -1881,6 +1957,9 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
     err = append_args_from_signers (gpg, ctx);
 
   if (!err)
+    err = append_args_from_sender (gpg, ctx);
+
+  if (!err)
     err = append_args_from_sig_notations (gpg, ctx);
 
   /* Tell the gpg object about the data.  */
@@ -2003,7 +2082,8 @@ gpg_add_algo_usage_expire (engine_gpg_t gpg,
   /* This condition is only required to allow the use of gpg < 2.1.16 */
   if (algo
       || (flags & (GPGME_CREATE_SIGN | GPGME_CREATE_ENCR
-                   | GPGME_CREATE_CERT | GPGME_CREATE_AUTH))
+                   | GPGME_CREATE_CERT | GPGME_CREATE_AUTH
+                   | GPGME_CREATE_NOEXPIRE))
       || expires)
     {
       err = add_arg (gpg, algo? algo : "default");
@@ -2017,11 +2097,18 @@ gpg_add_algo_usage_expire (engine_gpg_t gpg,
                     (flags & GPGME_CREATE_AUTH)? " auth":"");
           err = add_arg (gpg, *tmpbuf? tmpbuf : "default");
         }
-      if (!err && expires)
+      if (!err)
         {
-          char tmpbuf[8+20];
-          snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expires);
-          err = add_arg (gpg, tmpbuf);
+          if ((flags & GPGME_CREATE_NOEXPIRE))
+            err = add_arg (gpg, "never");
+          else if (expires == 0)
+            err = add_arg (gpg, "-");
+          else
+            {
+              char tmpbuf[8+20];
+              snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expires);
+              err = add_arg (gpg, tmpbuf);
+            }
         }
     }
   else
@@ -2067,6 +2154,8 @@ gpg_createkey (engine_gpg_t gpg,
       err = add_arg (gpg, "--passphrase");
       if (!err)
         err = add_arg (gpg, "");
+      if (!err)
+        err = add_arg (gpg, "--batch");
     }
   if (!err && (flags & GPGME_CREATE_FORCE))
     err = add_arg (gpg, "--yes");
@@ -2105,6 +2194,8 @@ gpg_addkey (engine_gpg_t gpg,
       err = add_arg (gpg, "--passphrase");
       if (!err)
         err = add_arg (gpg, "");
+      if (!err)
+        err = add_arg (gpg, "--batch");
     }
   if (!err)
     err = add_arg (gpg, "--");
@@ -2131,7 +2222,14 @@ gpg_adduid (engine_gpg_t gpg,
   if (!key || !key->fpr || !userid)
     return gpg_error (GPG_ERR_INV_ARG);
 
-  if ((extraflags & GENKEY_EXTRAFLAG_REVOKE))
+  if ((extraflags & GENKEY_EXTRAFLAG_SETPRIMARY))
+    {
+      if (!have_gpg_version (gpg, "2.1.20"))
+        err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+      else
+        err = add_arg (gpg, "--quick-set-primary-uid");
+    }
+  else if ((extraflags & GENKEY_EXTRAFLAG_REVOKE))
     err = add_arg (gpg, "--quick-revuid");
   else
     err = add_arg (gpg, "--quick-adduid");
@@ -2171,7 +2269,7 @@ gpg_genkey (void *engine,
    *  USERID && !KEY          - Create a new keyblock.
    * !USERID &&  KEY          - Add a new subkey to KEY (gpg >= 2.1.14)
    *  USERID &&  KEY && !ALGO - Add a new user id to KEY (gpg >= 2.1.14).
-   *
+   *                            or set a flag on a user id.
    */
   if (help_data)
     {
@@ -2440,7 +2538,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
       n = strlen (field[1]);
       if (n > 16)
         {
-          if (asprintf (r_line,
+          if (gpgrt_asprintf (r_line,
                         "pub:o%s:%s:%s:%s:%s:%s::::::::\n"
                         "fpr:::::::::%s:",
                         field[6], field[3], field[2], field[1] + n - 16,
@@ -2449,7 +2547,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
         }
       else
         {
-          if (asprintf (r_line,
+          if (gpgrt_asprintf (r_line,
                         "pub:o%s:%s:%s:%s:%s:%s::::::::",
                         field[6], field[3], field[2], field[1],
                         field[4], field[5]) < 0)
@@ -2507,7 +2605,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
          }
        *dst = '\0';
 
-       if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:",
+       if (gpgrt_asprintf (r_line, "uid:o%s::::%s:%s:::%s:",
                      field[4], field[2], field[3], uid) < 0)
          return gpg_error_from_syserror ();
       }
@@ -2531,7 +2629,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
   err = add_arg (gpg, "--with-colons");
 
   /* Since gpg 2.1.15 fingerprints are always printed, thus there is
-   * no more need to explictly request them.  */
+   * no more need to explicitly request them.  */
   if (!have_gpg_version (gpg, "2.1.15"))
     {
       if (!err)
@@ -2646,6 +2744,38 @@ gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
 
 
 static gpgme_error_t
+gpg_keylist_data (void *engine, gpgme_data_t data)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t err;
+
+  if (!have_gpg_version (gpg, "2.1.14"))
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+  err = add_arg (gpg, "--with-colons");
+  if (!err)
+    err = add_arg (gpg, "--with-fingerprint");
+  if (!err)
+    err = add_arg (gpg, "--import-options");
+  if (!err)
+    err = add_arg (gpg, "import-show");
+  if (!err)
+    err = add_arg (gpg, "--dry-run");
+  if (!err)
+    err = add_arg (gpg, "--import");
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    err = add_data (gpg, data, -1, 0);
+
+  if (!err)
+    err = start (gpg);
+
+  return err;
+}
+
+
+static gpgme_error_t
 gpg_keysign (void *engine, gpgme_key_t key, const char *userid,
              unsigned long expire, unsigned int flags,
              gpgme_ctx_t ctx)
@@ -2782,6 +2912,8 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
   if (!err)
     err = append_args_from_signers (gpg, ctx);
   if (!err)
+    err = append_args_from_sender (gpg, ctx);
+  if (!err)
     err = append_args_from_sig_notations (gpg, ctx);
 
   if (gpgme_data_get_file_name (in))
@@ -2833,12 +2965,15 @@ gpg_trustlist (void *engine, const char *pattern)
 
 static gpgme_error_t
 gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
-           gpgme_data_t plaintext)
+           gpgme_data_t plaintext, gpgme_ctx_t ctx)
 {
   engine_gpg_t gpg = engine;
-  gpgme_error_t err = 0;
+  gpgme_error_t err;
 
-  if (plaintext)
+  err = append_args_from_sender (gpg, ctx);
+  if (err)
+    ;
+  else if (plaintext)
     {
       /* Normal or cleartext signature.  */
       err = add_arg (gpg, "--output");
@@ -2923,6 +3058,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_import,
     gpg_keylist,
     gpg_keylist_ext,
+    gpg_keylist_data,
     gpg_keysign,
     gpg_tofu_policy,    /* tofu_policy */
     gpg_sign,
@@ -2932,6 +3068,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     gpg_set_io_cbs,
     gpg_io_event,
     gpg_cancel,