* gpgsm.c: New command --keydb-clear-some-cert-flags.
[gnupg.git] / sm / call-agent.c
index 1982b22..2e8c754 100644 (file)
@@ -1,5 +1,5 @@
 /* call-agent.c - divert operations to the agent
 /* call-agent.c - divert operations to the agent
- *     Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  *
  * This file is part of GnuPG.
  *
 #include <unistd.h> 
 #include <time.h>
 #include <assert.h>
 #include <unistd.h> 
 #include <time.h>
 #include <assert.h>
-
-#include <gcrypt.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
 
 #include "gpgsm.h"
 
 #include "gpgsm.h"
-#include "../assuan/assuan.h"
+#include <gcrypt.h>
+#include <assuan.h>
 #include "i18n.h"
 #include "i18n.h"
+#include "asshelp.h"
 #include "keydb.h" /* fixme: Move this to import.c */
 #include "keydb.h" /* fixme: Move this to import.c */
+#include "../common/membuf.h"
+
 
 static ASSUAN_CONTEXT agent_ctx = NULL;
 static int force_pipe_server = 0;
 
 static ASSUAN_CONTEXT agent_ctx = NULL;
 static int force_pipe_server = 0;
@@ -52,109 +57,47 @@ struct genkey_parm_s {
 struct learn_parm_s {
   int error;
   ASSUAN_CONTEXT ctx;
 struct learn_parm_s {
   int error;
   ASSUAN_CONTEXT ctx;
-  struct membuf *data;
+  membuf_t *data;
 };
 
 };
 
-struct membuf {
-  size_t len;
-  size_t size;
-  char *buf;
-  int out_of_core;
-};
-
-
-\f
-/* A simple implemnation of a dynamic buffer.  Use init_membuf() to
-   create a buffer, put_membuf to append bytes and get_membuf to
-   release and return the buffer.  Allocation errors are detected but
-   only returned at the final get_membuf(), this helps not to clutter
-   the code with out of core checks.  */
-
-static void
-init_membuf (struct membuf *mb, int initiallen)
-{
-  mb->len = 0;
-  mb->size = initiallen;
-  mb->out_of_core = 0;
-  mb->buf = xtrymalloc (initiallen);
-  if (!mb->buf)
-      mb->out_of_core = 1;
-}
-
-static void
-put_membuf (struct membuf *mb, const void *buf, size_t len)
-{
-  if (mb->out_of_core)
-    return;
-
-  if (mb->len + len >= mb->size)
-    {
-      char *p;
-      
-      mb->size += len + 1024;
-      p = xtryrealloc (mb->buf, mb->size);
-      if (!p)
-        {
-          mb->out_of_core = 1;
-          return;
-        }
-      mb->buf = p;
-    }
-  memcpy (mb->buf + mb->len, buf, len);
-  mb->len += len;
-}
-
-static void *
-get_membuf (struct membuf *mb, size_t *len)
-{
-  char *p;
-
-  if (mb->out_of_core)
-    {
-      xfree (mb->buf);
-      mb->buf = NULL;
-      return NULL;
-    }
-
-  p = mb->buf;
-  *len = mb->len;
-  mb->buf = NULL;
-  mb->out_of_core = 1; /* don't allow a reuse */
-  return p;
-}
-
 
 \f
 /* Try to connect to the agent via socket or fork it off and work by
    pipes.  Handle the server's initial greeting */
 static int
 
 \f
 /* 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;
+  int rc = 0;
   char *infostr, *p;
   char *infostr, *p;
-  ASSUAN_CONTEXT ctx;
+  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
 
   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");
 
   infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
-  if (!infostr)
+  if (!infostr || !*infostr)
     {
       const char *pgmname;
       const char *argv[3];
     {
       const char *pgmname;
       const char *argv[3];
+      int no_close_list[3];
+      int i;
 
 
-      log_info (_("no running gpg-agent - starting one\n"));
+      if (opt.verbose)
+        log_info (_("no running gpg-agent - starting one\n"));
       
       
+      gpgsm_status (ctrl, STATUS_PROGRESS, "starting_agent ? 0 0");
+
       if (fflush (NULL))
         {
       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));
           log_error ("error flushing pending output: %s\n", strerror (errno));
-          return seterr (Write_Error);
+          return tmperr;
         }
 
       if (!opt.agent_program || !*opt.agent_program)
         }
 
       if (!opt.agent_program || !*opt.agent_program)
-        opt.agent_program = "../agent/gpg-agent";
+        opt.agent_program = GNUPG_DEFAULT_AGENT;
       if ( !(pgmname = strrchr (opt.agent_program, '/')))
         pgmname = opt.agent_program;
       else
       if ( !(pgmname = strrchr (opt.agent_program, '/')))
         pgmname = opt.agent_program;
       else
@@ -164,8 +107,15 @@ start_agent (void)
       argv[1] = "--server";
       argv[2] = NULL;
 
       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 */
       /* connect to the agent and perform initial handshaking */
-      rc = assuan_pipe_connect (&ctx, opt.agent_program, (char**)argv, 0);
+      rc = assuan_pipe_connect (&ctx, opt.agent_program, (char**)argv,
+                                no_close_list);
     }
   else
     {
     }
   else
     {
@@ -178,7 +128,7 @@ start_agent (void)
           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
           xfree (infostr);
           force_pipe_server = 1;
           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);
         }
       *p++ = 0;
       pid = atoi (p);
@@ -191,7 +141,7 @@ start_agent (void)
                      prot);
           xfree (infostr);
           force_pipe_server = 1;
                      prot);
           xfree (infostr);
           force_pipe_server = 1;
-          return start_agent ();
+          return start_agent (ctrl);
         }
 
       rc = assuan_socket_connect (&ctx, infostr, pid);
         }
 
       rc = assuan_socket_connect (&ctx, infostr, pid);
@@ -200,28 +150,34 @@ start_agent (void)
         {
           log_error (_("can't connect to the agent - trying fall back\n"));
           force_pipe_server = 1;
         {
           log_error (_("can't connect to the agent - trying fall back\n"));
           force_pipe_server = 1;
-          return start_agent ();
+          return start_agent (ctrl);
         }
     }
 
         }
     }
 
-
   if (rc)
     {
       log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
   if (rc)
     {
       log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
-      return seterr (No_Agent);
+      return gpg_error (GPG_ERR_NO_AGENT);
     }
   agent_ctx = ctx;
 
     }
   agent_ctx = ctx;
 
-  if (DBG_AGENT)
+  if (DBG_ASSUAN)
     log_debug ("connection to agent established\n");
     log_debug ("connection to agent established\n");
-  return 0;
+
+  rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+  if (rc)
+    return map_assuan_err (rc);
+
+  return send_pinentry_environment (agent_ctx,
+                                    opt.display, opt.ttyname, opt.ttytype,
+                                    opt.lc_ctype, opt.lc_messages);
 }
 
 
 static AssuanError
 membuf_data_cb (void *opaque, const void *buffer, size_t length)
 {
 }
 
 
 static AssuanError
 membuf_data_cb (void *opaque, const void *buffer, size_t length)
 {
-  struct membuf *data = opaque;
+  membuf_t *data = opaque;
 
   if (buffer)
     put_membuf (data, buffer, length);
 
   if (buffer)
     put_membuf (data, buffer, length);
@@ -234,22 +190,22 @@ 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
 /* 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 )
 {
   int rc, i;
   char *p, line[ASSUAN_LINELENGTH];
                     unsigned char *digest, size_t digestlen, int digestalgo,
                     char **r_buf, size_t *r_buflen )
 {
   int rc, i;
   char *p, line[ASSUAN_LINELENGTH];
-  struct membuf data;
+  membuf_t data;
   size_t len;
 
   *r_buf = NULL;
   size_t len;
 
   *r_buf = NULL;
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (digestlen*2 + 50 > DIM(line))
   if (rc)
     return rc;
 
   if (digestlen*2 + 50 > DIM(line))
-    return seterr (General_Error);
+    return gpg_error (GPG_ERR_GENERAL);
 
   rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
 
   rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
@@ -261,6 +217,16 @@ gpgsm_agent_pksign (const char *keygrip,
   if (rc)
     return map_assuan_err (rc);
 
   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 )
   sprintf (line, "SETHASH %d ", digestalgo);
   p = line + strlen (line);
   for (i=0; i < digestlen ; i++, p += 2 )
@@ -282,10 +248,10 @@ gpgsm_agent_pksign (const char *keygrip,
   if (!gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL))
     {
       xfree (*r_buf); *r_buf = NULL;
   if (!gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL))
     {
       xfree (*r_buf); *r_buf = NULL;
-      return GNUPG_Invalid_Value;
+      return gpg_error (GPG_ERR_INV_VALUE);
     }
 
     }
 
-  return *r_buf? 0 : GNUPG_Out_Of_Core;
+  return *r_buf? 0 : OUT_OF_CORE (errno);
 }
 
 
 }
 
 
@@ -309,27 +275,27 @@ 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
 /* 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;
   char line[ASSUAN_LINELENGTH];
                        char **r_buf, size_t *r_buflen )
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
-  struct membuf data;
+  membuf_t data;
   struct cipher_parm_s cipher_parm;
   size_t n, len;
   char *buf, *endp;
   size_t ciphertextlen;
   
   if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen)
   struct cipher_parm_s cipher_parm;
   size_t n, len;
   char *buf, *endp;
   size_t ciphertextlen;
   
   if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
   *r_buf = NULL;
 
   ciphertextlen = gcry_sexp_canon_len (ciphertext, 0, NULL, NULL);
   if (!ciphertextlen)
   *r_buf = NULL;
 
   ciphertextlen = gcry_sexp_canon_len (ciphertext, 0, NULL, NULL);
   if (!ciphertextlen)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (rc)
     return rc;
 
@@ -344,6 +310,16 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
   if (rc)
     return map_assuan_err (rc);
 
   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;
   init_membuf (&data, 1024);
   cipher_parm.ctx = agent_ctx;
   cipher_parm.ciphertext = ciphertext;
@@ -360,16 +336,17 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
   put_membuf (&data, "", 1); /* make sure it is 0 terminated */
   buf = get_membuf (&data, &len);
   if (!buf)
   put_membuf (&data, "", 1); /* make sure it is 0 terminated */
   buf = get_membuf (&data, &len);
   if (!buf)
-    return seterr (Out_Of_Core);
+    return gpg_error (GPG_ERR_ENOMEM);
   /* FIXME: We would better a return a full S-exp and not just a part */
   assert (len);
   len--; /* remove the terminating 0 */
   n = strtoul (buf, &endp, 10);
   if (!n || *endp != ':')
   /* FIXME: We would better a return a full S-exp and not just a part */
   assert (len);
   len--; /* remove the terminating 0 */
   n = strtoul (buf, &endp, 10);
   if (!n || *endp != ':')
-    return seterr (Invalid_Sexp);
+    return gpg_error (GPG_ERR_INV_SEXP);
   endp++;
   if (endp-buf+n > len)
   endp++;
   if (endp-buf+n > len)
-    return seterr (Invalid_Sexp); /* oops len does not match internal len*/
+    return gpg_error (GPG_ERR_INV_SEXP); /* oops len does not
+                                           match internal len*/
   memmove (buf, endp, n);
   *r_buflen = n;
   *r_buf = buf;
   memmove (buf, endp, n);
   *r_buflen = n;
   *r_buf = buf;
@@ -396,16 +373,17 @@ inq_genkey_parms (void *opaque, const char *keyword)
 \f
 /* Call the agent to generate a newkey */
 int
 \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;
 {
   int rc;
   struct genkey_parm_s gk_parm;
-  struct membuf data;
+  membuf_t data;
   size_t len;
   char *buf;
 
   *r_pubkey = NULL;
   size_t len;
   char *buf;
 
   *r_pubkey = NULL;
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (rc)
     return rc;
 
@@ -418,7 +396,7 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
   gk_parm.sexp = keyparms;
   gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL);
   if (!gk_parm.sexplen)
   gk_parm.sexp = keyparms;
   gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL);
   if (!gk_parm.sexplen)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
   rc = assuan_transact (agent_ctx, "GENKEY",
                         membuf_data_cb, &data, 
                         inq_genkey_parms, &gk_parm, NULL, NULL);
   rc = assuan_transact (agent_ctx, "GENKEY",
                         membuf_data_cb, &data, 
                         inq_genkey_parms, &gk_parm, NULL, NULL);
@@ -429,11 +407,11 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
     }
   buf = get_membuf (&data, &len);
   if (!buf)
     }
   buf = get_membuf (&data, &len);
   if (!buf)
-    return GNUPG_Out_Of_Core;
+    return gpg_error (GPG_ERR_ENOMEM);
   if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
     {
       xfree (buf);
   if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
     {
       xfree (buf);
-      return GNUPG_Invalid_Sexp;
+      return gpg_error (GPG_ERR_INV_SEXP);
     }
   *r_pubkey = buf;
   return 0;
     }
   *r_pubkey = buf;
   return 0;
@@ -443,13 +421,13 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
 /* Ask the agent whether the certificate is in the list of trusted
    keys */
 int
 /* 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];
 
 {
   int rc;
   char *fpr;
   char line[ASSUAN_LINELENGTH];
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (rc)
     return rc;
 
@@ -457,7 +435,7 @@ gpgsm_agent_istrusted (KsbaCert cert)
   if (!fpr)
     {
       log_error ("error getting the fingerprint\n");
   if (!fpr)
     {
       log_error ("error getting the fingerprint\n");
-      return seterr (General_Error);
+      return gpg_error (GPG_ERR_GENERAL);
     }
 
   snprintf (line, DIM(line)-1, "ISTRUSTED %s", fpr);
     }
 
   snprintf (line, DIM(line)-1, "ISTRUSTED %s", fpr);
@@ -470,13 +448,13 @@ gpgsm_agent_istrusted (KsbaCert cert)
 
 /* Ask the agent to mark CERT as a trusted Root-CA one */
 int
 
 /* 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];
 
 {
   int rc;
   char *fpr, *dn;
   char line[ASSUAN_LINELENGTH];
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (rc)
     return rc;
 
@@ -484,14 +462,14 @@ gpgsm_agent_marktrusted (KsbaCert cert)
   if (!fpr)
     {
       log_error ("error getting the fingerprint\n");
   if (!fpr)
     {
       log_error ("error getting the fingerprint\n");
-      return seterr (General_Error);
+      return gpg_error (GPG_ERR_GENERAL);
     }
 
   dn = ksba_cert_get_issuer (cert, 0);
   if (!dn)
     {
       xfree (fpr);
     }
 
   dn = ksba_cert_get_issuer (cert, 0);
   if (!dn)
     {
       xfree (fpr);
-      return seterr (General_Error);
+      return gpg_error (GPG_ERR_GENERAL);
     }
   snprintf (line, DIM(line)-1, "MARKTRUSTED %s S %s", fpr, dn);
   line[DIM(line)-1] = 0;
     }
   snprintf (line, DIM(line)-1, "MARKTRUSTED %s S %s", fpr, dn);
   line[DIM(line)-1] = 0;
@@ -507,17 +485,17 @@ gpgsm_agent_marktrusted (KsbaCert cert)
 /* Ask the agent whether the a corresponding secret key is available
    for the given keygrip */
 int
 /* 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];
 
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (!hexkeygrip || strlen (hexkeygrip) != 40)
   if (rc)
     return rc;
 
   if (!hexkeygrip || strlen (hexkeygrip) != 40)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip);
   line[DIM(line)-1] = 0;
 
   snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip);
   line[DIM(line)-1] = 0;
@@ -533,7 +511,7 @@ learn_cb (void *opaque, const void *buffer, size_t length)
   struct learn_parm_s *parm = opaque;
   size_t len;
   char *buf;
   struct learn_parm_s *parm = opaque;
   size_t len;
   char *buf;
-  KsbaCert cert;
+  ksba_cert_t cert;
   int rc;
 
   if (parm->error)
   int rc;
 
   if (parm->error)
@@ -548,34 +526,46 @@ learn_cb (void *opaque, const void *buffer, size_t length)
   buf = get_membuf (parm->data, &len);
   if (!buf)
     {
   buf = get_membuf (parm->data, &len);
   if (!buf)
     {
-      parm->error = GNUPG_Out_Of_Core;
+      parm->error = gpg_error (GPG_ERR_ENOMEM);
       return 0;
     }
 
 
       return 0;
     }
 
 
-  /* FIXME: this shoudl go inot import.c */
-  cert = ksba_cert_new ();
-  if (!cert)
+  /* FIXME: this should go into import.c */
+  rc = ksba_cert_new (&cert);
+  if (rc)
     {
     {
-      parm->error = GNUPG_Out_Of_Core;
+      parm->error = rc;
       return 0;
     }
   rc = ksba_cert_init_from_mem (cert, buf, len);
   if (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);
       ksba_cert_release (cert);
-      parm->error = map_ksba_err (rc);
+      parm->error = rc;
       return 0;
     }
 
   rc = gpgsm_basic_cert_check (cert);
       return 0;
     }
 
   rc = gpgsm_basic_cert_check (cert);
-  if (rc)
-    log_error ("invalid certificate: %s\n", gnupg_strerror (rc));
+  if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT)
+    { /* For later use we store it in the ephemeral database. */
+      log_info ("issuer certificate missing - storing as ephemeral\n");
+      keydb_store_cert (cert, 1, NULL);
+    }
+  else if (rc)
+    log_error ("invalid certificate: %s\n", gpg_strerror (rc));
   else
     {
   else
     {
-      keydb_store_cert (cert);
-      log_error ("certificate stored\n");
+      int existed;
+
+      if (!keydb_store_cert (cert, 0, &existed))
+        {
+          if (opt.verbose > 1 && existed)
+            log_info ("certificate already in DB\n");
+          else if (opt.verbose && !existed)
+            log_info ("certificate imported\n");
+        }
     }
 
   ksba_cert_release (cert);
     }
 
   ksba_cert_release (cert);
@@ -585,14 +575,14 @@ learn_cb (void *opaque, const void *buffer, size_t length)
   
 /* Call the agent to learn about a smartcard */
 int
   
 /* 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;
 {
   int rc;
   struct learn_parm_s learn_parm;
-  struct membuf data;
+  membuf_t data;
   size_t len;
 
   size_t len;
 
-  rc = start_agent ();
+  rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   if (rc)
     return rc;
 
@@ -609,3 +599,37 @@ gpgsm_agent_learn ()
   return learn_parm.error;
 }
 
   return learn_parm.error;
 }
 
+\f
+/* 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 (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+
+  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;
+
+  rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+  return map_assuan_err (rc);
+}
+