[PATCH] Add inside-Emacs mode to GUI pinentry programs

Neal H. Walfield neal at walfield.org
Thu Jun 4 22:43:22 CEST 2015


Hi Daiki,

My comments are inline.

Thanks!

:) Neal

At Tue, 02 Jun 2015 17:56:44 +0900,
Daiki Ueno wrote:
> +AM_CONDITIONAL(BUILD_LIBPINENTRY_EMACS,
> +              test "$pinentry_emacs" = "yes" -o "$inside_emacs" = "yes")

I'm a bit confused by this.  I can unstand building the emacs pinentry
without the inside emacs check, but doesn't inside_emacs imply
pinentry_emacs?  If so, we only need to check if "$pinentry_emacs" =
"yes".  Right?

> diff --git a/pinentry/pinentry-emacs.c b/pinentry/pinentry-emacs.c
> new file mode 100644
> index 0000000..280546d
> --- /dev/null
> +++ b/pinentry/pinentry-emacs.c
> +  if (strlen (tmpdir) + strlen ("/emacs") + 10 + strlen ("/")
> +      + strlen (socket_name) + 1 >= sizeof (unaddr.sun_path))
> +    {
> +      fprintf (stderr, "socket name is too long\n");
> +      return 0;
> +    }

> +  sprintf (unaddr.sun_path, "%s/emacs%lu/%s", tmpdir,
> +	   (unsigned long) uid, socket_name);

Technically, this is too conservative.  10 is the maximum length of
the uid.  If the user is 1000, then only 4 characters are needed.

> +/* Percent-escape control characters in DATA.  Return a newly
> +   allocated string, or DATA itself if there is no need to escape any
> +   character.  Store the length of the string to LENGTHP.  */
> +static char *
> +escape (const char *data, size_t *lengthp)
> +{
> +  char *buffer, *out_p;
> +  size_t length = *lengthp, buffer_length;

The comment is inaccurate.  *LENGTHP needs to contain the length of
DATA.  It also doesn't return a string, but a character vector (it is
not NUL terminated as far as I can tell).  This is actually a problem
for set_label (and perhaps elsewhere): it calls escape with the length
of the string, not including the NUL character!

Also, the return semantics are a bit too smart for my taste.  It would
be better to always allocate a new string.

> +/* The inverse of escape.  Removes quoting in string STR by modifying
> +   the string in place.  */
> +static char *
> +unescape (char *data, size_t *lengthp)
> +{

The semantics doesn't match escape's semantics: *LENGTHP is not the
length of the character vector.  Instead, this function assumes that
DATA is a NUL-terminated C string.  This lack of symmetry is another
reason to change escape.

> +static int
> +do_password (pinentry_t pe)
> +{
> +  char *escaped, *password;
> +  size_t length;
> +  assuan_error_t error;
> +
> +  set_labels (pe);
> +
> +  if (!send_to_emacs (emacs_socket, "GETPIN\n", 7))
> +    return -1;
> +
> +  escaped = read_from_emacs (emacs_socket, pe->timeout, NULL, &length, &error);

It is a bit unfortunate that the password is initially read into
insecure memory.  Let's allocate, say, 1k of secure memory and use
that.

> +  password = unescape (escaped, &length);
> +  pinentry_setbufferlen (pe, length);
> +  if (pe->pin)
> +    memcpy (pe->pin, password, length);

You should NUL terminate the password.

> +static int
> +do_confirm (pinentry_t pe)
> +{
> +  char buffer[16];
> +  size_t length;
> +  assuan_error_t error;
> +
> +  set_labels (pe);
> +
> +  if (!send_to_emacs (emacs_socket, "CONFIRM\n", 8))
> +    return 0;
> +
> +  length = sizeof (buffer);
> +  read_from_emacs (emacs_socket, pe->timeout, buffer, &length, &error);
> +  if (error != ASSUAN_No_Error)
> +    {
> +      pe->specific_err = error;
> +      return 0;
> +    }
> +
> +  return 1;

You don't handle the case when the user pressed cancelled (we need to
set pe->cancelled) or the third button.  It is important to get at
least the former right.  Are there any specific reasons that this is
difficult?

> +int
> +pinentry_inside_emacs (void)
> +{
> +  const char *envvar;
> +
> +  /* Check if INSIDE_EMACS envvar is set.  */
> +  envvar = getenv ("INSIDE_EMACS");
> +  if (!envvar || !*envvar)
> +    return 0;
> +
> +  /* FIXME: Additional checks for the value.  */
> +  return 1;
> +}
> +
> +int
> +pinentry_emacs_init (void)
> +{
> +  char buffer[256];
> +  size_t length = sizeof (buffer);
> +  assuan_error_t error;
> +
> +  /* Check if we can connect to the Emacs server socket.  */
> +  if (!set_socket ("pinentry"))
> +    return 0;
> +
> +  /* Check if the server responds.  */
> +  read_from_emacs (emacs_socket, INITIAL_TIMEOUT, buffer, &length, &error);
> +  return error == ASSUAN_No_Error;
> +}

Why doesn't pinentry_inside_emacs directly call pinentry_emacs_init?
It seems to me that the real test of whether we can use an emacs pin
is there?

Also, in your previous patch, you checked for the existence of a
particular symbol.  Why don't you do that anymore?  I think it is
reasonable that the INSIDE_EMACS is set, but the version of emacs
doesn't have support for pinentry.

Thanks,

Neal



More information about the Gnupg-devel mailing list