gpg: Add pinentry-mode feature.
authorWerner Koch <wk@gnupg.org>
Thu, 7 Feb 2013 19:37:58 +0000 (20:37 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 7 Feb 2013 19:37:58 +0000 (20:37 +0100)
* g10/gpg.c: Include shareddefs.h.
(main): Add option --pinentry-mode.
* g10/options.h (struct opt): Add field pinentry_mode.
* g10/passphrase.c: Include shareddefs.h.
(have_static_passphrase): Take care of loopback pinentry_mode.
(read_passphrase_from_fd): Ditto.
(get_static_passphrase): New.
(passphrase_to_dek_ext): Factor some code out to ...
(emit_status_need_passphrase): new.
* g10/call-agent.c (start_agent): Send the pinentry mode.
(default_inq_cb): Take care of the PASSPHRASE inquiry.  Return a
proper error code.
(agent_pksign): Add args keyid, mainkeyid and pubkey_algo.
(agent_pkdecrypt): Ditto.
* g10/pubkey-enc.c (get_it): Pass new args.
* g10/sign.c (do_sign): Pass new args.

* g10/call-agent.c (struct default_inq_parm_s): New.  Change all
similar structs to reference this one.  Change all users and inquire
callback to use this struct, instead of NULL or some undefined but not
used structs.  This change will help to eventually get rid of global
variables.
--

This new features allows to use gpg without a Pinentry.  As a
prerequisite the agent must be configured to allow the loopback
pinentry mode (option --allow-loopback-pinentry).  For example

  gpg2 --pinentry-mode=loopback FILE.gpg

may be used to decrypt FILE.gpg while entering the passphrase on the
tty.  If batch is used, --passphrase et al. may be used, if
--command-fd is used, the passphrase may be provided by another
process.  Note that there are no try-again prompts in case of a bad
passphrase.

NEWS
doc/gpg.texi
g10/call-agent.c
g10/call-agent.h
g10/gpg.c
g10/keydb.h
g10/options.h
g10/passphrase.c
g10/pubkey-enc.c
g10/sign.c

diff --git a/NEWS b/NEWS
index a8352ca..b81759c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ Noteworthy changes in version 2.1.0beta4 (unreleased)
 
  * The hash algorithm is now printed for sig records in key listings.
 
+ * New option --pinentry-mode for GPG.
+
 
 Noteworthy changes in version 2.1.0beta3 (2011-12-20)
 -----------------------------------------------------
index d679000..cf647e1 100644 (file)
@@ -2611,6 +2611,26 @@ Note that this passphrase is only used if the option @option{--batch}
 has also been given.  This is different from @command{gpg}.
 @end ifclear
 
+@ifset gpgtwoone
+@item --pinentry-mode @code{mode}
+@opindex pinentry-mode
+Set the pinentry mode to @code{mode}.  Allowed values for @code{mode}
+are:
+@table @asis
+  @item default
+  Use the default of the agent, which is @code{ask}.
+  @item ask
+  Force the use of the Pinentry.
+  @item cancel
+  Emulate use of Pinentry's cancel button.
+  @item error
+  Return a Pinentry error (``No Pinentry'').
+  @item loopback
+  Redirect Pinentry queries to the caller.  Note that in contrast to
+  Pinentry the user is not prompted again if he enters a bad password.
+@end table
+@end ifset
+
 @item --command-fd @code{n}
 @opindex command-fd
 This is a replacement for the deprecated shared-memory IPC mode.
index a4d1dbb..ed141da 100644 (file)
@@ -1,6 +1,6 @@
 /* call-agent.c - Divert GPG operations to the agent.
  * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008, 2009,
- *               2010, 2011 Free Software Foundation, Inc.
+ *               2010, 2011, 2013 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include "sysutils.h"
 #include "call-agent.h"
 #include "status.h"
+#include "../common/shareddefs.h"
 
 #ifndef DBG_ASSUAN
 # define DBG_ASSUAN 1
 #endif
 
+#define CONTROL_D ('D' - 'A' + 1)
+
+
 static assuan_context_t agent_ctx = NULL;
 static int did_early_card_test;
 
-struct cipher_parm_s
+struct default_inq_parm_s
 {
   ctrl_t ctrl;
   assuan_context_t ctx;
+  struct {
+    u32 *keyid;
+    u32 *mainkeyid;
+    int pubkey_algo;
+  } keyinfo;
+};
+
+struct cipher_parm_s
+{
+  struct default_inq_parm_s *dflt;
+  assuan_context_t ctx;
   unsigned char *ciphertext;
   size_t ciphertextlen;
 };
 
 struct writecert_parm_s
 {
-  assuan_context_t ctx;
+  struct default_inq_parm_s *dflt;
   const unsigned char *certdata;
   size_t certdatalen;
 };
 
 struct writekey_parm_s
 {
-  assuan_context_t ctx;
+  struct default_inq_parm_s *dflt;
   const unsigned char *keydata;
   size_t keydatalen;
 };
 
 struct genkey_parm_s
 {
-  ctrl_t ctrl;
-  assuan_context_t ctx;
+  struct default_inq_parm_s *dflt;
   const char *keyparms;
 };
 
 struct import_key_parm_s
 {
-  ctrl_t ctrl;
-  assuan_context_t ctx;
+  struct default_inq_parm_s *dflt;
   const void *key;
   size_t keylen;
 };
@@ -161,6 +174,19 @@ start_agent (ctrl_t ctrl, int for_card)
              here used to indirectly enable GPG_ERR_FULLY_CANCELED.  */
           assuan_transact (agent_ctx, "OPTION agent-awareness=2.1.0",
                            NULL, NULL, NULL, NULL, NULL, NULL);
+          /* Pass on the pinentry mode.  */
+          if (opt.pinentry_mode)
+            {
+              char *tmp = xasprintf ("OPTION pinentry-mode=%s",
+                                     str_pinentry_mode (opt.pinentry_mode));
+              rc = assuan_transact (agent_ctx, tmp,
+                               NULL, NULL, NULL, NULL, NULL, NULL);
+              xfree (tmp);
+              if (rc)
+                log_error ("setting pinentry mode '%s' failed: %s\n",
+                           str_pinentry_mode (opt.pinentry_mode),
+                           gpg_strerror (rc));
+            }
 
         }
     }
@@ -307,21 +333,46 @@ get_serialno_cb (void *opaque, const char *line)
 static gpg_error_t
 default_inq_cb (void *opaque, const char *line)
 {
-  gpg_error_t err;
-  ctrl_t ctrl = opaque;
+  gpg_error_t err = 0;
+  struct default_inq_parm_s *parm = opaque;
 
   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
     {
-      err = gpg_proxy_pinentry_notify (ctrl, line);
+      err = gpg_proxy_pinentry_notify (parm->ctrl, line);
       if (err)
         log_error (_("failed to proxy %s inquiry to client\n"),
                    "PINENTRY_LAUNCHED");
       /* We do not pass errors to avoid breaking other code.  */
     }
+  else if (!strncmp (line, "PASSPHRASE", 10) && (line[10]==' '||!line[10])
+           && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
+    {
+      if (have_static_passphrase ())
+        {
+          const char *s = get_static_passphrase ();
+          err = assuan_send_data (parm->ctx, s, strlen (s));
+        }
+      else
+        {
+          char *pw;
+
+          if (parm->keyinfo.keyid)
+            emit_status_need_passphrase (parm->keyinfo.keyid,
+                                         parm->keyinfo.mainkeyid,
+                                         parm->keyinfo.pubkey_algo);
+          pw = cpr_get_hidden ("passphrase.enter", _("Enter passphrase: "));
+          cpr_kill_prompt ();
+          if (*pw == CONTROL_D && !pw[1])
+            err = gpg_error (GPG_ERR_CANCELED);
+          else
+            err = assuan_send_data (parm->ctx, pw, strlen (pw));
+          xfree (pw);
+        }
+    }
   else
     log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
 
-  return 0;
+  return err;
 }
 
 
@@ -516,6 +567,9 @@ int
 agent_learn (struct agent_card_info_s *info)
 {
   int rc;
+  struct default_inq_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
 
   rc = start_agent (NULL, 1);
   if (rc)
@@ -533,10 +587,10 @@ agent_learn (struct agent_card_info_s *info)
   if (rc)
     return rc;
 
-
+  parm.ctx = agent_ctx;
   memset (info, 0, sizeof *info);
   rc = assuan_transact (agent_ctx, "SCD LEARN --force",
-                        dummy_data_cb, NULL, default_inq_cb, NULL,
+                        dummy_data_cb, NULL, default_inq_cb, &parm,
                         learn_status_cb, info);
   /* Also try to get the key attributes.  */
   if (!rc)
@@ -553,6 +607,9 @@ agent_scd_getattr (const char *name, struct agent_card_info_s *info)
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
 
   if (!*name)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -566,7 +623,8 @@ agent_scd_getattr (const char *name, struct agent_card_info_s *info)
   if (rc)
     return rc;
 
-  rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, NULL,
+  parm.ctx = agent_ctx;
+  rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm,
                         learn_status_cb, info);
 
   return rc;
@@ -584,6 +642,9 @@ agent_scd_setattr (const char *name,
   int rc;
   char line[ASSUAN_LINELENGTH];
   char *p;
+  struct default_inq_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
 
   (void)serialno;
 
@@ -615,8 +676,9 @@ agent_scd_setattr (const char *name,
   rc = start_agent (NULL, 1);
   if (!rc)
     {
+      parm.ctx = agent_ctx;
       rc = assuan_transact (agent_ctx, line, NULL, NULL,
-                            default_inq_cb, NULL, NULL, NULL);
+                            default_inq_cb, &parm, NULL, NULL);
     }
 
   status_sc_op_failure (rc);
@@ -636,10 +698,11 @@ inq_writecert_parms (void *opaque, const char *line)
 
   if (!strncmp (line, "CERTDATA", 8) && (line[8]==' '||!line[8]))
     {
-      rc = assuan_send_data (parm->ctx, parm->certdata, parm->certdatalen);
+      rc = assuan_send_data (parm->dflt->ctx,
+                             parm->certdata, parm->certdatalen);
     }
   else
-    rc = default_inq_cb (opaque, line);
+    rc = default_inq_cb (parm->dflt, line);
 
   return rc;
 }
@@ -653,6 +716,9 @@ agent_scd_writecert (const char *certidstr,
   int rc;
   char line[ASSUAN_LINELENGTH];
   struct writecert_parm_s parms;
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   rc = start_agent (NULL, 1);
   if (rc)
@@ -662,7 +728,8 @@ agent_scd_writecert (const char *certidstr,
 
   snprintf (line, DIM(line)-1, "SCD WRITECERT %s", certidstr);
   line[DIM(line)-1] = 0;
-  parms.ctx = agent_ctx;
+  dfltparm.ctx = agent_ctx;
+  parms.dflt = &dfltparm;
   parms.certdata = certdata;
   parms.certdatalen = certdatalen;
 
@@ -684,10 +751,10 @@ inq_writekey_parms (void *opaque, const char *line)
 
   if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7]))
     {
-      rc = assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen);
+      rc = assuan_send_data (parm->dflt->ctx, parm->keydata, parm->keydatalen);
     }
   else
-    rc = default_inq_cb (opaque, line);
+    rc = default_inq_cb (parm->dflt, line);
 
   return rc;
 }
@@ -701,6 +768,9 @@ agent_scd_writekey (int keyno, const char *serialno,
   int rc;
   char line[ASSUAN_LINELENGTH];
   struct writekey_parm_s parms;
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   (void)serialno;
 
@@ -712,7 +782,8 @@ agent_scd_writekey (int keyno, const char *serialno,
 
   snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno);
   line[DIM(line)-1] = 0;
-  parms.ctx = agent_ctx;
+  dfltparm.ctx = agent_ctx;
+  parms.dflt = &dfltparm;
   parms.keydata = keydata;
   parms.keydatalen = keydatalen;
 
@@ -837,6 +908,9 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
   char line[ASSUAN_LINELENGTH];
   gnupg_isotime_t tbuf;
   struct scd_genkey_parm_s parms;
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   (void)serialno;
 
@@ -858,9 +932,10 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
             keyno);
   line[DIM(line)-1] = 0;
 
+  dfltparm.ctx = agent_ctx;
   memset (info, 0, sizeof *info);
   rc = assuan_transact (agent_ctx, line,
-                        NULL, NULL, default_inq_cb, NULL,
+                        NULL, NULL, default_inq_cb, &dfltparm,
                         scd_genkey_cb, &parms);
 
   xfree (parms.savedbytes);
@@ -986,19 +1061,25 @@ agent_scd_readcert (const char *certidstr,
   char line[ASSUAN_LINELENGTH];
   membuf_t data;
   size_t len;
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   *r_buf = NULL;
   rc = start_agent (NULL, 1);
   if (rc)
     return rc;
 
+  dfltparm.ctx = agent_ctx;
+
   init_membuf (&data, 2048);
 
   snprintf (line, DIM(line)-1, "SCD READCERT %s", certidstr);
   line[DIM(line)-1] = 0;
   rc = assuan_transact (agent_ctx, line,
                         membuf_data_cb, &data,
-                        default_inq_cb, NULL, NULL, NULL);
+                        default_inq_cb, &dfltparm,
+                        NULL, NULL);
   if (rc)
     {
       xfree (get_membuf (&data, &len));
@@ -1029,6 +1110,9 @@ agent_scd_change_pin (int chvno, const char *serialno)
   int rc;
   char line[ASSUAN_LINELENGTH];
   const char *reset = "";
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   (void)serialno;
 
@@ -1039,11 +1123,14 @@ agent_scd_change_pin (int chvno, const char *serialno)
   rc = start_agent (NULL, 1);
   if (rc)
     return rc;
+  dfltparm.ctx = agent_ctx;
 
   snprintf (line, DIM(line)-1, "SCD PASSWD %s %d", reset, chvno);
   line[DIM(line)-1] = 0;
-  rc = assuan_transact (agent_ctx, line, NULL, NULL,
-                        default_inq_cb, NULL, NULL, NULL);
+  rc = assuan_transact (agent_ctx, line,
+                        NULL, NULL,
+                        default_inq_cb, &dfltparm,
+                        NULL, NULL);
   status_sc_op_failure (rc);
   return rc;
 }
@@ -1057,16 +1144,21 @@ agent_scd_checkpin  (const char *serialno)
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   rc = start_agent (NULL, 1);
   if (rc)
     return rc;
+  dfltparm.ctx = agent_ctx;
 
   snprintf (line, DIM(line)-1, "SCD CHECKPIN %s", serialno);
   line[DIM(line)-1] = 0;
   rc = assuan_transact (agent_ctx, line,
                         NULL, NULL,
-                        default_inq_cb, NULL, NULL, NULL);
+                        default_inq_cb, &dfltparm,
+                        NULL, NULL);
   status_sc_op_failure (rc);
   return rc;
 }
@@ -1102,12 +1194,16 @@ agent_get_passphrase (const char *cache_id,
   char *arg3 = NULL;
   char *arg4 = NULL;
   membuf_t data;
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   *r_passphrase = NULL;
 
   rc = start_agent (NULL, 0);
   if (rc)
     return rc;
+  dfltparm.ctx = agent_ctx;
 
   /* Check that the gpg-agent understands the repeat option.  */
   if (assuan_transact (agent_ctx,
@@ -1145,7 +1241,8 @@ agent_get_passphrase (const char *cache_id,
   init_membuf_secure (&data, 64);
   rc = assuan_transact (agent_ctx, line,
                         membuf_data_cb, &data,
-                        default_inq_cb, NULL, NULL, NULL);
+                        default_inq_cb, &dfltparm,
+                        NULL, NULL);
 
   if (rc)
     xfree (get_membuf (&data, NULL));
@@ -1172,6 +1269,9 @@ agent_clear_passphrase (const char *cache_id)
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   if (!cache_id || !*cache_id)
     return 0;
@@ -1179,11 +1279,14 @@ agent_clear_passphrase (const char *cache_id)
   rc = start_agent (NULL, 0);
   if (rc)
     return rc;
+  dfltparm.ctx = agent_ctx;
 
   snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id);
   line[DIM(line)-1] = 0;
-  return assuan_transact (agent_ctx, line, NULL, NULL,
-                          default_inq_cb, NULL, NULL, NULL);
+  return assuan_transact (agent_ctx, line,
+                          NULL, NULL,
+                          default_inq_cb, &dfltparm,
+                          NULL, NULL);
 }
 
 
@@ -1195,10 +1298,14 @@ gpg_agent_get_confirmation (const char *desc)
   int rc;
   char *tmp;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
 
   rc = start_agent (NULL, 0);
   if (rc)
     return rc;
+  dfltparm.ctx = agent_ctx;
 
   tmp = percent_plus_escape (desc);
   if (!tmp)
@@ -1207,8 +1314,10 @@ gpg_agent_get_confirmation (const char *desc)
   line[DIM(line)-1] = 0;
   xfree (tmp);
 
-  rc = assuan_transact (agent_ctx, line, NULL, NULL,
-                        default_inq_cb, NULL, NULL, NULL);
+  rc = assuan_transact (agent_ctx, line,
+                        NULL, NULL,
+                        default_inq_cb, &dfltparm,
+                        NULL, NULL);
   return rc;
 }
 
@@ -1439,11 +1548,11 @@ inq_genkey_parms (void *opaque, const char *line)
 
   if (!strncmp (line, "KEYPARAM", 8) && (line[8]==' '||!line[8]))
     {
-      err = assuan_send_data (parm->ctx,
+      err = assuan_send_data (parm->dflt->ctx,
                               parm->keyparms, strlen (parm->keyparms));
     }
   else
-    err = default_inq_cb (parm->ctrl, line);
+    err = default_inq_cb (parm->dflt, line);
 
   return err;
 }
@@ -1460,15 +1569,20 @@ agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
   gpg_error_t err;
   struct genkey_parm_s gk_parm;
   struct cache_nonce_parm_s cn_parm;
+  struct default_inq_parm_s dfltparm;
   membuf_t data;
   size_t len;
   unsigned char *buf;
   char line[ASSUAN_LINELENGTH];
 
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
+
   *r_pubkey = NULL;
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   err = assuan_transact (agent_ctx, "RESET",
                          NULL, NULL, NULL, NULL, NULL, NULL);
@@ -1476,8 +1590,7 @@ agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
     return err;
 
   init_membuf (&data, 1024);
-  gk_parm.ctrl     = ctrl;
-  gk_parm.ctx      = agent_ctx;
+  gk_parm.dflt     = &dfltparm;
   gk_parm.keyparms = keyparms;
   snprintf (line, sizeof line, "GENKEY%s%s%s",
             no_protection? " --no-protection":"",
@@ -1521,11 +1634,16 @@ agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
   size_t len;
   unsigned char *buf;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
 
   *r_pubkey = NULL;
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   err = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
   if (err)
@@ -1536,7 +1654,8 @@ agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
   init_membuf (&data, 1024);
   err = assuan_transact (agent_ctx, line,
                          membuf_data_cb, &data,
-                         default_inq_cb, NULL, NULL, NULL);
+                         default_inq_cb, &dfltparm,
+                         NULL, NULL);
   if (err)
     {
       xfree (get_membuf (&data, &len));
@@ -1565,17 +1684,26 @@ agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
 gpg_error_t
 agent_pksign (ctrl_t ctrl, const char *cache_nonce,
               const char *keygrip, const char *desc,
+              u32 *keyid, u32 *mainkeyid, int pubkey_algo,
               unsigned char *digest, size_t digestlen, int digestalgo,
               gcry_sexp_t *r_sigval)
 {
   gpg_error_t err;
   char line[ASSUAN_LINELENGTH];
   membuf_t data;
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
+  dfltparm.keyinfo.keyid       = keyid;
+  dfltparm.keyinfo.mainkeyid   = mainkeyid;
+  dfltparm.keyinfo.pubkey_algo = pubkey_algo;
 
   *r_sigval = NULL;
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   if (digestlen*2 + 50 > DIM(line))
     return gpg_error (GPG_ERR_GENERAL);
@@ -1613,8 +1741,9 @@ agent_pksign (ctrl_t ctrl, const char *cache_nonce,
             cache_nonce? " -- ":"",
             cache_nonce? cache_nonce:"");
   err = assuan_transact (agent_ctx, line,
-                        membuf_data_cb, &data, default_inq_cb, ctrl,
-                        NULL, NULL);
+                         membuf_data_cb, &data,
+                         default_inq_cb, &dfltparm,
+                         NULL, NULL);
   if (err)
     xfree (get_membuf (&data, NULL));
   else
@@ -1647,11 +1776,12 @@ inq_ciphertext_cb (void *opaque, const char *line)
   if (!strncmp (line, "CIPHERTEXT", 10) && (line[10]==' '||!line[10]))
     {
       assuan_begin_confidential (parm->ctx);
-      rc = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen);
+      rc = assuan_send_data (parm->dflt->ctx,
+                             parm->ciphertext, parm->ciphertextlen);
       assuan_end_confidential (parm->ctx);
     }
   else
-    rc = default_inq_cb (parm->ctrl, line);
+    rc = default_inq_cb (parm->dflt, line);
 
   return rc;
 }
@@ -1660,9 +1790,12 @@ inq_ciphertext_cb (void *opaque, const char *line)
 /* Call the agent to do a decrypt operation using the key identified
    by the hex string KEYGRIP and the input data S_CIPHERTEXT.  On the
    success the decoded value is stored verbatim at R_BUF and its
-   length at R_BUF; the callers needs to release it.  */
+   length at R_BUF; the callers needs to release it.  KEYID, MAINKEYID
+   and PUBKEY_ALGO are used to construct additional promots or status
+   messages. */
 gpg_error_t
 agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
+                 u32 *keyid, u32 *mainkeyid, int pubkey_algo,
                  gcry_sexp_t s_ciphertext,
                  unsigned char **r_buf, size_t *r_buflen)
 {
@@ -1671,6 +1804,13 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
   membuf_t data;
   size_t n, len;
   char *p, *buf, *endp;
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
+  dfltparm.keyinfo.keyid       = keyid;
+  dfltparm.keyinfo.mainkeyid   = mainkeyid;
+  dfltparm.keyinfo.pubkey_algo = pubkey_algo;
 
   if (!keygrip || strlen(keygrip) != 40 || !s_ciphertext || !r_buf || !r_buflen)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -1679,6 +1819,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   err = assuan_transact (agent_ctx, "RESET",
                          NULL, NULL, NULL, NULL, NULL, NULL);
@@ -1704,7 +1845,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
   {
     struct cipher_parm_s parm;
 
-    parm.ctrl = ctrl;
+    parm.dflt = &dfltparm;
     parm.ctx = agent_ctx;
     err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen);
     if (err)
@@ -1773,11 +1914,16 @@ agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen)
   size_t len;
   unsigned char *buf;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
 
   *r_kek = NULL;
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s",
             forexport? "--export":"--import");
@@ -1785,7 +1931,8 @@ agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen)
   init_membuf_secure (&data, 64);
   err = assuan_transact (agent_ctx, line,
                          membuf_data_cb, &data,
-                         default_inq_cb, ctrl, NULL, NULL);
+                         default_inq_cb, &dfltparm,
+                         NULL, NULL);
   if (err)
     {
       xfree (get_membuf (&data, &len));
@@ -1810,10 +1957,10 @@ inq_import_key_parms (void *opaque, const char *line)
 
   if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7]))
     {
-      err = assuan_send_data (parm->ctx, parm->key, parm->keylen);
+      err = assuan_send_data (parm->dflt->ctx, parm->key, parm->keylen);
     }
   else
-    err = default_inq_cb (parm->ctrl, line);
+    err = default_inq_cb (parm->dflt, line);
 
   return err;
 }
@@ -1828,10 +1975,15 @@ agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
   struct import_key_parm_s parm;
   struct cache_nonce_parm_s cn_parm;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
 
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   if (desc)
     {
@@ -1843,8 +1995,7 @@ agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
         return err;
     }
 
-  parm.ctrl   = ctrl;
-  parm.ctx    = agent_ctx;
+  parm.dflt   = &dfltparm;
   parm.key    = key;
   parm.keylen = keylen;
 
@@ -1854,7 +2005,8 @@ agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
   cn_parm.cache_nonce_addr = cache_nonce_addr;
   cn_parm.passwd_nonce_addr = NULL;
   err = assuan_transact (agent_ctx, line,
-                         NULL, NULL, inq_import_key_parms, &parm,
+                         NULL, NULL,
+                         inq_import_key_parms, &parm,
                          cache_nonce_status_cb, &cn_parm);
   return err;
 }
@@ -1876,12 +2028,17 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
   size_t len;
   unsigned char *buf;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
 
   *r_result = NULL;
 
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   if (desc)
     {
@@ -1902,7 +2059,7 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
   cn_parm.passwd_nonce_addr = NULL;
   err = assuan_transact (agent_ctx, line,
                          membuf_data_cb, &data,
-                         default_inq_cb, ctrl,
+                         default_inq_cb, &dfltparm,
                          cache_nonce_status_cb, &cn_parm);
   if (err)
     {
@@ -1932,14 +2089,20 @@ agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
   gpg_error_t err;
   struct cache_nonce_parm_s cn_parm;
   char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
 
   err = start_agent (ctrl, 0);
   if (err)
     return err;
+  dfltparm.ctx = agent_ctx;
 
   if (!hexkeygrip || strlen (hexkeygrip) != 40)
     return gpg_error (GPG_ERR_INV_VALUE);
 
+
   if (desc)
     {
       snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
@@ -1958,7 +2121,7 @@ agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
   cn_parm.cache_nonce_addr = cache_nonce_addr;
   cn_parm.passwd_nonce_addr = passwd_nonce_addr;
   err = assuan_transact (agent_ctx, line, NULL, NULL,
-                         default_inq_cb, ctrl,
+                         default_inq_cb, &dfltparm,
                          cache_nonce_status_cb, &cn_parm);
   return err;
 }
index 43de14f..de05d7a 100644 (file)
@@ -155,12 +155,14 @@ gpg_error_t agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
 /* Create a signature.  */
 gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce,
                           const char *hexkeygrip, const char *desc,
+                          u32 *keyid, u32 *mainkeyid, int pubkey_algo,
                           unsigned char *digest, size_t digestlen,
                           int digestalgo,
                           gcry_sexp_t *r_sigval);
 
 /* Decrypt a ciphertext.  */
 gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
+                             u32 *keyid, u32 *mainkeyid, int pubkey_algo,
                              gcry_sexp_t s_ciphertext,
                              unsigned char **r_buf, size_t *r_buflen);
 
index b614a94..9adc21a 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -56,6 +56,7 @@
 #include "asshelp.h"
 #include "call-dirmngr.h"
 #include "../common/init.h"
+#include "../common/shareddefs.h"
 
 #if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
 #define MY_O_BINARY  O_BINARY
@@ -217,6 +218,7 @@ enum cmd_and_opt_values
     oPassphraseFD,
     oPassphraseFile,
     oPassphraseRepeat,
+    oPinentryMode,
     oCommandFD,
     oCommandFile,
     oQuickRandom,
@@ -611,6 +613,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_i (oPassphraseFD,    "passphrase-fd", "@"),
   ARGPARSE_s_s (oPassphraseFile,  "passphrase-file", "@"),
   ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"),
+  ARGPARSE_s_s (oPinentryMode,    "pinentry-mode", "@"),
   ARGPARSE_s_i (oCommandFD, "command-fd", "@"),
   ARGPARSE_s_s (oCommandFile, "command-file", "@"),
   ARGPARSE_s_n (oQuickRandom, "debug-quick-random", "@"),
@@ -2593,7 +2596,16 @@ main (int argc, char **argv)
          case oPassphraseFile:
             pwfd = open_info_file (pargs.r.ret_str, 0, 1);
             break;
-         case oPassphraseRepeat: opt.passphrase_repeat=pargs.r.ret_int; break;
+         case oPassphraseRepeat:
+            opt.passphrase_repeat = pargs.r.ret_int;
+            break;
+
+          case oPinentryMode:
+           opt.pinentry_mode = parse_pinentry_mode (pargs.r.ret_str);
+           if (opt.pinentry_mode == -1)
+              log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str);
+           break;
+
          case oCommandFD:
             opt.command_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
             break;
index 22c2b67..fd80c25 100644 (file)
@@ -181,6 +181,7 @@ unsigned char encode_s2k_iterations (int iterations);
 assuan_context_t agent_open (int try, const char *orig_codeset);
 void agent_close (assuan_context_t ctx);
 int  have_static_passphrase(void);
+const char *get_static_passphrase (void);
 void set_passphrase_from_string(const char *pass);
 void read_passphrase_from_fd( int fd );
 void passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo );
@@ -196,6 +197,8 @@ void set_next_passphrase( const char *s );
 char *get_last_passphrase(void);
 void next_to_last_passphrase(void);
 
+void emit_status_need_passphrase (u32 *keyid, u32 *mainkeyid, int pubkey_algo);
+
 char *gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped);
 
 
index e67d0ce..223b115 100644 (file)
@@ -242,6 +242,7 @@ struct
   } *auto_key_locate;
 
   int passphrase_repeat;
+  int pinentry_mode;
 } opt;
 
 /* CTRL is used to keep some global variables we currently can't
index cc56555..d872e36 100644 (file)
@@ -43,7 +43,7 @@
 #include "i18n.h"
 #include "status.h"
 #include "call-agent.h"
-
+#include "../common/shareddefs.h"
 
 static char *fd_passwd = NULL;
 static char *next_pw = NULL;
@@ -104,9 +104,21 @@ encode_s2k_iterations (int iterations)
 int
 have_static_passphrase()
 {
-  return !!fd_passwd && opt.batch;
+  return (!!fd_passwd
+          && (opt.batch || opt.pinentry_mode == PINENTRY_MODE_LOOPBACK));
+}
+
+/* Return a static passphrase.  The returned value is only valid as
+   long as no other passphrase related function is called.  NULL may
+   be returned if no passphrase has been set; better use
+   have_static_passphrase first.  */
+const char *
+get_static_passphrase (void)
+{
+  return fd_passwd;
 }
 
+
 /****************
  * Set the passphrase to be used for the next query and only for the next
  * one.
@@ -156,7 +168,7 @@ read_passphrase_from_fd( int fd )
   int i, len;
   char *pw;
 
-  if ( !opt.batch )
+  if ( !opt.batch && opt.pinentry_mode != PINENTRY_MODE_LOOPBACK)
     { /* Not used but we have to do a dummy read, so that it won't end
          up at the begin of the message if the quite usual trick to
          prepend the passphtrase to the message is used. */
@@ -187,7 +199,7 @@ read_passphrase_from_fd( int fd )
         break;
     }
   pw[i] = 0;
-  if (!opt.batch)
+  if (!opt.batch && opt.pinentry_mode != PINENTRY_MODE_LOOPBACK)
     tty_printf("\b\b\b   \n" );
 
   xfree ( fd_passwd );
@@ -458,30 +470,9 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
 
       if ( keyid )
         {
-          u32 used_kid[2];
-          char *us;
-
-          if ( keyid[2] && keyid[3] )
-            {
-              used_kid[0] = keyid[2];
-              used_kid[1] = keyid[3];
-            }
-          else
-            {
-              used_kid[0] = keyid[0];
-              used_kid[1] = keyid[1];
-            }
-
-          us = get_long_user_id_string ( keyid );
-          write_status_text ( STATUS_USERID_HINT, us );
-          xfree(us);
-
-          snprintf (buf, sizeof buf -1, "%08lX%08lX %08lX%08lX %d 0",
-                    (ulong)keyid[0], (ulong)keyid[1],
-                    (ulong)used_kid[0], (ulong)used_kid[1],
-                    pubkey_algo );
-
-          write_status_text ( STATUS_NEED_PASSPHRASE, buf );
+          emit_status_need_passphrase (keyid,
+                                       keyid[2] && keyid[3]? keyid+2:NULL,
+                                       pubkey_algo);
        }
       else
         {
@@ -614,6 +605,29 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
 }
 
 
+/* Emit the USERID_HINT and the NEED_PASSPHRASE status messages.
+   MAINKEYID may be NULL. */
+void
+emit_status_need_passphrase (u32 *keyid, u32 *mainkeyid, int pubkey_algo)
+{
+  char buf[50];
+  char *us;
+
+  us = get_long_user_id_string (keyid);
+  write_status_text (STATUS_USERID_HINT, us);
+  xfree (us);
+
+  snprintf (buf, sizeof buf -1, "%08lX%08lX %08lX%08lX %d 0",
+            (ulong)keyid[0],
+            (ulong)keyid[1],
+            (ulong)(mainkeyid? mainkeyid[0]:keyid[0]),
+            (ulong)(mainkeyid? mainkeyid[1]:keyid[1]),
+            pubkey_algo);
+
+  write_status_text (STATUS_NEED_PASSPHRASE, buf);
+}
+
+
 /* Return an allocated utf-8 string describing the key PK.  If ESCAPED
    is true spaces and control characters are percent or plus escaped.
    MODE 0 is for the common prompt, MODE 1 for the import prompt. */
index 254e810..a98a723 100644 (file)
@@ -193,7 +193,9 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
 
   /* Decrypt. */
   desc = gpg_format_keydesc (sk, 0, 1);
-  err = agent_pkdecrypt (NULL, keygrip, desc, s_data, &frame, &nframe);
+  err = agent_pkdecrypt (NULL, keygrip,
+                         desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
+                         s_data, &frame, &nframe);
   xfree (desc);
   gcry_sexp_release (s_data);
   if (err)
index 501f1ff..8944067 100644 (file)
@@ -272,6 +272,7 @@ do_sign (PKT_public_key *pksk, PKT_signature *sig,
 
       desc = gpg_format_keydesc (pksk, 0, 1);
       err = agent_pksign (NULL/*ctrl*/, cache_nonce, hexgrip, desc,
+                          pksk->keyid, pksk->main_keyid, pksk->pubkey_algo,
                           dp, gcry_md_get_algo_dlen (mdalgo), mdalgo,
                           &s_sigval);
       xfree (desc);