signatures, the progress callback is invoked for both the detached
signature and the plaintext message, though.
+ * gpgme_passphrase_cb_t has been changed to not return a complete
+ description, but the UID hint, passphrase info and a flag
+ indicating if this is a repeated attempt individually, so the user
+ can compose his own description from this information. Furthermore
+ it does not expect the user to return a string, but write the
+ passphrase followed by a newline character directly to a file
+ descriptor.
+
+ * gpgme_edit_cb_t has been changed to take a file descriptor argument.
+ The user is expected to write the response to the file descriptor,
+ followed by a newline.
+
* gpgme_op_verify and gpgme_op_decrypt_verify don't return a status
summary anymore. Use gpgme_get_sig_status to retrieve the individual
stati.
gpgme_status_code_t NEW
gpgme_io_cb_t CHANGED: Return type from void to GpgmeError.
gpgme_event_io_t CHANGED: New event type (all numbers changed).
+gpgme_passphrase_cb_t CHANGED: Desc decomposed, write directly to FD.
+gpgme_edit_cb_t CHANGED: Write directly to FD.
gpgme_key_get_string_attr CHANGED: Don't handle GPGME_ATTR_IS_SECRET.
gpgme_op_verify CHANGED: Drop R_STAT argument.
gpgme_op_decrypt_verify CHANGED: Drop R_STAT argument.
release all resources on error (for example to free assuan_cmd).
* Operations
-** Passphrase callback should not copy password. !!!
-*** If no passphrase cb is installed, status handler is not run even if
- password is required by crypto engine. !!
-*** Verify that passphrase callback beaves correctly with cancel etc.
+** If no passphrase cb is installed, status handler is not run even if
+ password is required by crypto engine. !!
** Export status handler need much more work. !!!
** Import should return a useful error when one happened.
*** Import does not take notice of NODATA status report.
+2003-05-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.texi (Passphrase Callback): Document new prototype.
+
2003-05-18 Marcus Brinkmann <marcus@g10code.de>
* gpgme.texi (Header): Remove Gpgme as namespace prefix. Add
@cindex callback, passphrase
@cindex passphrase callback
-@deftp {Data type} {gpgme_error_t (*gpgme_passphrase_cb_t)(void *@var{hook}, const char *@var{desc}, void **@var{r_hd}, const char **@var{result})}
+@deftp {Data type} {gpgme_error_t (*gpgme_passphrase_cb_t)(void *@var{hook}, const char *@var{uid_hint}, const char *@var{passphrase_info}, @w{int @var{prev_was_bad}}, @w{int @var{fd}})}
@tindex gpgme_passphrase_cb_t
The @code{gpgme_passphrase_cb_t} type is the type of functions usable as
passphrase callback function.
-The string @var{desc} contains a text usable to be displayed to the
-user of the application. The function should return a passphrase for
-the context when invoked with @var{desc} not being @code{NULL} in
-*@var{result}.
+The argument @var{uid_hint} might contain a string that gives an
+indication for which user ID the passphrase is required. If this is
+not available, or not applicable (in the case of symmetric encryption,
+for example), @var{uid_hint} will be @code{NULL}.
-The user may store information about the resources associated with the
-returned passphrase in @var{*r_hd}. When the passphrase is no longer
-needed by @acronym{GPGME}, the passphrase callback function will be
-called with @var{desc} being @var{NULL}, and @var{r_hd} being the same
-as at the first invocation.
+The argument @var{passphrase_info}, if not @code{NULL}, will give
+further information about the context in which the passphrase is
+required. This information is engine and operation specific.
+
+If this is the repeated attempt to get the passphrase, because
+previous attempts failed, then @var{prev_was_bad} is 1, otherwise it
+will be 0.
+
+The user must write the passphrase, followed by a newline character,
+to the file descriptor @var{fd}. If the user does not return 0
+indicating success, the user must at least write a newline character
+before returning from the callback.
If an error occurs, return the corresponding @code{gpgme_error_t} value.
You can use @code{GPGME_Canceled} to abort the operation. Otherwise,
2003-05-26 Marcus Brinkmann <marcus@g10code.de>
+ * engine.h (EngineCommandHandler): Change last argument to int fd.
+ * gpgme.h (gpgme_passphrase_cb_t): Rewritten to take parts of the
+ description and fd.
+ (gpgme_edit_cb_t): Change last argument to int fd.
+ * ops.h (_gpgme_passphrase_command_handler_internal): New prototype.
+ * passphrase.c: Include <assert.h>.
+ (op_data_t): Rename userid_hint to uid_hint, remove last_pw_handle.
+ (release_op_data): Check values before calling free.
+ (_gpgme_passphrase_status_handler): Likewise.
+ (_gpgme_passphrase_command_handler_internal): New function.
+ (_gpgme_passphrase_command_handler): Rewritten.
+ * edit.c (edit_status_handler): Pass -1 as fd argument.
+ (command_handler): Update prototype. New variable processed. Use
+ it to store return value of
+ _gpgme_passphrase_command_handler_internal which is now used
+ instead _gpgme_passphrase_command_handler. Use it also to check
+ if we should call the user's edit function. Pass fd to user's
+ edit function.
+ * rungpg.c (struct gpg_object_s): Change type of cmd.cb_data to
+ void *.
+ (gpg_release): Check value before calling free. Do not release
+ cmd.cb_data.
+ (command_cb): Function removed.
+ (command_handler): New function. Thus we don't use a data object
+ for command handler stuff anymore, but handle it directly. This
+ allows proper error reporting (cancel of passphrase requests, for
+ example). Also all callbacks work via direct writes to the file
+ descriptor (so that passphrases are not kept in insecure memory).
+ (gpg_set_command_handler): Rewritten to use even more ugly hacks.
+ (read_status): Check cmd.keyword before calling free. Install
+ command_handler as the I/O callback handler with GPG as private
+ data.
+
* rungpg.c (gpg_new): Add --enable-progress-filter to gpg
invocation.
* decrypt-verify.c (_gpgme_op_decrypt_verify_start): Rename to
return _gpgme_passphrase_status_handler (priv, status, args)
|| _gpgme_progress_status_handler (priv, status, args)
|| _gpgme_op_data_lookup (ctx, OPDATA_EDIT, (void **) &opd, -1, NULL)
- || (*opd->fnc) (opd->fnc_value, status, args, NULL);
+ || (*opd->fnc) (opd->fnc_value, status, args, -1);
}
static gpgme_error_t
command_handler (void *priv, gpgme_status_code_t status, const char *args,
- const char **result)
+ int fd)
{
gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
gpgme_error_t err;
op_data_t opd;
+ int processed = 0;
- *result = NULL;
if (ctx->passphrase_cb)
{
- err = _gpgme_passphrase_command_handler (ctx, status, args, result);
+ err = _gpgme_passphrase_command_handler_internal (ctx, status, args,
+ fd, &processed);
if (err)
return err;
}
- if (!*result)
+ if (!processed)
{
err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, (void **) &opd, -1, NULL);
if (err)
return err;
- return (*opd->fnc) (opd->fnc_value, status, args, result);
+ return (*opd->fnc) (opd->fnc_value, status, args, fd);
}
return 0;
}
typedef gpgme_error_t (*EngineCommandHandler) (void *priv,
gpgme_status_code_t code,
const char *keyword,
- const char **result);
+ int fd);
gpgme_error_t _gpgme_engine_new (gpgme_protocol_t proto,
EngineObject *r_engine);
/* Types for callback functions. */
/* Request a passphrase from the user. */
-typedef gpgme_error_t (*gpgme_passphrase_cb_t) (void *hook, const char *desc,
- void **r_hd,
- const char **result);
+typedef gpgme_error_t (*gpgme_passphrase_cb_t) (void *hook,
+ const char *uid_hint,
+ const char *passphrase_info,
+ int prev_was_bad, int fd);
/* Inform the user about progress made. */
typedef void (*gpgme_progress_cb_t) (void *opaque, const char *what,
/* Interact with the user about an edit operation. */
typedef gpgme_error_t (*gpgme_edit_cb_t) (void *opaque,
gpgme_status_code_t status,
- const char *args,
- const char **reply);
+ const char *args, int fd);
\f
/* Context management functions. */
char *args);
gpgme_error_t _gpgme_passphrase_command_handler (void *opaque,
gpgme_status_code_t code,
- const char *key,
- const char **result);
+ const char *key, int fd);
+gpgme_error_t _gpgme_passphrase_command_handler_internal (void *opaque,
+ gpgme_status_code_t code,
+ const char *key, int fd,
+ int *processed);
\f
/* From progress.c. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
#include "gpgme.h"
#include "context.h"
typedef struct
{
int no_passphrase;
- void *last_pw_handle;
- char *userid_hint;
+ char *uid_hint;
char *passphrase_info;
int bad_passphrase;
} *op_data_t;
{
op_data_t opd = (op_data_t) hook;
- free (opd->passphrase_info);
- free (opd->userid_hint);
+ if (opd->passphrase_info)
+ free (opd->passphrase_info);
+ if (opd->uid_hint)
+ free (opd->uid_hint);
}
\f
switch (code)
{
case GPGME_STATUS_USERID_HINT:
- if (opd->userid_hint)
- free (opd->userid_hint);
- if (!(opd->userid_hint = strdup (args)))
+ if (opd->uid_hint)
+ free (opd->uid_hint);
+ if (!(opd->uid_hint = strdup (args)))
return GPGME_Out_Of_Core;
break;
gpgme_error_t
-_gpgme_passphrase_command_handler (void *priv, gpgme_status_code_t code,
- const char *key, const char **result)
+_gpgme_passphrase_command_handler_internal (void *priv,
+ gpgme_status_code_t code,
+ const char *key, int fd,
+ int *processed)
{
gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
gpgme_error_t err;
op_data_t opd;
- if (!ctx->passphrase_cb)
- return 0;
+ assert (ctx->passphrase_cb);
err = _gpgme_op_data_lookup (ctx, OPDATA_PASSPHRASE, (void **) &opd,
sizeof (*opd), release_op_data);
if (err)
return err;
- if (!code)
- {
- /* We have been called for cleanup. */
- if (ctx->passphrase_cb)
- /* FIXME: Take the key in account. */
- err = ctx->passphrase_cb (ctx->passphrase_cb_value, NULL,
- &opd->last_pw_handle, NULL);
- *result = NULL;
- return err;
- }
-
- if (!key || !ctx->passphrase_cb)
- {
- *result = NULL;
- return 0;
- }
-
if (code == GPGME_STATUS_GET_HIDDEN && !strcmp (key, "passphrase.enter"))
{
- const char *userid_hint = opd->userid_hint;
- const char *passphrase_info = opd->passphrase_info;
- int bad_passphrase = opd->bad_passphrase;
- char *buf;
+ if (processed)
+ processed = 1;
+ err = ctx->passphrase_cb (ctx->passphrase_cb_value,
+ opd->uid_hint, opd->passphrase_info,
+ opd->bad_passphrase, fd);
+
+ /* Reset bad passphrase flag, in case it is correct now. */
opd->bad_passphrase = 0;
- if (!userid_hint)
- userid_hint = "[User ID hint missing]";
- if (!passphrase_info)
- passphrase_info = "[passphrase info missing]";
- buf = malloc (20 + strlen (userid_hint)
- + strlen (passphrase_info) + 3);
- if (!buf)
- return GPGME_Out_Of_Core;
- sprintf (buf, "%s\n%s\n%s",
- bad_passphrase ? "TRY_AGAIN":"ENTER",
- userid_hint, passphrase_info);
- err = ctx->passphrase_cb (ctx->passphrase_cb_value, buf,
- &opd->last_pw_handle, result);
- free (buf);
return err;
}
- *result = NULL;
return 0;
}
+
+
+gpgme_error_t
+_gpgme_passphrase_command_handler (void *priv, gpgme_status_code_t code,
+ const char *key, int fd)
+{
+ return _gpgme_passphrase_command_handler_internal (priv, code, key, fd,
+ NULL);
+}
{
int used;
int fd;
+ void *cb_data;
int idx; /* Index in fd_data_map */
- gpgme_data_t cb_data; /* hack to get init the above idx later */
gpgme_status_code_t code; /* last code */
char *keyword; /* what has been requested (malloced) */
EngineCommandHandler fnc;
{
struct arg_and_data_s *next = gpg->arglist->next;
- free (gpg->arglist);
+ if (gpg->arglist)
+ free (gpg->arglist);
gpg->arglist = next;
}
- free (gpg->status.buffer);
- free (gpg->colon.buffer);
+ if (gpg->status.buffer)
+ free (gpg->status.buffer);
+ if (gpg->colon.buffer)
+ free (gpg->colon.buffer);
if (gpg->argv)
free_argv (gpg->argv);
- gpgme_data_release (gpg->cmd.cb_data);
- free (gpg->cmd.keyword);
+ if (gpg->cmd.keyword)
+ free (gpg->cmd.keyword);
if (gpg->status.fd[0] != -1)
_gpgme_io_close (gpg->status.fd[0]);
_gpgme_io_close (gpg->colon.fd[0]);
if (gpg->colon.fd[1] != -1)
_gpgme_io_close (gpg->colon.fd[1]);
- free_fd_data_map (gpg->fd_data_map);
+ if (gpg->fd_data_map)
+ free_fd_data_map (gpg->fd_data_map);
if (gpg->cmd.fd != -1)
_gpgme_io_close (gpg->cmd.fd);
free (gpg);
}
-/* Here we handle --command-fd. This works closely together with the
- status handler. */
static gpgme_error_t
-command_cb (void *opaque, char *buffer, size_t length, size_t *nread)
+command_handler (void *opaque, int fd)
{
gpgme_error_t err;
- GpgObject gpg = opaque;
- const char *value;
- int value_len;
+ GpgObject gpg = (GpgObject) opaque;
- DEBUG0 ("command_cb: enter\n");
assert (gpg->cmd.used);
- if (!buffer || !length || !nread)
- return 0; /* These values are reserved for extensions. */
- *nread = 0;
- if (!gpg->cmd.code)
- {
- DEBUG0 ("command_cb: no code\n");
- return -1;
- }
-
- if (!gpg->cmd.fnc)
- {
- DEBUG0 ("command_cb: no user cb\n");
- return -1;
- }
+ assert (gpg->cmd.code);
+ assert (gpg->cmd.fnc);
- /* FIXME catch error */
- err = gpg->cmd.fnc (gpg->cmd.fnc_value,
- gpg->cmd.code, gpg->cmd.keyword, &value);
+ err = gpg->cmd.fnc (gpg->cmd.fnc_value, gpg->cmd.code, gpg->cmd.keyword, fd);
if (err)
return err;
- if (!value)
- {
- DEBUG0 ("command_cb: no data from user cb\n");
- gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value, &value);
- return -1;
- }
-
- value_len = strlen (value);
- if (value_len + 1 > length)
- {
- DEBUG0 ("command_cb: too much data from user cb\n");
- gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value, &value);
- return -1;
- }
-
- memcpy (buffer, value, value_len);
- if (!value_len || (value_len && value[value_len-1] != '\n'))
- buffer[value_len++] = '\n';
- *nread = value_len;
-
- gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value, &value);
gpg->cmd.code = 0;
/* And sleep again until read_status will wake us up again. */
/* XXX We must check if there are any more fds active after removing
}
+
/* The Fnc will be called to get a value for one of the commands with
a key KEY. If the Code pssed to FNC is 0, the function may release
resources associated with the returned value from another call. To
void *fnc_value, gpgme_data_t linked_data)
{
GpgObject gpg = engine;
- gpgme_data_t tmp;
- gpgme_error_t err;
- err = gpgme_data_new_with_read_cb (&tmp, command_cb, gpg);
- if (err)
- return err;
-
add_arg (gpg, "--command-fd");
- add_data (gpg, tmp, -2, 0);
- gpg->cmd.cb_data = tmp;
+ /* This is a hack. We don't have a real data object. The only
+ thing that matters is that we use something unique, so we use the
+ address of the cmd structure in the gpg object. */
+ add_data (gpg, (void *) &gpg->cmd, -2, 0);
gpg->cmd.fnc = fnc;
+ gpg->cmd.cb_data = (void *) &gpg->cmd;
gpg->cmd.fnc_value = fnc_value;
gpg->cmd.linked_data = linked_data;
gpg->cmd.used = 1;
if (use_agent)
argc++;
if (!gpg->cmd.used)
- argc++;
+ argc++; /* --batch */
argc += 2; /* --comment */
argv = calloc (argc + 1, sizeof *argv);
|| r->code == GPGME_STATUS_GET_HIDDEN))
{
gpg->cmd.code = r->code;
- free (gpg->cmd.keyword);
+ if (gpg->cmd.keyword)
+ free (gpg->cmd.keyword);
gpg->cmd.keyword = strdup (rest);
if (!gpg->cmd.keyword)
return GPGME_Out_Of_Core;
}
add_io_cb (gpg, gpg->cmd.fd, 0,
- _gpgme_data_outbound_handler,
- gpg->fd_data_map[gpg->cmd.idx].data,
+ command_handler, gpg,
&gpg->fd_data_map[gpg->cmd.idx].tag);
gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd;
gpg->cmd.fd = -1;
+2003-05-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * (t-decrypt-verify.c, t-decrypt.c, t-edit.c, t-encrypt-sign.c,
+ t-encrypt-sym.c, t-sign.c, t-signers.c): Include <unistd.h>.
+ (passphrase_cb): Rewritten.
+ * t-edit.c (edit_fnc): Rewritten.
+
2003-05-04 Marcus Brinkmann <marcus@g10code.de>
* gpg/t-keylist-sig.c (main): Remove timestamp check.
#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <unistd.h>
#include <gpgme.h>
static gpgme_error_t
-passphrase_cb (void *opaque, const char *desc, void **hd, const char **result)
+passphrase_cb (void *opaque, const char *uid_hint, const char *passphrase_info,
+ int last_was_bad, int fd)
{
- /* Cleanup by looking at *hd. */
- if (!desc)
- return 0;
-
- *result = "abc";
+ write (fd, "abc\n", 4);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <unistd.h>
#include <gpgme.h>
static gpgme_error_t
-passphrase_cb (void *opaque, const char *desc, void **hd, const char **result)
+passphrase_cb (void *opaque, const char *uid_hint, const char *passphrase_info,
+ int last_was_bad, int fd)
{
- /* Cleanup by looking at *hd. */
- if (!desc)
- return 0;
-
- *result = "abc";
+ write (fd, "abc\n", 4);
return 0;
}
#include <string.h>
#include <assert.h>
#include <errno.h>
+#include <unistd.h>
#include <gpgme.h>
static gpgme_error_t
-passphrase_cb (void *opaque, const char *desc,
- void **r_hd, const char **result)
+passphrase_cb (void *opaque, const char *uid_hint, const char *passphrase_info,
+ int last_was_bad, int fd)
{
- if (!desc)
- /* Cleanup by looking at *r_hd. */
- return 0;
-
- *result = "abc";
- fprintf (stderr, "%% requesting passphrase for `%s': ", desc);
- fprintf (stderr, "sending `%s'\n", *result);
-
+ write (fd, "abc\n", 4);
return 0;
}
gpgme_error_t
-edit_fnc (void *opaque, gpgme_status_code_t status, const char *args, const char **result)
+edit_fnc (void *opaque, gpgme_status_code_t status, const char *args, int fd)
{
+ char *result = NULL;
gpgme_data_t out = (gpgme_data_t) opaque;
fputs ("[-- Response --]\n", stdout);
fprintf (stdout, "[-- Code: %i, %s --]\n", status, args);
- if (result)
+ if (!strcmp (args, "keyedit.prompt"))
{
- if (!strcmp (args, "keyedit.prompt"))
- {
- static int step = 0;
-
- switch (step)
- {
- case 0:
- *result = "fpr";
- break;
- case 1:
- *result = "expire";
- break;
- default:
- *result = "quit";
- break;
- }
- step++;
- }
- else if (!strcmp (args, "keyedit.save.okay"))
- {
- *result = "Y";
- }
- else if (!strcmp (args, "keygen.valid"))
+ static int step = 0;
+
+ switch (step)
{
- *result = "0";
+ case 0:
+ result = "fpr";
+ break;
+ case 1:
+ result = "expire";
+ break;
+ default:
+ result = "quit";
+ break;
}
+ step++;
}
+ else if (!strcmp (args, "keyedit.save.okay"))
+ result = "Y";
+ else if (!strcmp (args, "keygen.valid"))
+ result = "0";
+ if (result)
+ {
+ write (fd, result, strlen (result));
+ write (fd, "\n", 1);
+ }
return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#include <gpgme.h>
static gpgme_error_t
-passphrase_cb (void *opaque, const char *desc, void **hd, const char **result)
+passphrase_cb (void *opaque, const char *uid_hint, const char *passphrase_info,
+ int last_was_bad, int fd)
{
- /* Cleanup by looking at *hd. */
- if (!desc)
- return 0;
-
- *result = "abc";
+ write (fd, "abc\n", 4);
return 0;
}
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <unistd.h>
#include <gpgme.h>
fail_if_err (GPGME_File_Error);
}
+
static gpgme_error_t
-passphrase_cb (void *opaque, const char *desc,
- void **r_hd, const char **result)
+passphrase_cb (void *opaque, const char *uid_hint, const char *passphrase_info,
+ int last_was_bad, int fd)
{
- if (!desc)
- /* Cleanup by looking at *r_hd. */
- return 0;
-
- *result = "abc";
- fprintf (stderr, "%% requesting passphrase for `%s': ", desc);
- fprintf (stderr, "sending `%s'\n", *result);
-
+ write (fd, "abc\n", 4);
return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#include <gpgme.h>
static gpgme_error_t
-passphrase_cb (void *opaque, const char *desc, void **hd, const char **result)
+passphrase_cb (void *opaque, const char *uid_hint, const char *passphrase_info,
+ int last_was_bad, int fd)
{
- /* Cleanup by looking at *hd. */
- if (!desc)
- return 0;
-
- *result = "abc";
+ write (fd, "abc\n", 4);
return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#include <gpgme.h>
static gpgme_error_t
-passphrase_cb (void *opaque, const char *desc, void **hd, const char **result)
+passphrase_cb (void *opaque, const char *uid_hint, const char *passphrase_info,
+ int last_was_bad, int fd)
{
- /* Cleanup by looking at *hd. */
- if (!desc)
- return 0;
-
- *result = "abc";
+ write (fd, "abc\n", 4);
return 0;
}