unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH 0/2] Prompting for the GPG password within Emacs
@ 2013-07-07 11:14 Neil Roberts
  2013-07-07 11:14 ` [PATCH 1/2] cli: crypto: Support requesting passwords via FDs from the caller Neil Roberts
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Neil Roberts @ 2013-07-07 11:14 UTC (permalink / raw)
  To: notmuch

Hi,

I've recently started using notmuch to try and read PGP-encrypted
email. However the trouble is I normally access my email remotely via
SSH and it's very difficult to get gpg-agent to work in those
circumstances. I've therefore made some patches to try and get Emacs
to prompt for the password. They are based on the way mml communicates
with gpg by having two pipes so that notmuch can notify Emacs that it
needs a password and it will reply on the other.

Regards,
- Neil

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/2] cli: crypto: Support requesting passwords via FDs from the caller
  2013-07-07 11:14 [PATCH 0/2] Prompting for the GPG password within Emacs Neil Roberts
@ 2013-07-07 11:14 ` Neil Roberts
  2013-07-07 11:14 ` [PATCH 2/2] emacs: crypto: Handle prompting for passwords Neil Roberts
  2013-07-07 23:08 ` [PATCH 0/2] Prompting for the GPG password within Emacs Daniel Kahn Gillmor
  2 siblings, 0 replies; 14+ messages in thread
From: Neil Roberts @ 2013-07-07 11:14 UTC (permalink / raw)
  To: notmuch

This adds --status-fd and --command-fd options to the show and reply
commands which are used to specify a file descriptor for communicating
with notmuch as it is running. Notmuch will write status messages to
the status-fd and expect responses to them on the command-fd. These
options are inspired by similar options for controlling gpg.

Currently the only use for this is to have a way for the CLI program
to query passwords from the calling application. When the client needs
a password it will write a string in the following format to the
status-fd:

[NOTMUCH:] GET_HIDDEN <user_id> <prompt> <reprompt>

The user_id and prompt are string arguments that are encoded like C
strings. Ie, they will be surrounded by "quotes" and will contain
backslash escape sequences. These can also be parsed directly by the
read function in Emacs. The last argument is either a 1 or 0 to
specify whether the password is being prompted for a second time.

When the application sees this status it is expected to write the
password followed by a newline to the command-fd.
---
 crypto.c         | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 notmuch-client.h |   2 +
 notmuch-reply.c  |   6 +-
 notmuch-show.c   |   6 +-
 4 files changed, 185 insertions(+), 13 deletions(-)

diff --git a/crypto.c b/crypto.c
index 9736517..eded53f 100644
--- a/crypto.c
+++ b/crypto.c
@@ -22,28 +22,188 @@
 
 #ifdef GMIME_ATLEAST_26
 
+#define NOTMUCH_PASSWORD_DATA_KEY "notmuch-password-data"
+
+typedef struct
+{
+    int status_fd;
+    GIOChannel *command_channel;
+} notmuch_password_data_t;
+
+static void
+free_password_data_cb (void *data_ptr)
+{
+    notmuch_password_data_t *data = data_ptr;
+
+    g_io_channel_unref (data->command_channel);
+    free (data);
+}
+
+static void
+escape_string (GString *buf,
+	       const char *str)
+{
+    const char *p;
+
+    g_string_append_c (buf, '"');
+
+    for (p = str; *p; p++)
+	switch (*p) {
+	case '\n':
+	    g_string_append (buf, "\\n");
+	    break;
+	case '\t':
+	    g_string_append (buf, "\\t");
+	    break;
+	case '\r':
+	    g_string_append (buf, "\\t");
+	    break;
+	case '"':
+	    g_string_append (buf, "\\\"");
+	    break;
+	default:
+	    if (*p < 32)
+		g_string_append_printf (buf, "\\0%o", *p);
+	    else
+		g_string_append_c (buf, *p);
+	    break;
+	}
+
+    g_string_append_c (buf, '"');
+}
+
+static gboolean
+write_all (const char *buf,
+	   size_t length,
+	   int fd)
+{
+    ssize_t wrote;
+
+    while (length > 0) {
+	wrote = write (fd, buf, length);
+	if (wrote == -1)
+	    return FALSE;
+	buf += wrote;
+	length -= wrote;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+password_request_cb (GMimeCryptoContext *ctx,
+		     const char *user_id,
+		     const char *prompt_ctx,
+		     gboolean reprompt,
+		     GMimeStream *response,
+		     GError **err)
+{
+    notmuch_password_data_t *data;
+    GString *buf;
+    gboolean write_result;
+    char *line = NULL;
+    gsize line_length;
+    ssize_t wrote;
+
+    data = g_object_get_data (G_OBJECT (ctx), NOTMUCH_PASSWORD_DATA_KEY);
+
+    buf = g_string_new ("[NOTMUCH:] GET_HIDDEN ");
+    escape_string (buf, user_id);
+    g_string_append_c (buf, ' ');
+    escape_string (buf, prompt_ctx);
+    g_string_append_c (buf, ' ');
+    g_string_append_c (buf, (!!reprompt) + '0');
+    g_string_append_c (buf, '\n');
+
+    write_result = write_all (buf->str, buf->len, data->status_fd);
+
+    g_string_free (buf, TRUE);
+
+    if (!write_result) {
+	g_set_error_literal (err,
+			     G_FILE_ERROR,
+			     g_file_error_from_errno (errno),
+			     strerror (errno));
+	return FALSE;
+    }
+
+    if (!g_io_channel_read_line (data->command_channel,
+				 &line,
+				 &line_length,
+				 NULL, /* terminator_pos */
+				 err))
+	return FALSE;
+
+    wrote = g_mime_stream_write (response, line, line_length);
+
+    /* TODO: is there a more reliable way to clear the memory
+     * containing a password? */
+    memset (line, 0, line_length);
+
+    g_free (line);
+
+    if (wrote < (ssize_t) line_length) {
+	g_set_error_literal (err,
+			     G_FILE_ERROR,
+			     G_FILE_ERROR_FAILED,
+			     "Failed to write password response");
+	return FALSE;
+    }
+
+    return TRUE;
+}
+
 /* Create a GPG context (GMime 2.6) */
-static notmuch_crypto_context_t *
-create_gpg_context (void)
+static notmuch_bool_t
+create_gpg_context (notmuch_crypto_t *crypto)
 {
     notmuch_crypto_context_t *gpgctx;
+    GMimePasswordRequestFunc password_func;
+    notmuch_password_data_t *password_data;
+
+    /* If the --status-fd and --command-fd options were specified then
+     * we can handle password requests by forwarding them on to the
+     * application that invoked notmuch */
+    if (crypto->status_fd != -1 && crypto->command_fd != -1) {
+	password_func = password_request_cb;
+	password_data = malloc (sizeof (*password_data));
+	if (password_data == NULL)
+	    return FALSE;
+	password_data->status_fd = crypto->status_fd;
+	password_data->command_channel =
+	    g_io_channel_unix_new (crypto->command_fd);
+    } else {
+	password_func = NULL;
+	password_data = NULL;
+    }
 
-    /* TODO: GMimePasswordRequestFunc */
-    gpgctx = g_mime_gpg_context_new (NULL, "gpg");
+    gpgctx = g_mime_gpg_context_new (password_func, "gpg");
     if (! gpgctx)
-	return NULL;
+	return FALSE;
+
+    /* The password callback for GMime doesn't seem to provide any
+     * user data pointer so we'll work around it by attaching the data
+     * to the gpg context, which it does pass */
+    if (password_data) {
+	g_object_set_data_full (G_OBJECT (gpgctx),
+				NOTMUCH_PASSWORD_DATA_KEY,
+				password_data,
+				free_password_data_cb);
+    }
 
     g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE);
     g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);
 
-    return gpgctx;
+    crypto->gpgctx = gpgctx;
+
+    return TRUE;
 }
 
 #else /* GMIME_ATLEAST_26 */
 
 /* Create a GPG context (GMime 2.4) */
-static notmuch_crypto_context_t *
-create_gpg_context (void)
+static notmuch_bool_t
+create_gpg_context (notmuch_crypto_t *crypto)
 {
     GMimeSession *session;
     notmuch_crypto_context_t *gpgctx;
@@ -53,11 +213,13 @@ create_gpg_context (void)
     g_object_unref (session);
 
     if (! gpgctx)
-	return NULL;
+	return FALSE;
 
     g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);
 
-    return gpgctx;
+    crypto->gpgctx = gpgctx;
+
+    return TRUE;
 }
 
 #endif /* GMIME_ATLEAST_26 */
@@ -78,7 +240,7 @@ notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)
     if (strcasecmp (protocol, "application/pgp-signature") == 0 ||
 	strcasecmp (protocol, "application/pgp-encrypted") == 0) {
 	if (! crypto->gpgctx) {
-	    crypto->gpgctx = create_gpg_context ();
+	    create_gpg_context (crypto);
 	    if (! crypto->gpgctx)
 		fprintf (stderr, "Failed to construct gpg context.\n");
 	}
diff --git a/notmuch-client.h b/notmuch-client.h
index 5f2a6d0..edf47ce 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -80,6 +80,8 @@ typedef struct notmuch_crypto {
     notmuch_crypto_context_t* gpgctx;
     notmuch_bool_t verify;
     notmuch_bool_t decrypt;
+    int status_fd;
+    int command_fd;
 } notmuch_crypto_t;
 
 typedef struct notmuch_show_params {
diff --git a/notmuch-reply.c b/notmuch-reply.c
index e151f78..d46bec4 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -718,7 +718,9 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
 	.part = -1,
 	.crypto = {
 	    .verify = FALSE,
-	    .decrypt = FALSE
+	    .decrypt = FALSE,
+	    .status_fd = -1,
+	    .command_fd = -1
 	}
     };
     int format = FORMAT_DEFAULT;
@@ -738,6 +740,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
 				  { "sender", FALSE },
 				  { 0, 0 } } },
 	{ NOTMUCH_OPT_BOOLEAN, &params.crypto.decrypt, "decrypt", 'd', 0 },
+	{ NOTMUCH_OPT_INT, &params.crypto.status_fd, "status-fd", 0, 0 },
+	{ NOTMUCH_OPT_INT, &params.crypto.command_fd, "command-fd", 0, 0 },
 	{ 0, 0, 0, 0, 0 }
     };
 
diff --git a/notmuch-show.c b/notmuch-show.c
index 62178f7..0a67807 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -1076,7 +1076,9 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 	.output_body = TRUE,
 	.crypto = {
 	    .verify = FALSE,
-	    .decrypt = FALSE
+	    .decrypt = FALSE,
+	    .status_fd = -1,
+	    .command_fd = -1
 	}
     };
     int format_sel = NOTMUCH_FORMAT_NOT_SPECIFIED;
@@ -1104,6 +1106,8 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 	{ NOTMUCH_OPT_INT, &params.part, "part", 'p', 0 },
 	{ NOTMUCH_OPT_BOOLEAN, &params.crypto.decrypt, "decrypt", 'd', 0 },
 	{ NOTMUCH_OPT_BOOLEAN, &params.crypto.verify, "verify", 'v', 0 },
+	{ NOTMUCH_OPT_INT, &params.crypto.status_fd, "status-fd", 0, 0 },
+	{ NOTMUCH_OPT_INT, &params.crypto.command_fd, "command-fd", 0, 0 },
 	{ NOTMUCH_OPT_BOOLEAN, &params.output_body, "body", 'b', 0 },
 	{ 0, 0, 0, 0, 0 }
     };
-- 
1.7.11.3.g3c3efa5

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 2/2] emacs: crypto: Handle prompting for passwords
  2013-07-07 11:14 [PATCH 0/2] Prompting for the GPG password within Emacs Neil Roberts
  2013-07-07 11:14 ` [PATCH 1/2] cli: crypto: Support requesting passwords via FDs from the caller Neil Roberts
@ 2013-07-07 11:14 ` Neil Roberts
  2013-07-07 23:08 ` [PATCH 0/2] Prompting for the GPG password within Emacs Daniel Kahn Gillmor
  2 siblings, 0 replies; 14+ messages in thread
From: Neil Roberts @ 2013-07-07 11:14 UTC (permalink / raw)
  To: notmuch

This makes the Emacs client handle prompting for passwords when
decrypting PGP messages via the new --status-fd and --command-fd
options to the show and reply commands.

The prompting is handled directly in notmuch-call-notmuch-sexp.
Instead of calling call-process to invoke notmuch synchronously it is
now run asynchronously via notmuch-start-notmuch so that it can
install a process filter to handle the status messages. The function
then runs a loop calling accept-process-output so that it can block
until the process actually completes.

Emacs doesn't support having multiple file descriptors to talk to a
child process apart from stdin/out so the --status-fd is set to 1 and
the --command-fd is set to 0. This means that the status messages will
actually be interleaved with the sexp output. I think this shouldn't
be a problem because both the sexp output and the status messages are
split into lines and it shouldn't be possible for a sexp message to
begin with [NOTMUCH:] so it is easy to separate out the two types of
message.

The process filter collects the output into a temporary buffer.
Whenever it receives a line beginning with [NOTMUCH:] it will process
the command and remove the line so that the final buffer only contains
the sexp. The only handler currently is for GET_HIDDEN which just
calls read-passwd to prompt for the password and then writes it back
out to the stdin of the notmuch process.

This is based on similar functionality in epg.el which handles
invoking GPG.
---
 emacs/notmuch-lib.el   | 66 +++++++++++++++++++++++++++++++++++++++++++-------
 emacs/notmuch-mua.el   |  2 +-
 emacs/notmuch-query.el |  3 ++-
 3 files changed, 60 insertions(+), 11 deletions(-)

diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el
index 8deb7de..0cc23fe 100644
--- a/emacs/notmuch-lib.el
+++ b/emacs/notmuch-lib.el
@@ -467,6 +467,48 @@ You may need to restart Emacs or upgrade your notmuch package."))
 	;; `notmuch-logged-error' does not return.
 	))))
 
+(defun notmuch--status-GET_HIDDEN (process args)
+  ;; The args string should be readable by emacs if it is made into a list
+  (let* ((args-list (read (concat "(" args ")")))
+	 (prompt (nth 1 args-list))
+	 (pwd (read-passwd (if prompt
+			       (concat prompt ": ")
+			     "GPG password: "))))
+    (process-send-string process (concat pwd "\n"))
+    (clear-string pwd)))
+
+(defun notmuch--process-filter (process string)
+  "Callback used by `notmuch-call-notmuch-sexp` used to handle the status fd."
+
+  (when (buffer-live-p (process-buffer process))
+    (with-current-buffer (process-buffer process)
+
+      (goto-char (point-max))
+
+      (let ((insertion-point (point)))
+	(insert string)
+
+	;; Check if the string contains any status lines
+	(goto-char insertion-point)
+	(beginning-of-line)
+
+	(while (re-search-forward "^\\[NOTMUCH:\\].*\n" nil t)
+	  (let ((line-beginning (match-beginning 0))
+		(line-end (match-end 0)))
+	    (goto-char (+ line-beginning 10))
+	    (when (looking-at " *\\([A-Z_]+\\)")
+	      ;; If there is a function defined that looks like it is made
+	      ;; to handle this particular status then call it passing the
+	      ;; remainder of the line as an argument
+	      (let ((symbol (intern-soft (concat "notmuch--status-"
+						 (match-string 1)))))
+		(when (and symbol (fboundp symbol))
+		  (funcall symbol
+			   process
+			   (buffer-substring (match-end 0) line-end)))))
+	    ;; Remove the status so that it won't form part of the sexp
+	    (delete-region line-beginning line-end)))))))
+
 (defun notmuch-call-notmuch-sexp (&rest args)
   "Invoke `notmuch-command' with ARGS and return the parsed S-exp output.
 
@@ -474,15 +516,21 @@ If notmuch exits with a non-zero status, this will pop up a
 buffer containing notmuch's output and signal an error."
 
   (with-temp-buffer
-    (let ((err-file (make-temp-file "nmerr")))
-      (unwind-protect
-	  (let ((status (apply #'call-process
-			       notmuch-command nil (list t err-file) nil args)))
-	    (notmuch-check-exit-status status (cons notmuch-command args)
-				       (buffer-string) err-file)
-	    (goto-char (point-min))
-	    (read (current-buffer)))
-	(delete-file err-file)))))
+    (let ((proc (apply #'notmuch-start-notmuch
+		       "notmuch"
+		       (current-buffer) nil args)))
+      (set-process-filter proc #'notmuch--process-filter)
+
+      ;; Synchronously wait until the process completes
+      (while (eq (process-status proc) 'run)
+	(accept-process-output proc 1))
+
+      ;; According to similar code in epg-wait-for-completion, this is
+      ;; needed to run the process filter right now.
+      (sleep-for 0.1)
+
+      (goto-char (point-min))
+      (read (current-buffer)))))
 
 (defun notmuch-start-notmuch (name buffer sentinel &rest args)
   "Start and return an asynchronous notmuch command.
diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 329d342..af7dd03 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -150,7 +150,7 @@ list."
 	reply
 	original)
     (when notmuch-show-process-crypto
-      (setq args (append args '("--decrypt"))))
+      (setq args (append args '("--decrypt" "--status-fd=1" "--command-fd=0"))))
 
     (if reply-all
 	(setq args (append args '("--reply-to=all")))
diff --git a/emacs/notmuch-query.el b/emacs/notmuch-query.el
index 51d427f..0788e8c 100644
--- a/emacs/notmuch-query.el
+++ b/emacs/notmuch-query.el
@@ -31,7 +31,8 @@ is a possibly empty forest of replies.
 "
   (let ((args '("show" "--format=sexp" "--format-version=1")))
     (if notmuch-show-process-crypto
-	(setq args (append args '("--decrypt"))))
+	(setq args (append args
+			   '("--decrypt" "--status-fd=1" "--command-fd=0"))))
     (setq args (append args search-terms))
     (apply #'notmuch-call-notmuch-sexp args)))
 
-- 
1.7.11.3.g3c3efa5

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-07 11:14 [PATCH 0/2] Prompting for the GPG password within Emacs Neil Roberts
  2013-07-07 11:14 ` [PATCH 1/2] cli: crypto: Support requesting passwords via FDs from the caller Neil Roberts
  2013-07-07 11:14 ` [PATCH 2/2] emacs: crypto: Handle prompting for passwords Neil Roberts
@ 2013-07-07 23:08 ` Daniel Kahn Gillmor
  2013-07-08  6:45   ` craven
  2013-07-08 11:07   ` Neil Roberts
  2 siblings, 2 replies; 14+ messages in thread
From: Daniel Kahn Gillmor @ 2013-07-07 23:08 UTC (permalink / raw)
  To: Neil Roberts; +Cc: notmuch

[-- Attachment #1: Type: text/plain, Size: 1390 bytes --]

Hi Niel--

On 07/07/2013 07:14 AM, Neil Roberts wrote:

> I've recently started using notmuch to try and read PGP-encrypted
> email. However the trouble is I normally access my email remotely via
> SSH and it's very difficult to get gpg-agent to work in those
> circumstances. I've therefore made some patches to try and get Emacs
> to prompt for the password. They are based on the way mml communicates
> with gpg by having two pipes so that notmuch can notify Emacs that it
> needs a password and it will reply on the other.

I strongly encourage you to get the gpg-agent model sorted out for your
use case, instead of moving in the direction of this patch series.

The fewer tools that handle your OpenPGP passphrase the better, and
future versions of GnuPG will not be able to work without the gpg-agent
anyway (all secret key activity will be handled by the agent as of gnupg
version 2.1, if i understand upstream's development plans correctly).

I personally hope that notmuch (and notmuch-emacs) will resist the urge
to try to handle any sort of sensitive material like secret keys or
passphrases directly, but will leave that work to libraries or
out-of-process agents.

Can you describe what you've tried in terms of using gpg-agent?  where
are your secret keys stored?  are they on your local machine, or on the
remote machine?

Regards,

	--dkg


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 1027 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-07 23:08 ` [PATCH 0/2] Prompting for the GPG password within Emacs Daniel Kahn Gillmor
@ 2013-07-08  6:45   ` craven
  2013-07-08 10:55     ` Daniel Kahn Gillmor
  2013-07-08 11:07   ` Neil Roberts
  1 sibling, 1 reply; 14+ messages in thread
From: craven @ 2013-07-08  6:45 UTC (permalink / raw)
  To: Daniel Kahn Gillmor, Neil Roberts; +Cc: notmuch

>> I've recently started using notmuch to try and read PGP-encrypted
>> email. However the trouble is I normally access my email remotely via
>> SSH and it's very difficult to get gpg-agent to work in those
>> circumstances. I've therefore made some patches to try and get Emacs
>> to prompt for the password. They are based on the way mml communicates
>> with gpg by having two pipes so that notmuch can notify Emacs that it
>> needs a password and it will reply on the other.

This is what I have tried too :) My setup is as follows:
I run emacs in non-window mode (-nw) on a server machine under tmux (a
terminal multiplexer, like screen). I connect to this via SSH from
different machines (maybe even across multiple SSH logins).

> I strongly encourage you to get the gpg-agent model sorted out for your
> use case, instead of moving in the direction of this patch series.
> Can you describe what you've tried in terms of using gpg-agent?  where
> are your secret keys stored?  are they on your local machine, or on the
> remote machine?

I have the secret keys on the local machine and the remote machine.
gpg-agent runs fine on the remote machine, however I have found no way
to make it consistently prompt for the password on the right SSH
connection (I don't believe there even is a way to achieve this, as
there may be multiple, or I may leave several connections open and move
between machines). The only thing that mostly works is using the remote
gpg-agent to prompt on the terminal, which messes up the display
temporarily.

I'd be really happy about any ideas how to solve this :)

Greetings,

Peter

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-08  6:45   ` craven
@ 2013-07-08 10:55     ` Daniel Kahn Gillmor
  2013-07-08 21:39       ` Peter Feigl
  0 siblings, 1 reply; 14+ messages in thread
From: Daniel Kahn Gillmor @ 2013-07-08 10:55 UTC (permalink / raw)
  To: craven; +Cc: notmuch

[-- Attachment #1: Type: text/plain, Size: 1437 bytes --]

On 07/08/2013 02:45 AM, craven@gmx.net wrote:
> This is what I have tried too :) My setup is as follows:
> I run emacs in non-window mode (-nw) on a server machine under tmux (a
> terminal multiplexer, like screen). I connect to this via SSH from
> different machines (maybe even across multiple SSH logins).

cool, this makes sense to me.

on the remote server, do you have the pinentry-curses and gnupg-agent
packages installed (these are the packages' names on debian and
debian-derived systems -- they might be named something else on your
distro).

do you have use-agent (on its own line) in ~/.gnupg/gpg.conf on the server?

> I have the secret keys on the local machine and the remote machine.
> gpg-agent runs fine on the remote machine, however I have found no way
> to make it consistently prompt for the password on the right SSH
> connection (I don't believe there even is a way to achieve this, as
> there may be multiple, or I may leave several connections open and move
> between machines). The only thing that mostly works is using the remote
> gpg-agent to prompt on the terminal, which messes up the display
> temporarily.

The route i suggest above (with pinentry-curses might be what you're
describing here.  when you say "messes up the display temporarily" can
you be more specific about what it does to the display?  this may be a
bug in pinentry-curses that could be fixed.

	--dkg


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 1027 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-07 23:08 ` [PATCH 0/2] Prompting for the GPG password within Emacs Daniel Kahn Gillmor
  2013-07-08  6:45   ` craven
@ 2013-07-08 11:07   ` Neil Roberts
  2013-07-08 11:44     ` Daniel Kahn Gillmor
  1 sibling, 1 reply; 14+ messages in thread
From: Neil Roberts @ 2013-07-08 11:07 UTC (permalink / raw)
  To: Daniel Kahn Gillmor, craven; +Cc: notmuch

Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes:

> The fewer tools that handle your OpenPGP passphrase the better, and
> future versions of GnuPG will not be able to work without the gpg-agent
> anyway (all secret key activity will be handled by the agent as of gnupg
> version 2.1, if i understand upstream's development plans correctly).

Ok, I didn't realise that GnuPG will be taking away for support for
directly prompting for passwords. I agree that using gpg-agent would be
better but this patch seemed to be simpler to get working in my use case
and it matches how mml currently handles it.

> Can you describe what you've tried in terms of using gpg-agent? where
> are your secret keys stored? are they on your local machine, or on the
> remote machine?

Both machines are trusted personal machines so I can put the keys on
either (or both). I think what would be ideal is if OpenSSH could
support gpg-agent forwarding like it does for ssh-agent. That way when
gpg needs a password it could prompt for it via GNOME Keyring on my
local machine. I thought about trying to patch OpenSSH but to be honest
I lost all motivation when I noticed that it is still maintained in CVS.
I found that someone else has made a patch to add support for forwarding
of arbitrary Unix domain sockets¹ but it is now out of date. I suppose
that could be used quite easily to do gpg-agent forwarding. I couldn't
find any feedback from any of the maintainers about why it isn't in the
main source code tree yet. It seems like quite a compelling feature. I'm
guessing (although I'm not sure) that it requires a change in the
protocol and presumably I would have to also compile the server from
source so it seemed like quite a lot of faff to start using that patch
and I opted for this simpler approach instead.

Regards,
- Neil

1. https://bugzilla.mindrot.org/show_bug.cgi?id=1256
---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-08 11:07   ` Neil Roberts
@ 2013-07-08 11:44     ` Daniel Kahn Gillmor
  2013-07-08 12:19       ` Neil Roberts
  0 siblings, 1 reply; 14+ messages in thread
From: Daniel Kahn Gillmor @ 2013-07-08 11:44 UTC (permalink / raw)
  To: Neil Roberts; +Cc: notmuch

[-- Attachment #1: Type: text/plain, Size: 1777 bytes --]

Hi Niel--

On 07/08/2013 07:07 AM, Neil Roberts wrote:

> Both machines are trusted personal machines so I can put the keys on
> either (or both).

cool, this makes it a little bit easier.

> I think what would be ideal is if OpenSSH could
> support gpg-agent forwarding like it does for ssh-agent.

Hm, interesting.  I bet we could figure out a way to do this with
existing OpenSSH without needing to patch anything, as long as you're
willing to use helper utilities like socat.

it came up on a blog post i made a while back about forwarding
unix-domain sockets over ssh:

 https://www.debian-administration.org/users/dkg/weblog/68

but no one offered an explicit recipe, and my examples there are for
forwarding a unix domain socket from the ssh client to the ssh server,
which i think is the reverse of what you're proposing.


I just did a little test, and got the following to work with a single
connection (a bit more tuning and you can probably make it work repeatedly):

on the remote server (i'll call it "xxx"), i did:

 mkdir ~/.sockets
 chmod 0700 ~/.sockets
 export GPG_AGENT_INFO=~/.sockets/S.gpg-agent:0:1

and on my local machine, i ran the following bash command (this is all
one command, sorry about the line wrap):

  socat
   EXEC:'ssh xxx socat UNIX-LISTEN\:.sockets/S.gpg-agent STDIO'
   UNIX:${GPG_AGENT_INFO%%:*}

then on the remote server, i created a secret key, and ran:

  echo test > test.txt
  gpg --clearsign test.txt

and was prompted by my local graphical gpg-agent.

note that this means that any passphrases cached by my local gpg-agent
are also visible to the account on the remote server, but in your
scenario (you control and trust both machines) that should be OK.

hth,

	--dkg


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 1027 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-08 11:44     ` Daniel Kahn Gillmor
@ 2013-07-08 12:19       ` Neil Roberts
  2013-07-08 12:22         ` Daniel Kahn Gillmor
  0 siblings, 1 reply; 14+ messages in thread
From: Neil Roberts @ 2013-07-08 12:19 UTC (permalink / raw)
  To: Daniel Kahn Gillmor; +Cc: notmuch

Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes:

> Hm, interesting.  I bet we could figure out a way to do this with
> existing OpenSSH without needing to patch anything, as long as you're
> willing to use helper utilities like socat.

Heh, yeah, I also experimented with going down this route. I wrote an
overly complicated C program¹ to manage launching the two SSH sessions.
That also only handles one session, but yeah I guess you could get the
program to relaunch the SSH session after the first connection dies.

I came across some other people who were suggesting² to make socat wrap
the Unix socket in a TCP socket and then tunnel that over SSH. I guess
that would be a simple way to support multiple sessions. However it
seems a bit dodgy to open a port because you can't restrict the access
by user. In my case though that wouldn't really be a problem because I'm
the only one with an account on both machines.

Regards,
- Neil

1. http://git.busydoingnothing.co.uk/cgit/gpgssh.git/tree/gpgssh.c
2. http://is.gd/Fy0C4g
---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-08 12:19       ` Neil Roberts
@ 2013-07-08 12:22         ` Daniel Kahn Gillmor
  0 siblings, 0 replies; 14+ messages in thread
From: Daniel Kahn Gillmor @ 2013-07-08 12:22 UTC (permalink / raw)
  To: Neil Roberts; +Cc: notmuch

[-- Attachment #1: Type: text/plain, Size: 1008 bytes --]

On 07/08/2013 08:19 AM, Neil Roberts wrote:
> Heh, yeah, I also experimented with going down this route. I wrote an
> overly complicated C program¹ to manage launching the two SSH sessions.
> That also only handles one session, but yeah I guess you could get the
> program to relaunch the SSH session after the first connection dies.

did you try the approach i outlined?  no extra C code needed, and if you
want it to re-launch, you should be able to just wrap it in a shell for
loop :)  You might also want a ,reuseaddr on the inner UNIX-LISTEN socat
endpoint.

> I came across some other people who were suggesting² to make socat wrap
> the Unix socket in a TCP socket and then tunnel that over SSH. I guess
> that would be a simple way to support multiple sessions. However it
> seems a bit dodgy to open a port because you can't restrict the access
> by user.

I agree you don't want to wrap it up in a TCP socket, for exactly the
access control reasons you describe.

	--dkg



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 1027 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-08 10:55     ` Daniel Kahn Gillmor
@ 2013-07-08 21:39       ` Peter Feigl
  2013-07-09 13:26         ` Peter Feigl
  0 siblings, 1 reply; 14+ messages in thread
From: Peter Feigl @ 2013-07-08 21:39 UTC (permalink / raw)
  To: Daniel Kahn Gillmor; +Cc: notmuch

Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes:
> The route i suggest above (with pinentry-curses might be what you're
> describing here.  when you say "messes up the display temporarily" can
> you be more specific about what it does to the display?  this may be a
> bug in pinentry-curses that could be fixed.

pinentry-curses does not work reliably under tmux (and thus neither
inside emacs inside tmux). it sometimes works, sometimes loses track of
input (stars or even unstarred letters appearing somewhere else on the
screen).
But this seems to be related to pinentry-curses, not notmuch, so I'll
try to take it somewhere there, maybe they or the tmux people can help
me :)

Thanks for your suggestions!

Greetings, Peter

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-08 21:39       ` Peter Feigl
@ 2013-07-09 13:26         ` Peter Feigl
  2013-07-10  7:27           ` Peter Feigl
  0 siblings, 1 reply; 14+ messages in thread
From: Peter Feigl @ 2013-07-09 13:26 UTC (permalink / raw)
  To: Daniel Kahn Gillmor; +Cc: notmuch

> But this seems to be related to pinentry-curses, not notmuch, so I'll
> try to take it somewhere there, maybe they or the tmux people can help
> me :)

Further investigation shows that pinentry-curses doesn't work under
TERM=dumb (which emacs seems to use), and I cannot find a way to make
MML use gpg1 (instead of gpg2, which is the default for Arch Linux). EPA
for en- and decrypting files works fine though.
If anyone knows how to fix this, I'd be really glad about pointers.

Thank you,

Peter

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-09 13:26         ` Peter Feigl
@ 2013-07-10  7:27           ` Peter Feigl
  2013-07-13  8:37             ` Tomi Ollila
  0 siblings, 1 reply; 14+ messages in thread
From: Peter Feigl @ 2013-07-10  7:27 UTC (permalink / raw)
  To: notmuch

[-- Attachment #1: Type: text/plain, Size: 302 bytes --]

Just in case anyone is interested, I wrote a simple pinentry-emacs
script, that uses emacsclient to ask inside emacs for the password.

Probably not secure, but I'm the only one accessing this host.

https://github.com/ecraven/pinentry-emacs

Finally things seem to work reliably :)

Greetings,

Peter

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 274 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/2] Prompting for the GPG password within Emacs
  2013-07-10  7:27           ` Peter Feigl
@ 2013-07-13  8:37             ` Tomi Ollila
  0 siblings, 0 replies; 14+ messages in thread
From: Tomi Ollila @ 2013-07-13  8:37 UTC (permalink / raw)
  To: Peter Feigl, notmuch

On Wed, Jul 10 2013, Peter Feigl <craven@gmx.net> wrote:

> Just in case anyone is interested, I wrote a simple pinentry-emacs
> script, that uses emacsclient to ask inside emacs for the password.
>
> Probably not secure, but I'm the only one accessing this host.
>
> https://github.com/ecraven/pinentry-emacs
>
> Finally things seem to work reliably :)

Hoo, using emacsclient is a pretty novel idea to work around the
"problem".

The TERM=dumb is not the only problem with pinentry-curses:
/dev/tty is set up by emacs and it doesn't passthrough input
to the pinentry program. The orignal tty (like /dev/pts/2) emacs
is running is available in TTY environment variable but emacs would
compete with the input...

... these are my recollections of the problems -- In one 
Scientific Linux 6 system I am using has gpg2 installed -- running
`emacs foo.org.gpg` fails miserably due to these problems. I'll wait
until gpg2 comes into more widespread use and align to the methods
that prevail there...

> Greetings,
>
> Peter

Tomi

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2013-07-13  8:37 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-07-07 11:14 [PATCH 0/2] Prompting for the GPG password within Emacs Neil Roberts
2013-07-07 11:14 ` [PATCH 1/2] cli: crypto: Support requesting passwords via FDs from the caller Neil Roberts
2013-07-07 11:14 ` [PATCH 2/2] emacs: crypto: Handle prompting for passwords Neil Roberts
2013-07-07 23:08 ` [PATCH 0/2] Prompting for the GPG password within Emacs Daniel Kahn Gillmor
2013-07-08  6:45   ` craven
2013-07-08 10:55     ` Daniel Kahn Gillmor
2013-07-08 21:39       ` Peter Feigl
2013-07-09 13:26         ` Peter Feigl
2013-07-10  7:27           ` Peter Feigl
2013-07-13  8:37             ` Tomi Ollila
2013-07-08 11:07   ` Neil Roberts
2013-07-08 11:44     ` Daniel Kahn Gillmor
2013-07-08 12:19       ` Neil Roberts
2013-07-08 12:22         ` Daniel Kahn Gillmor

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).