Add Kludge for RegTP sillyness.
[gnupg.git] / sm / call-agent.c
index 4d26e34..9942672 100644 (file)
@@ -1,5 +1,5 @@
 /* call-agent.c - divert operations to the agent
- *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <gcrypt.h>
 #include <assuan.h>
 #include "i18n.h"
+#include "asshelp.h"
 #include "keydb.h" /* fixme: Move this to import.c */
 #include "../common/membuf.h"
 
 
-static ASSUAN_CONTEXT agent_ctx = NULL;
+static assuan_context_t agent_ctx = NULL;
 static int force_pipe_server = 0;
 
-struct cipher_parm_s {
-  ASSUAN_CONTEXT ctx;
-  const char *ciphertext;
+struct cipher_parm_s
+{
+  assuan_context_t ctx;
+  const unsigned char *ciphertext;
   size_t ciphertextlen;
 };
 
-struct genkey_parm_s {
-  ASSUAN_CONTEXT ctx;
-  const char *sexp;
+struct genkey_parm_s
+{
+  assuan_context_t ctx;
+  const unsigned char *sexp;
   size_t sexplen;
 };
 
-struct learn_parm_s {
+struct learn_parm_s
+{
   int error;
-  ASSUAN_CONTEXT ctx;
+  assuan_context_t ctx;
   membuf_t *data;
 };
 
@@ -64,60 +68,69 @@ struct learn_parm_s {
 /* Try to connect to the agent via socket or fork it off and work by
    pipes.  Handle the server's initial greeting */
 static int
-start_agent (void)
+start_agent (ctrl_t ctrl)
 {
   int rc = 0;
   char *infostr, *p;
-  ASSUAN_CONTEXT ctx;
-  char *dft_display = NULL;
-  char *dft_ttyname = NULL;
-  char *dft_ttytype = NULL;
-  char *old_lc = NULL;
-  char *dft_lc = NULL;
+  assuan_context_t ctx;
 
   if (agent_ctx)
     return 0; /* fixme: We need a context for each thread or serialize
                  the access to the agent (which is suitable given that
-                 the agent is not MT */
+                 the agent is not MT. */
 
   infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
-  if (!infostr)
+  if (!infostr || !*infostr)
     {
       const char *pgmname;
       const char *argv[3];
+      char *sockname;
       int no_close_list[3];
       int i;
 
-      if (opt.verbose)
-        log_info (_("no running gpg-agent - starting one\n"));
+      /* First check whether we can connect at the standard
+         socket.  */
+      sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
+      rc = assuan_socket_connect (&ctx, sockname, 0);
+      xfree (sockname);
 
-      if (fflush (NULL))
+      if (rc)
         {
-          gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
-          log_error ("error flushing pending output: %s\n", strerror (errno));
-          return tmperr;
+          /* With no success start a new server.  */
+          if (opt.verbose)
+            log_info (_("no running gpg-agent - starting one\n"));
+          
+          gpgsm_status (ctrl, STATUS_PROGRESS, "starting_agent ? 0 0");
+          
+          if (fflush (NULL))
+            {
+              gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
+              log_error ("error flushing pending output: %s\n",
+                         strerror (errno));
+              return tmperr;
+            }
+          
+          if (!opt.agent_program || !*opt.agent_program)
+            opt.agent_program = GNUPG_DEFAULT_AGENT;
+          if ( !(pgmname = strrchr (opt.agent_program, '/')))
+            pgmname = opt.agent_program;
+          else
+            pgmname++;
+
+          argv[0] = pgmname;
+          argv[1] = "--server";
+          argv[2] = NULL;
+
+          i=0;
+          if (log_get_fd () != -1)
+            no_close_list[i++] = log_get_fd ();
+          no_close_list[i++] = fileno (stderr);
+          no_close_list[i] = -1;
+
+          /* Connect to the agent and perform initial handshaking. */
+          rc = assuan_pipe_connect (&ctx, opt.agent_program, (char**)argv,
+                                    no_close_list);
         }
-
-      if (!opt.agent_program || !*opt.agent_program)
-        opt.agent_program = GNUPG_DEFAULT_AGENT;
-      if ( !(pgmname = strrchr (opt.agent_program, '/')))
-        pgmname = opt.agent_program;
-      else
-        pgmname++;
-
-      argv[0] = pgmname;
-      argv[1] = "--server";
-      argv[2] = NULL;
-
-      i=0;
-      if (log_get_fd () != -1)
-        no_close_list[i++] = log_get_fd ();
-      no_close_list[i++] = fileno (stderr);
-      no_close_list[i] = -1;
-
-      /* connect to the agent and perform initial handshaking */
-      rc = assuan_pipe_connect (&ctx, opt.agent_program, (char**)argv,
-                                no_close_list);
     }
   else
     {
@@ -125,16 +138,16 @@ start_agent (void)
       int pid;
 
       infostr = xstrdup (infostr);
-      if ( !(p = strchr (infostr, ':')) || p == infostr)
+      if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
         {
           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
           xfree (infostr);
           force_pipe_server = 1;
-          return start_agent ();
+          return start_agent (ctrl);
         }
       *p++ = 0;
       pid = atoi (p);
-      while (*p && *p != ':')
+      while (*p && *p != PATHSEP_C)
         p++;
       prot = *p? atoi (p+1) : 0;
       if (prot != 1)
@@ -143,7 +156,7 @@ start_agent (void)
                      prot);
           xfree (infostr);
           force_pipe_server = 1;
-          return start_agent ();
+          return start_agent (ctrl);
         }
 
       rc = assuan_socket_connect (&ctx, infostr, pid);
@@ -152,7 +165,7 @@ start_agent (void)
         {
           log_error (_("can't connect to the agent - trying fall back\n"));
           force_pipe_server = 1;
-          return start_agent ();
+          return start_agent (ctrl);
         }
     }
 
@@ -170,118 +183,9 @@ start_agent (void)
   if (rc)
     return map_assuan_err (rc);
 
-  dft_display = getenv ("DISPLAY");
-  if (opt.display || dft_display)
-    {
-      char *optstr;
-      if (asprintf (&optstr, "OPTION display=%s",
-                   opt.display ? opt.display : dft_display) < 0)
-       return OUT_OF_CORE (errno);
-      rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
-                           NULL);
-      free (optstr);
-      if (rc)
-       return map_assuan_err (rc);
-    }
-  if (!opt.ttyname)
-    {
-      dft_ttyname = getenv ("GPG_TTY");
-      if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
-        dft_ttyname = ttyname (0);
-    }
-  if (opt.ttyname || dft_ttyname)
-    {
-      char *optstr;
-      if (asprintf (&optstr, "OPTION ttyname=%s",
-                   opt.ttyname ? opt.ttyname : dft_ttyname) < 0)
-       return OUT_OF_CORE (errno);
-      rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
-                           NULL);
-      free (optstr);
-      if (rc)
-       return map_assuan_err (rc);
-    }
-  dft_ttytype = getenv ("TERM");
-  if (opt.ttytype || (dft_ttyname && dft_ttytype))
-    {
-      char *optstr;
-      if (asprintf (&optstr, "OPTION ttytype=%s",
-                   opt.ttyname ? opt.ttytype : dft_ttytype) < 0)
-       return OUT_OF_CORE (errno);
-      rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
-                           NULL);
-      free (optstr);
-      if (rc)
-       return map_assuan_err (rc);
-    }
-#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
-  old_lc = setlocale (LC_CTYPE, NULL);
-  if (old_lc)
-    {
-      old_lc = strdup (old_lc);
-      if (!old_lc)
-        return OUT_OF_CORE (errno);
-    }
-  dft_lc = setlocale (LC_CTYPE, "");
-#endif
-  if (opt.lc_ctype || (dft_ttyname && dft_lc))
-    {
-      char *optstr;
-      if (asprintf (&optstr, "OPTION lc-ctype=%s",
-                   opt.lc_ctype ? opt.lc_ctype : dft_lc) < 0)
-       rc = OUT_OF_CORE (errno);
-      else
-       {
-         rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
-                               NULL);
-         free (optstr);
-         if (rc)
-           rc = map_assuan_err (rc);
-       }
-    }
-#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
-  if (old_lc)
-    {
-      setlocale (LC_CTYPE, old_lc);
-      free (old_lc);
-    }
-#endif
-  if (rc)
-    return rc;
-#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
-  old_lc = setlocale (LC_MESSAGES, NULL);
-  if (old_lc)
-    {
-      old_lc = strdup (old_lc);
-      if (!old_lc)
-        return OUT_OF_CORE (errno);
-    }
-  dft_lc = setlocale (LC_MESSAGES, "");
-#endif
-  if (opt.lc_messages || (dft_ttyname && dft_lc))
-    {
-      char *optstr;
-      if (asprintf (&optstr, "OPTION lc-messages=%s",
-                   opt.lc_messages ? opt.lc_messages : dft_lc) < 0)
-       rc = OUT_OF_CORE (errno);
-      else
-       {
-         rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
-                               NULL);
-         free (optstr);
-         if (rc)
-           rc = map_assuan_err (rc);
-       }
-    }
-#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
-  if (old_lc)
-    {
-      setlocale (LC_MESSAGES, old_lc);
-      free (old_lc);
-    }
-#endif
-
-  return rc;
+  return send_pinentry_environment (agent_ctx, GPG_ERR_SOURCE_DEFAULT,
+                                    opt.display, opt.ttyname, opt.ttytype,
+                                    opt.lc_ctype, opt.lc_messages);
 }
 
 
@@ -301,9 +205,9 @@ membuf_data_cb (void *opaque, const void *buffer, size_t length)
 /* Call the agent to do a sign operation using the key identified by
    the hex string KEYGRIP. */
 int
-gpgsm_agent_pksign (const char *keygrip,
+gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
                     unsigned char *digest, size_t digestlen, int digestalgo,
-                    char **r_buf, size_t *r_buflen )
+                    unsigned char **r_buf, size_t *r_buflen )
 {
   int rc, i;
   char *p, line[ASSUAN_LINELENGTH];
@@ -311,7 +215,7 @@ gpgsm_agent_pksign (const char *keygrip,
   size_t len;
 
   *r_buf = NULL;
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
@@ -328,6 +232,16 @@ gpgsm_agent_pksign (const char *keygrip,
   if (rc)
     return map_assuan_err (rc);
 
+  if (desc)
+    {
+      snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+      line[DIM(line)-1] = 0;
+      rc = assuan_transact (agent_ctx, line,
+                            NULL, NULL, NULL, NULL, NULL, NULL);
+      if (rc)
+        return map_assuan_err (rc);
+    }
+
   sprintf (line, "SETHASH %d ", digestalgo);
   p = line + strlen (line);
   for (i=0; i < digestlen ; i++, p += 2 )
@@ -376,8 +290,8 @@ inq_ciphertext_cb (void *opaque, const char *keyword)
 /* Call the agent to do a decrypt operation using the key identified by
    the hex string KEYGRIP. */
 int
-gpgsm_agent_pkdecrypt (const char *keygrip,
-                       KsbaConstSexp ciphertext, 
+gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
+                       ksba_const_sexp_t ciphertext, 
                        char **r_buf, size_t *r_buflen )
 {
   int rc;
@@ -396,7 +310,7 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
   if (!ciphertextlen)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
@@ -411,6 +325,16 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
   if (rc)
     return map_assuan_err (rc);
 
+  if (desc)
+    {
+      snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+      line[DIM(line)-1] = 0;
+      rc = assuan_transact (agent_ctx, line,
+                            NULL, NULL, NULL, NULL, NULL, NULL);
+      if (rc)
+        return map_assuan_err (rc);
+    }
+
   init_membuf (&data, 1024);
   cipher_parm.ctx = agent_ctx;
   cipher_parm.ciphertext = ciphertext;
@@ -464,16 +388,17 @@ inq_genkey_parms (void *opaque, const char *keyword)
 \f
 /* Call the agent to generate a newkey */
 int
-gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
+gpgsm_agent_genkey (ctrl_t ctrl,
+                    ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey)
 {
   int rc;
   struct genkey_parm_s gk_parm;
   membuf_t data;
   size_t len;
-  char *buf;
+  unsigned char *buf;
 
   *r_pubkey = NULL;
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
@@ -508,16 +433,61 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
 }
 
 \f
+/* Call the agent to read the public key part for a given keygrip.  */
+int
+gpgsm_agent_readkey (ctrl_t ctrl, const char *hexkeygrip,
+                     ksba_sexp_t *r_pubkey)
+{
+  int rc;
+  membuf_t data;
+  size_t len;
+  unsigned char *buf;
+  char line[ASSUAN_LINELENGTH];
+
+  *r_pubkey = NULL;
+  rc = start_agent (ctrl);
+  if (rc)
+    return rc;
+
+  rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
+  if (rc)
+    return map_assuan_err (rc);
+
+  snprintf (line, DIM(line)-1, "READKEY %s", hexkeygrip);
+  line[DIM(line)-1] = 0;
+
+  init_membuf (&data, 1024);
+  rc = assuan_transact (agent_ctx, line,
+                        membuf_data_cb, &data, 
+                        NULL, NULL, NULL, NULL);
+  if (rc)
+    {
+      xfree (get_membuf (&data, &len));
+      return map_assuan_err (rc);
+    }
+  buf = get_membuf (&data, &len);
+  if (!buf)
+    return gpg_error (GPG_ERR_ENOMEM);
+  if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
+    {
+      xfree (buf);
+      return gpg_error (GPG_ERR_INV_SEXP);
+    }
+  *r_pubkey = buf;
+  return 0;
+}
+
+\f
 /* Ask the agent whether the certificate is in the list of trusted
    keys */
 int
-gpgsm_agent_istrusted (KsbaCert cert)
+gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert)
 {
   int rc;
   char *fpr;
   char line[ASSUAN_LINELENGTH];
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
@@ -538,13 +508,13 @@ gpgsm_agent_istrusted (KsbaCert cert)
 
 /* Ask the agent to mark CERT as a trusted Root-CA one */
 int
-gpgsm_agent_marktrusted (KsbaCert cert)
+gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert)
 {
   int rc;
   char *fpr, *dn;
   char line[ASSUAN_LINELENGTH];
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
@@ -575,12 +545,12 @@ gpgsm_agent_marktrusted (KsbaCert cert)
 /* Ask the agent whether the a corresponding secret key is available
    for the given keygrip */
 int
-gpgsm_agent_havekey (const char *hexkeygrip)
+gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip)
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
@@ -601,7 +571,7 @@ learn_cb (void *opaque, const void *buffer, size_t length)
   struct learn_parm_s *parm = opaque;
   size_t len;
   char *buf;
-  KsbaCert cert;
+  ksba_cert_t cert;
   int rc;
 
   if (parm->error)
@@ -622,18 +592,18 @@ learn_cb (void *opaque, const void *buffer, size_t length)
 
 
   /* FIXME: this should go into import.c */
-  cert = ksba_cert_new ();
-  if (!cert)
+  rc = ksba_cert_new (&cert);
+  if (rc)
     {
-      parm->error = gpg_error (GPG_ERR_ENOMEM);
+      parm->error = rc;
       return 0;
     }
   rc = ksba_cert_init_from_mem (cert, buf, len);
   if (rc)
     {
-      log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc));
+      log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc));
       ksba_cert_release (cert);
-      parm->error = map_ksba_err (rc);
+      parm->error = rc;
       return 0;
     }
 
@@ -665,14 +635,14 @@ learn_cb (void *opaque, const void *buffer, size_t length)
   
 /* Call the agent to learn about a smartcard */
 int
-gpgsm_agent_learn ()
+gpgsm_agent_learn (ctrl_t ctrl)
 {
   int rc;
   struct learn_parm_s learn_parm;
   membuf_t data;
   size_t len;
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
@@ -690,20 +660,32 @@ gpgsm_agent_learn ()
 }
 
 \f
-/* Ask the agent to change the passphrase of the key identified by HEXKEYGRIP. */
+/* Ask the agent to change the passphrase of the key identified by
+   HEXKEYGRIP. If DESC is not NULL, display instead of the default
+   description message. */
 int
-gpgsm_agent_passwd (const char *hexkeygrip)
+gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (!hexkeygrip || strlen (hexkeygrip) != 40)
     return gpg_error (GPG_ERR_INV_VALUE);
 
+  if (desc)
+    {
+      snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+      line[DIM(line)-1] = 0;
+      rc = assuan_transact (agent_ctx, line,
+                            NULL, NULL, NULL, NULL, NULL, NULL);
+      if (rc)
+        return map_assuan_err (rc);
+    }
+
   snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip);
   line[DIM(line)-1] = 0;
 
@@ -711,3 +693,23 @@ gpgsm_agent_passwd (const char *hexkeygrip)
   return map_assuan_err (rc);
 }
 
+
+\f
+/* Ask the agent to pop up a confirmation dialog with the text DESC
+   and an okay and cancel button.  */
+gpg_error_t
+gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+
+  rc = start_agent (ctrl);
+  if (rc)
+    return rc;
+
+  snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", desc);
+  line[DIM(line)-1] = 0;
+
+  rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+  return map_assuan_err (rc);
+}