X-Git-Url: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gpgme.git;a=blobdiff_plain;f=src%2Fengine-gpg.c;h=3b9a6ff5e6f8befcfab776fad79b1d2735523a3e;hp=ede098ef96395c3e4de0b601e1ff02cb77a23c09;hb=fed024eff1091056647296ac589a0c88c2be41bb;hpb=991cde9e79fec70aad093ded383c5574d30f9388
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
index ede098ef..3b9a6ff5 100644
--- a/src/engine-gpg.c
+++ b/src/engine-gpg.c
@@ -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 .
+ License along with this program; if not, see .
*/
#if HAVE_CONFIG_H
@@ -42,6 +42,7 @@
#include "priv-io.h"
#include "sema.h"
#include "debug.h"
+#include "data.h"
#include "engine-backend.h"
@@ -73,11 +74,16 @@ 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
{
char *file_name;
+ char *version;
char *lc_messages;
char *lc_ctype;
@@ -95,6 +101,8 @@ struct engine_gpg
int eof;
engine_status_handler_t fnc;
void *fnc_value;
+ gpgme_status_cb_t mon_cb;
+ void *mon_cb_value;
void *tag;
} status;
@@ -135,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;
@@ -202,14 +214,16 @@ close_notify_handler (int fd, void *opaque)
/* If FRONT is true, push at the front of the list. Use this for
options added late in the process. */
static gpgme_error_t
-_add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
+_add_arg (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen,
+ int front, int *arg_locp)
{
struct arg_and_data_s *a;
+ size_t prefixlen = prefix? strlen (prefix) : 0;
assert (gpg);
assert (arg);
- a = malloc (sizeof *a + strlen (arg));
+ a = malloc (sizeof *a + prefixlen + arglen);
if (!a)
return gpg_error_from_syserror ();
@@ -217,7 +231,10 @@ _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
a->dup_to = -1;
a->arg_locp = arg_locp;
- strcpy (a->arg, arg);
+ if (prefixlen)
+ memcpy (a->arg, prefix, prefixlen);
+ memcpy (a->arg + prefixlen, arg, arglen);
+ a->arg[prefixlen + arglen] = 0;
if (front)
{
a->next = gpg->arglist;
@@ -239,24 +256,36 @@ _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
return 0;
}
+
static gpgme_error_t
add_arg_ext (engine_gpg_t gpg, const char *arg, int front)
{
- return _add_arg (gpg, arg, front, NULL);
+ return _add_arg (gpg, NULL, arg, strlen (arg), front, NULL);
}
-
static gpgme_error_t
add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp)
{
- return _add_arg (gpg, arg, 0, locp);
+ return _add_arg (gpg, NULL, arg, strlen (arg), 0, locp);
}
-
static gpgme_error_t
add_arg (engine_gpg_t gpg, const char *arg)
{
- return add_arg_ext (gpg, arg, 0);
+ return _add_arg (gpg, NULL, arg, strlen (arg), 0, NULL);
+}
+
+static gpgme_error_t
+add_arg_pfx (engine_gpg_t gpg, const char *prefix, const char *arg)
+{
+ return _add_arg (gpg, prefix, arg, strlen (arg), 0, NULL);
+}
+
+static gpgme_error_t
+add_arg_len (engine_gpg_t gpg, const char *prefix,
+ const char *arg, size_t arglen)
+{
+ return _add_arg (gpg, prefix, arg, arglen, 0, NULL);
}
@@ -291,6 +320,15 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
return 0;
}
+
+/* Return true if the engine's version is at least VERSION. */
+static int
+have_gpg_version (engine_gpg_t gpg, const char *version)
+{
+ return _gpgme_compare_versions (gpg->version, version);
+}
+
+
static char *
gpg_get_version (const char *file_name)
@@ -386,6 +424,8 @@ gpg_release (void *engine)
if (gpg->file_name)
free (gpg->file_name);
+ if (gpg->version)
+ free (gpg->version);
if (gpg->lc_messages)
free (gpg->lc_messages);
@@ -396,8 +436,7 @@ gpg_release (void *engine)
{
struct arg_and_data_s *next = gpg->arglist->next;
- if (gpg->arglist)
- free (gpg->arglist);
+ free (gpg->arglist);
gpg->arglist = next;
}
@@ -410,18 +449,22 @@ gpg_release (void *engine)
if (gpg->cmd.keyword)
free (gpg->cmd.keyword);
+ gpgme_data_release (gpg->override_session_key);
+
free (gpg);
}
static gpgme_error_t
-gpg_new (void **engine, const char *file_name, const char *home_dir)
+gpg_new (void **engine, const char *file_name, const char *home_dir,
+ const char *version)
{
engine_gpg_t gpg;
gpgme_error_t rc = 0;
char *dft_display = NULL;
char dft_ttyname[64];
char *dft_ttytype = NULL;
+ char *env_tty = NULL;
gpg = calloc (1, sizeof *gpg);
if (!gpg)
@@ -437,6 +480,16 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
}
}
+ if (version)
+ {
+ gpg->version = strdup (version);
+ if (!gpg->version)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
gpg->argtail = &gpg->arglist;
gpg->status.fd[0] = -1;
gpg->status.fd[1] = -1;
@@ -501,6 +554,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;
@@ -514,16 +569,28 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
rc = add_arg (gpg, dft_display);
free (dft_display);
+ if (rc)
+ 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 (err)
- rc = gpg_error_from_errno (err);
+ 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. */
+ if (!err)
{
if (*dft_ttyname)
{
@@ -548,9 +615,9 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
free (dft_ttytype);
}
+ if (rc)
+ goto leave;
}
- if (rc)
- goto leave;
}
leave:
@@ -562,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)
{
@@ -607,6 +692,17 @@ gpg_set_locale (void *engine, int category, const char *value)
return 0;
}
+/* This sets a status callback for monitoring status lines before they
+ * are passed to a caller set handler. */
+static void
+gpg_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
+{
+ engine_gpg_t gpg = engine;
+
+ gpg->status.mon_cb = cb;
+ gpg->status.mon_cb_value = cb_value;
+}
+
/* Note, that the status_handler is allowed to modifiy the args
value. */
@@ -779,7 +875,7 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
argc++;
if (!gpg->cmd.used)
argc++; /* --batch */
- argc += 1; /* --no-sk-comment */
+ argc += 2; /* --no-sk-comments, --request-origin */
argv = calloc (argc + 1, sizeof *argv);
if (!argv)
@@ -827,7 +923,21 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
argc++;
}
- if (gpg->pinentry_mode)
+ 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;
switch (gpg->pinentry_mode)
@@ -864,7 +974,7 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
}
argc++;
}
- argv[argc] = strdup ("--no-sk-comment");
+ argv[argc] = strdup ("--no-sk-comments");
if (!argv[argc])
{
int saved_err = gpg_error_from_syserror ();
@@ -1017,6 +1127,7 @@ read_status (engine_gpg_t gpg)
size_t bufsize = gpg->status.bufsize;
char *buffer = gpg->status.buffer;
size_t readpos = gpg->status.readpos;
+ gpgme_error_t err;
assert (buffer);
if (bufsize - readpos < 256)
@@ -1035,15 +1146,20 @@ read_status (engine_gpg_t gpg)
if (!nread)
{
+ err = 0;
gpg->status.eof = 1;
+ if (gpg->status.mon_cb)
+ err = gpg->status.mon_cb (gpg->status.mon_cb_value, "", "");
if (gpg->status.fnc)
- {
- gpgme_error_t err;
- err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, "");
- if (err)
- return err;
- }
- return 0;
+ {
+ char emptystring[1] = {0};
+ err = gpg->status.fnc (gpg->status.fnc_value,
+ GPGME_STATUS_EOF, emptystring);
+ if (gpg_err_code (err) == GPG_ERR_FALSE)
+ err = 0; /* Drop special error code. */
+ }
+
+ return err;
}
while (nread > 0)
@@ -1069,6 +1185,15 @@ read_status (engine_gpg_t gpg)
*rest++ = 0;
r = _gpgme_parse_status (buffer + 9);
+ if (gpg->status.mon_cb && r != GPGME_STATUS_PROGRESS)
+ {
+ /* Note that we call the monitor even if we do
+ * not know the status code (r < 0). */
+ err = gpg->status.mon_cb (gpg->status.mon_cb_value,
+ buffer + 9, rest);
+ if (err)
+ return err;
+ }
if (r >= 0)
{
if (gpg->cmd.used
@@ -1097,9 +1222,10 @@ read_status (engine_gpg_t gpg)
}
else if (gpg->status.fnc)
{
- gpgme_error_t err;
err = gpg->status.fnc (gpg->status.fnc_value,
r, rest);
+ if (gpg_err_code (err) == GPG_ERR_FALSE)
+ err = 0; /* Drop special error code. */
if (err)
return err;
}
@@ -1242,10 +1368,26 @@ read_colon_line (engine_gpg_t gpg)
}
assert (gpg->colon.fnc);
- gpg->colon.fnc (gpg->colon.fnc_value, line ? line : buffer);
- if (line)
- free (line);
- }
+ if (line)
+ {
+ char *linep = line;
+ char *endp;
+
+ do
+ {
+ endp = strchr (linep, '\n');
+ if (endp)
+ *endp++ = 0;
+ gpg->colon.fnc (gpg->colon.fnc_value, linep);
+ linep = endp;
+ }
+ while (linep && *linep);
+
+ gpgrt_free (line);
+ }
+ else
+ gpg->colon.fnc (gpg->colon.fnc_value, buffer);
+ }
/* To reuse the buffer for the next line we have to
shift the remaining data to the buffer start and
@@ -1420,14 +1562,89 @@ start (engine_gpg_t gpg)
}
+/* Add the --input-size-hint option if requested. */
+static gpgme_error_t
+add_input_size_hint (engine_gpg_t gpg, gpgme_data_t data)
+{
+ gpgme_error_t err;
+ gpgme_off_t value = _gpgme_data_get_size_hint (data);
+ char numbuf[50]; /* Large enough for even 2^128 in base-10. */
+ char *p;
+
+ if (!value || !have_gpg_version (gpg, "2.1.15"))
+ return 0;
+
+ err = add_arg (gpg, "--input-size-hint");
+ if (!err)
+ {
+ p = numbuf + sizeof numbuf;
+ *--p = 0;
+ do
+ {
+ *--p = '0' + (value % 10);
+ value /= 10;
+ }
+ while (value);
+ err = add_arg (gpg, p);
+ }
+ return err;
+}
+
+
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");
@@ -1436,23 +1653,30 @@ gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
if (!err)
err = add_data (gpg, plain, 1, 1);
if (!err)
+ err = add_input_size_hint (gpg, ciph);
+ if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, ciph, -1, 0);
if (!err)
- start (gpg);
+ err = start (gpg);
return err;
}
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)
@@ -1464,7 +1688,7 @@ gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
}
if (!err)
- start (gpg);
+ err = start (gpg);
return err;
}
@@ -1475,6 +1699,8 @@ gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags)
engine_gpg_t gpg = engine;
gpgme_error_t err;
+ (void)flags;
+
if (!key || !key->subkeys || !key->subkeys->fpr)
return gpg_error (GPG_ERR_INV_CERT_OBJ);
@@ -1482,7 +1708,7 @@ gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags)
if (!err)
err = add_arg (gpg, key->subkeys->fpr);
if (!err)
- start (gpg);
+ err = start (gpg);
return err;
}
@@ -1505,8 +1731,26 @@ append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
err = add_arg (gpg, s);
}
gpgme_key_unref (key);
- if (err) break;
+ if (err)
+ break;
+ }
+ return err;
+}
+
+
+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;
}
@@ -1654,18 +1898,39 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
{
engine_gpg_t gpg = engine;
- gpgme_error_t err;
- int symmetric = !recp;
+ gpgme_error_t err = 0;
- err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
+ if (recp)
+ err = add_arg (gpg, "--encrypt");
+
+ if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp))
+ err = add_arg (gpg, "--symmetric");
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 (!symmetric)
+ 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");
+
+ if (recp)
{
/* If we know that all recipients are valid (full or ultimate trust)
we can suppress further checks. */
@@ -1694,6 +1959,8 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
err = add_arg (gpg, gpgme_data_get_file_name (plain));
}
if (!err)
+ err = add_input_size_hint (gpg, plain);
+ if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, plain, -1, 0);
@@ -1712,10 +1979,13 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
gpgme_ctx_t ctx /* FIXME */)
{
engine_gpg_t gpg = engine;
- gpgme_error_t err;
- int symmetric = !recp;
+ gpgme_error_t err = 0;
- err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
+ if (recp)
+ err = add_arg (gpg, "--encrypt");
+
+ if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp))
+ err = add_arg (gpg, "--symmetric");
if (!err)
err = add_arg (gpg, "--sign");
@@ -1725,7 +1995,14 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
err = add_arg (gpg, "--compress-algo=none");
- if (!symmetric)
+ 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");
+
+ if (recp)
{
/* If we know that all recipients are valid (full or ultimate trust)
we can suppress further checks. */
@@ -1743,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. */
@@ -1760,6 +2040,8 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
err = add_arg (gpg, gpgme_data_get_file_name (plain));
}
if (!err)
+ err = add_input_size_hint (gpg, plain);
+ if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, plain, -1, 0);
@@ -1778,7 +2060,8 @@ export_common (engine_gpg_t gpg, gpgme_export_mode_t mode,
gpgme_error_t err = 0;
if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
- |GPGME_EXPORT_MODE_MINIMAL)))
+ |GPGME_EXPORT_MODE_MINIMAL
+ |GPGME_EXPORT_MODE_SECRET)))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
if ((mode & GPGME_EXPORT_MODE_MINIMAL))
@@ -1792,7 +2075,10 @@ export_common (engine_gpg_t gpg, gpgme_export_mode_t mode,
}
else
{
- err = add_arg (gpg, "--export");
+ if ((mode & GPGME_EXPORT_MODE_SECRET))
+ err = add_arg (gpg, "--export-secret-keys");
+ else
+ err = add_arg (gpg, "--export");
if (!err && use_armor)
err = add_arg (gpg, "--armor");
if (!err)
@@ -1846,33 +2132,229 @@ gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
}
+
+/* Helper to add algo, usage, and expire to the list of args. */
static gpgme_error_t
-gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
- gpgme_data_t pubkey, gpgme_data_t seckey)
+gpg_add_algo_usage_expire (engine_gpg_t gpg,
+ const char *algo,
+ unsigned long expires,
+ unsigned int flags)
{
- engine_gpg_t gpg = engine;
- gpgme_error_t err;
+ gpg_error_t err;
- if (!gpg)
- return gpg_error (GPG_ERR_INV_VALUE);
+ /* 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_NOEXPIRE))
+ || expires)
+ {
+ err = add_arg (gpg, algo? algo : "default");
+ if (!err)
+ {
+ char tmpbuf[5*4+1];
+ snprintf (tmpbuf, sizeof tmpbuf, "%s%s%s%s",
+ (flags & GPGME_CREATE_SIGN)? " sign":"",
+ (flags & GPGME_CREATE_ENCR)? " encr":"",
+ (flags & GPGME_CREATE_CERT)? " cert":"",
+ (flags & GPGME_CREATE_AUTH)? " auth":"");
+ err = add_arg (gpg, *tmpbuf? tmpbuf : "default");
+ }
+ if (!err)
+ {
+ 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
+ err = 0;
- /* We need a special mechanism to get the fd of a pipe here, so that
- we can use this for the %pubring and %secring parameters. We
- don't have this yet, so we implement only the adding to the
- standard keyrings. */
- if (pubkey || seckey)
- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_createkey_from_param (engine_gpg_t gpg,
+ gpgme_data_t help_data, unsigned int extraflags)
+{
+ gpgme_error_t err;
err = add_arg (gpg, "--gen-key");
- if (!err && use_armor)
+ if (!err && (extraflags & GENKEY_EXTRAFLAG_ARMOR))
err = add_arg (gpg, "--armor");
if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, help_data, -1, 0);
+ if (!err)
+ err = start (gpg);
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_createkey (engine_gpg_t gpg,
+ const char *userid, const char *algo,
+ unsigned long expires,
+ unsigned int flags,
+ unsigned int extraflags)
+{
+ gpgme_error_t err;
+
+ err = add_arg (gpg, "--quick-gen-key");
+ if (!err && (extraflags & GENKEY_EXTRAFLAG_ARMOR))
+ err = add_arg (gpg, "--armor");
+ if (!err && (flags & GPGME_CREATE_NOPASSWD))
+ {
+ 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");
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_arg (gpg, userid);
+
+ if (!err)
+ err = gpg_add_algo_usage_expire (gpg, algo, expires, flags);
+
+ if (!err)
+ err = start (gpg);
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_addkey (engine_gpg_t gpg,
+ const char *algo,
+ unsigned long expires,
+ gpgme_key_t key,
+ unsigned int flags,
+ unsigned int extraflags)
+{
+ gpgme_error_t err;
+
+ if (!key || !key->fpr)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ err = add_arg (gpg, "--quick-addkey");
+ if (!err && (extraflags & GENKEY_EXTRAFLAG_ARMOR))
+ err = add_arg (gpg, "--armor");
+ if (!err && (flags & GPGME_CREATE_NOPASSWD))
+ {
+ err = add_arg (gpg, "--passphrase");
+ if (!err)
+ err = add_arg (gpg, "");
+ if (!err)
+ err = add_arg (gpg, "--batch");
+ }
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_arg (gpg, key->fpr);
+
+ if (!err)
+ err = gpg_add_algo_usage_expire (gpg, algo, expires, flags);
if (!err)
err = start (gpg);
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_adduid (engine_gpg_t gpg,
+ gpgme_key_t key,
+ const char *userid,
+ unsigned int extraflags)
+{
+ gpgme_error_t err;
+
+ if (!key || !key->fpr || !userid)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ 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");
+
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_arg (gpg, key->fpr);
+ if (!err)
+ err = add_arg (gpg, userid);
+
+ if (!err)
+ err = start (gpg);
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_genkey (void *engine,
+ const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t key, unsigned int flags,
+ gpgme_data_t help_data, unsigned int extraflags,
+ gpgme_data_t pubkey, gpgme_data_t seckey)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ (void)reserved;
+
+ if (!gpg)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* If HELP_DATA is given the use of the old interface
+ * (gpgme_op_genkey) has been requested. The other modes are:
+ *
+ * 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)
+ {
+ /* We need a special mechanism to get the fd of a pipe here, so
+ that we can use this for the %pubring and %secring
+ parameters. We don't have this yet, so we implement only the
+ adding to the standard keyrings. */
+ if (pubkey || seckey)
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ else
+ err = gpg_createkey_from_param (gpg, help_data, extraflags);
+ }
+ else if (!have_gpg_version (gpg, "2.1.13"))
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else if (userid && !key)
+ err = gpg_createkey (gpg, userid, algo, expires, flags, extraflags);
+ else if (!userid && key)
+ err = gpg_addkey (gpg, algo, expires, key, flags, extraflags);
+ else if (userid && key && !algo)
+ err = gpg_adduid (gpg, key, userid, extraflags);
+ else
+ err = gpg_error (GPG_ERR_INV_VALUE);
return err;
}
@@ -2071,6 +2553,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
#define NR_FIELDS 16
char *field[NR_FIELDS];
int fields = 0;
+ size_t n;
*r_line = NULL;
@@ -2106,16 +2589,34 @@ gpg_keylist_preprocess (char *line, char **r_line)
pub::::::
as defined in 5.2. Machine Readable Indexes of the OpenPGP
- HTTP Keyserver Protocol (draft).
+ HTTP Keyserver Protocol (draft). Modern versions of the SKS
+ keyserver return the fingerprint instead of the keyid. We
+ detect this here and use the v4 fingerprint format to convert
+ it to a key id.
We want:
pub:o:::::::::::::
*/
- if (asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::",
- field[6], field[3], field[2], field[1],
- field[4], field[5]) < 0)
- return gpg_error_from_syserror ();
+ n = strlen (field[1]);
+ if (n > 16)
+ {
+ 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,
+ field[4], field[5], field[1]) < 0)
+ return gpg_error_from_syserror ();
+ }
+ else
+ {
+ 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)
+ return gpg_error_from_syserror ();
+ }
+
return 0;
case RT_UID:
@@ -2126,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:
+
We want:
uid:o:::::::::
*/
@@ -2160,15 +2664,24 @@ gpg_keylist_preprocess (char *line, char **r_line)
{
*dst++ = '\\';
*dst++ = '\\';
+ src++;
}
else
*(dst++) = *(src++);
}
*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;
@@ -2188,12 +2701,26 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
gpg_error_t err;
err = add_arg (gpg, "--with-colons");
- if (!err)
- err = add_arg (gpg, "--fixed-list-mode");
- if (!err)
- err = add_arg (gpg, "--with-fingerprint");
- if (!err)
- err = add_arg (gpg, "--with-fingerprint");
+
+ /* Since gpg 2.1.15 fingerprints are always printed, thus there is
+ * no more need to explicitly request them. */
+ if (!have_gpg_version (gpg, "2.1.15"))
+ {
+ if (!err)
+ err = add_arg (gpg, "--fixed-list-mode");
+ if (!err)
+ err = add_arg (gpg, "--with-fingerprint");
+ if (!err)
+ err = add_arg (gpg, "--with-fingerprint");
+ }
+
+ if (!err && (mode & GPGME_KEYLIST_MODE_WITH_TOFU)
+ && have_gpg_version (gpg, "2.1.16"))
+ err = add_arg (gpg, "--with-tofu-info");
+
+ if (!err && (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
+ err = add_arg (gpg, "--with-secret");
+
if (!err
&& (mode & GPGME_KEYLIST_MODE_SIGS)
&& (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
@@ -2202,6 +2729,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
if (!err)
err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
}
+
if (!err)
{
if ( (mode & GPGME_KEYLIST_MODE_EXTERN) )
@@ -2233,6 +2761,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
? "--check-sigs" : "--list-keys"));
}
}
+
if (!err)
err = add_arg (gpg, "--");
@@ -2242,11 +2771,13 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
static gpgme_error_t
gpg_keylist (void *engine, const char *pattern, int secret_only,
- gpgme_keylist_mode_t mode)
+ gpgme_keylist_mode_t mode, int engine_flags)
{
engine_gpg_t gpg = engine;
gpgme_error_t err;
+ (void)engine_flags;
+
err = gpg_keylist_build_options (gpg, secret_only, mode);
if (!err && pattern && *pattern)
@@ -2261,11 +2792,13 @@ gpg_keylist (void *engine, const char *pattern, int secret_only,
static gpgme_error_t
gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
- int reserved, gpgme_keylist_mode_t mode)
+ int reserved, gpgme_keylist_mode_t mode, int engine_flags)
{
engine_gpg_t gpg = engine;
gpgme_error_t err;
+ (void)engine_flags;
+
if (reserved)
return gpg_error (GPG_ERR_INV_VALUE);
@@ -2285,6 +2818,143 @@ 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)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+ const char *s;
+
+ if (!key || !key->fpr)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ if (!have_gpg_version (gpg, "2.1.12"))
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ if ((flags & GPGME_KEYSIGN_LOCAL))
+ err = add_arg (gpg, "--quick-lsign-key");
+ else
+ err = add_arg (gpg, "--quick-sign-key");
+
+ if (!err)
+ err = append_args_from_signers (gpg, ctx);
+
+ /* If an expiration time has been given use that. If none has been
+ * given the default from gpg.conf is used. To make sure not to set
+ * an expiration time at all the flag GPGME_KEYSIGN_NOEXPIRE can be
+ * used. */
+ if (!err && (expire || (flags & GPGME_KEYSIGN_NOEXPIRE)))
+ {
+ char tmpbuf[8+20];
+
+ if ((flags & GPGME_KEYSIGN_NOEXPIRE))
+ expire = 0;
+ snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expire);
+ err = add_arg (gpg, "--default-cert-expire");
+ if (!err)
+ err = add_arg (gpg, tmpbuf);
+ }
+
+ if (!err)
+ err = add_arg (gpg, "--");
+
+ if (!err)
+ err = add_arg (gpg, key->fpr);
+ if (!err && userid)
+ {
+ if ((flags & GPGME_KEYSIGN_LFSEP))
+ {
+ for (; !err && (s = strchr (userid, '\n')); userid = s + 1)
+ if ((s - userid))
+ err = add_arg_len (gpg, "=", userid, s - userid);
+ if (!err && *userid)
+ err = add_arg_pfx (gpg, "=", userid);
+ }
+ else
+ err = add_arg_pfx (gpg, "=", userid);
+ }
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_tofu_policy (void *engine, gpgme_key_t key, gpgme_tofu_policy_t policy)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+ const char *policystr = NULL;
+
+ if (!key || !key->fpr)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ switch (policy)
+ {
+ case GPGME_TOFU_POLICY_NONE: break;
+ case GPGME_TOFU_POLICY_AUTO: policystr = "auto"; break;
+ case GPGME_TOFU_POLICY_GOOD: policystr = "good"; break;
+ case GPGME_TOFU_POLICY_BAD: policystr = "bad"; break;
+ case GPGME_TOFU_POLICY_ASK: policystr = "ask"; break;
+ case GPGME_TOFU_POLICY_UNKNOWN: policystr = "unknown"; break;
+ }
+ if (!policystr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!have_gpg_version (gpg, "2.1.10"))
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ err = add_arg (gpg, "--tofu-policy");
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_arg (gpg, policystr);
+ if (!err)
+ err = add_arg (gpg, key->fpr);
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
gpgme_sig_mode_t mode, int use_armor, int use_textmode,
int include_certs, gpgme_ctx_t ctx /* FIXME */)
@@ -2292,6 +2962,8 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
engine_gpg_t gpg = engine;
gpgme_error_t err;
+ (void)include_certs;
+
if (mode == GPGME_SIG_MODE_CLEAR)
err = add_arg (gpg, "--clearsign");
else
@@ -2301,13 +2973,21 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
err = add_arg (gpg, "--detach");
if (!err && use_armor)
err = add_arg (gpg, "--armor");
- if (!err && use_textmode)
- err = add_arg (gpg, "--textmode");
+ if (!err)
+ {
+ if (gpgme_data_get_encoding (in) == GPGME_DATA_ENCODING_MIME
+ && have_gpg_version (gpg, "2.1.14"))
+ err = add_arg (gpg, "--mimemode");
+ else if (use_textmode)
+ err = add_arg (gpg, "--textmode");
+ }
}
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))
@@ -2320,6 +3000,8 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
/* Tell the gpg object about the data. */
if (!err)
+ err = add_input_size_hint (gpg, in);
+ if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, in, -1, 0);
@@ -2327,7 +3009,7 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
err = add_data (gpg, out, 1, 1);
if (!err)
- start (gpg);
+ err = start (gpg);
return err;
}
@@ -2357,19 +3039,26 @@ 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;
+
+ err = append_args_from_sender (gpg, ctx);
+ if (!err && ctx->auto_key_retrieve)
+ err = add_arg (gpg, "--auto-key-retrieve");
- if (plaintext)
+ if (err)
+ ;
+ else if (plaintext)
{
/* Normal or cleartext signature. */
-
err = add_arg (gpg, "--output");
if (!err)
err = add_arg (gpg, "-");
if (!err)
+ err = add_input_size_hint (gpg, sig);
+ if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, sig, -1, 0);
@@ -2380,6 +3069,8 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
{
err = add_arg (gpg, "--verify");
if (!err)
+ err = add_input_size_hint (gpg, signed_text);
+ if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, sig, -1, 0);
@@ -2426,13 +3117,14 @@ struct engine_ops _gpgme_engine_ops_gpg =
/* Member functions. */
gpg_release,
NULL, /* reset */
+ gpg_set_status_cb,
gpg_set_status_handler,
gpg_set_command_handler,
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,
@@ -2443,6 +3135,9 @@ 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,
gpg_trustlist,
gpg_verify,
@@ -2450,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,