Merge branch 'master' of ssh+git://playfair.gnupg.org/git/gpgme
[gpgme.git] / src / engine-gpg.c
index 3f1d34d..3b9a6ff 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,10 @@ struct engine_gpg
 
   struct gpgme_io_cbs io_cbs;
   gpgme_pinentry_mode_t pinentry_mode;
+  char request_origin[10];
+
+  /* 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 +449,8 @@ gpg_release (void *engine)
   if (gpg->cmd.keyword)
     free (gpg->cmd.keyword);
 
+  gpgme_data_release (gpg->override_session_key);
+
   free (gpg);
 }
 
@@ -619,6 +629,24 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,
 }
 
 
+/* Copy flags from CTX into the engine object.  */
+static void
+gpg_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
+{
+  engine_gpg_t gpg = engine;
+
+  if (ctx->request_origin && have_gpg_version (gpg, "2.2.6"))
+    {
+      if (strlen (ctx->request_origin) + 1 > sizeof gpg->request_origin)
+        strcpy (gpg->request_origin, "xxx"); /* Too long  - force error */
+      else
+        strcpy (gpg->request_origin, ctx->request_origin);
+    }
+  else
+    *gpg->request_origin = 0;
+}
+
+
 static gpgme_error_t
 gpg_set_locale (void *engine, int category, const char *value)
 {
@@ -847,7 +875,7 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
     argc++;
   if (!gpg->cmd.used)
     argc++;    /* --batch */
-  argc += 1;   /* --no-sk-comments */
+  argc += 2;   /* --no-sk-comments, --request-origin */
 
   argv = calloc (argc + 1, sizeof *argv);
   if (!argv)
@@ -895,6 +923,20 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
       argc++;
     }
 
+  if (*gpg->request_origin)
+    {
+      argv[argc] = _gpgme_strconcat ("--request-origin=",
+                                     gpg->request_origin, NULL);
+      if (!argv[argc])
+       {
+          int saved_err = gpg_error_from_syserror ();
+         free (fd_data_map);
+         free_argv (argv);
+         return saved_err;
+        }
+      argc++;
+    }
+
   if (gpg->pinentry_mode && have_gpg_version (gpg, "2.1.0"))
     {
       const char *s = NULL;
@@ -1341,7 +1383,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);
@@ -1550,13 +1592,59 @@ 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_decrypt_flags_t flags,
+             gpgme_data_t ciph, gpgme_data_t plain,
+             int export_session_key, const char *override_session_key,
+             int auto_key_retrieve)
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
 
   err = add_arg (gpg, "--decrypt");
 
+  if (!err && (flags & GPGME_DECRYPT_UNWRAP))
+    {
+      if (!have_gpg_version (gpg, "2.1.12"))
+        err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+      else
+        err = add_arg (gpg, "--unwrap");
+    }
+
+  if (!err && export_session_key)
+    err = add_arg (gpg, "--show-session-key");
+
+  if (!err && auto_key_retrieve)
+    err = add_arg (gpg, "--auto-key-retrieve");
+
+  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");
@@ -1577,13 +1665,18 @@ gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 }
 
 static gpgme_error_t
-gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
+gpg_delete (void *engine, gpgme_key_t key, unsigned int flags)
 {
   engine_gpg_t gpg = engine;
-  gpgme_error_t err;
+  gpgme_error_t err = 0;
+  int allow_secret = flags & GPGME_DELETE_ALLOW_SECRET;
+  int force = flags & GPGME_DELETE_FORCE;
 
-  err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key"
-                : "--delete-key");
+  if (force)
+    err = add_arg (gpg, "--yes");
+  if (!err)
+    err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key"
+                  : "--delete-key");
   if (!err)
     err = add_arg (gpg, "--");
   if (!err)
@@ -1646,6 +1739,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;
@@ -1799,9 +1909,23 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
   if (!err && use_armor)
     err = add_arg (gpg, "--armor");
 
+  if (!err && (flags & GPGME_ENCRYPT_WRAP))
+    {
+      /* gpg is current not able to detect already compressed
+       * packets.  Thus when using
+       *   gpg --unwrap -d | gpg --no-literal -e
+       * the encryption would add an additional compression layer.
+       * We better suppress that.  */
+      flags |= GPGME_ENCRYPT_NO_COMPRESS;
+      err = add_arg (gpg, "--no-literal");
+    }
+
   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");
@@ -1871,6 +1995,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");
@@ -1893,6 +2020,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.  */
@@ -2015,7 +2145,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");
@@ -2029,11 +2160,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
@@ -2079,6 +2217,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");
@@ -2117,6 +2257,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, "--");
@@ -2143,7 +2285,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");
@@ -2183,7 +2332,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)
     {
@@ -2452,7 +2601,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,
@@ -2461,7 +2610,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)
@@ -2478,6 +2627,9 @@ gpg_keylist_preprocess (char *line, char **r_line)
         as defined in 5.2. Machine Readable Indexes of the OpenPGP
         HTTP Keyserver Protocol (draft).
 
+         For an ldap keyserver the format is:
+         uid:<escaped uid string>
+
         We want:
         uid:o<flags>::::<creatdate>:<expdate>:::<c-coded uid>:
       */
@@ -2519,9 +2671,17 @@ gpg_keylist_preprocess (char *line, char **r_line)
          }
        *dst = '\0';
 
-       if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:",
-                     field[4], field[2], field[3], uid) < 0)
-         return gpg_error_from_syserror ();
+        if (fields < 4)
+          {
+            if (gpgrt_asprintf (r_line, "uid:o::::::::%s:", uid) < 0)
+              return gpg_error_from_syserror ();
+          }
+        else
+          {
+            if (gpgrt_asprintf (r_line, "uid:o%s::::%s:%s:::%s:",
+                                field[4], field[2], field[3], uid) < 0)
+              return gpg_error_from_syserror ();
+          }
       }
       return 0;
 
@@ -2543,7 +2703,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)
@@ -2658,6 +2818,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)
@@ -2794,6 +2986,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))
@@ -2845,12 +3039,18 @@ 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 && ctx->auto_key_retrieve)
+    err = add_arg (gpg, "--auto-key-retrieve");
+
+  if (err)
+    ;
+  else if (plaintext)
     {
       /* Normal or cleartext signature.  */
       err = add_arg (gpg, "--output");
@@ -2923,8 +3123,8 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_set_colon_line_handler,
     gpg_set_locale,
     NULL,                              /* set_protocol */
+    gpg_set_engine_flags,               /* set_engine_flags */
     gpg_decrypt,
-    gpg_decrypt,                       /* decrypt_verify */
     gpg_delete,
     gpg_edit,
     gpg_encrypt,
@@ -2935,6 +3135,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,
@@ -2944,6 +3145,8 @@ struct engine_ops _gpgme_engine_ops_gpg =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,              /* conf_dir */
+    NULL,               /* query_swdb */
     gpg_set_io_cbs,
     gpg_io_event,
     gpg_cancel,