Take advantage of newer gpg-error features.
[gnupg.git] / sm / export.c
index 8a89110..0f01e5f 100644 (file)
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
 #include <time.h>
 #include <assert.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <sys/wait.h>
 
 #include "gpgsm.h"
 #include <gcrypt.h>
 #include <ksba.h>
 
 #include "keydb.h"
+#include "exechelp.h"
 #include "i18n.h"
 
-#ifdef _POSIX_OPEN_MAX
-#define MAX_OPEN_FDS _POSIX_OPEN_MAX
-#else
-#define MAX_OPEN_FDS 20
-#endif
 
 
 /* A table to store a fingerprint as used in a duplicates table.  We
@@ -65,7 +58,8 @@ typedef struct duptable_s *duptable_t;
 
 
 static void print_short_info (ksba_cert_t cert, FILE *fp);
-static gpg_error_t export_p12 (const unsigned char *certimg, size_t certimglen,
+static gpg_error_t export_p12 (ctrl_t ctrl,
+                               const unsigned char *certimg, size_t certimglen,
                                const char *prompt, const char *keygrip,
                                FILE **retfp);
 
@@ -123,7 +117,7 @@ insert_duptable (duptable_t *table, unsigned char *fpr, int *exists)
   /* Insert that fingerprint. */
   t = xtrymalloc (sizeof *t);
   if (!t)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   memcpy (t->fpr, fpr+1, 19);
   t->next = table[idx];
   table[idx] = t;
@@ -135,7 +129,7 @@ insert_duptable (duptable_t *table, unsigned char *fpr, int *exists)
 
 /* Export all certificates or just those given in NAMES. */
 void
-gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
+gpgsm_export (ctrl_t ctrl, STRLIST names, FILE *fp)
 {
   KEYDB_HANDLE hd = NULL;
   KEYDB_SEARCH_DESC *desc = NULL;
@@ -176,7 +170,7 @@ gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
   if (!ndesc)
     {
       log_error ("allocating memory for export failed: %s\n",
-                 gpg_strerror (OUT_OF_CORE (errno)));
+                 gpg_strerror (out_of_core ()));
       goto leave;
     }
 
@@ -355,7 +349,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
   if (!desc)
     {
       log_error ("allocating memory for export failed: %s\n",
-                 gpg_strerror (OUT_OF_CORE (errno)));
+                 gpg_strerror (out_of_core ()));
       goto leave;
     }
 
@@ -392,7 +386,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
     }
       
   keygrip = gpgsm_get_keygrip_hexstring (cert);
-  if (!keygrip || gpgsm_agent_havekey (keygrip))
+  if (!keygrip || gpgsm_agent_havekey (ctrl, keygrip))
     {
       /* Note, that the !keygrip case indicates a bad certificate. */
       rc = gpg_error (GPG_ERR_NO_SECKEY);
@@ -423,7 +417,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
 
 
   prompt = gpgsm_format_keydesc (cert);
-  rc = export_p12 (image, imagelen, prompt, keygrip, &datafp);
+  rc = export_p12 (ctrl, image, imagelen, prompt, keygrip, &datafp);
   xfree (prompt);
   if (rc)
     goto leave;
@@ -521,106 +515,40 @@ popen_protect_tool (const char *pgmname,
                     const char *prompt, const char *keygrip,
                     pid_t *pid)
 {
-  gpg_error_t err;
-  int fd, fdout, rp[2];
-  int n, i;
-
-  fflush (infile);
-  rewind (infile);
-  fd = fileno (infile);
-  fdout = fileno (outfile);
-  if (fd == -1 || fdout == -1)
-    log_fatal ("no file descriptor for temporary file: %s\n",
-               strerror (errno));
-
-  /* Now start the protect-tool. */
-  if (pipe (rp) == -1)
-    {
-      err = gpg_error_from_errno (errno);
-      log_error (_("error creating a pipe: %s\n"), strerror (errno));
-      return err;
-    }
-      
-  *pid = fork ();
-  if (*pid == -1)
-    {
-      err = gpg_error_from_errno (errno);
-      log_error (_("error forking process: %s\n"), strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
-      return err;
-    }
-
-  if (!*pid)
-    { /* Child. */
-      const char *arg0;
-
-      arg0 = strrchr (pgmname, '/');
-      if (arg0)
-        arg0++;
-      else
-        arg0 = pgmname;
-
-      /* Connect the infile to stdin. */
-      if (fd != 0 && dup2 (fd, 0) == -1)
-        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
-
-      /* Connect the outfile to stdout. */
-      if (fdout != 1 && dup2 (fdout, 1) == -1)
-        log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
-      
-      /* Connect stderr to our pipe. */
-      if (rp[1] != 2 && dup2 (rp[1], 2) == -1)
-        log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
-
-      /* Close all other files. */
-      n = sysconf (_SC_OPEN_MAX);
-      if (n < 0)
-        n = MAX_OPEN_FDS;
-      for (i=3; i < n; i++)
-        close(i);
-      errno = 0;
-
-      setup_pinentry_env ();
-
-      execlp (pgmname, arg0,
-              "--homedir", opt.homedir,
-              "--p12-export",
-              "--prompt", prompt?prompt:"", 
-              "--",
-              keygrip,
-              NULL);
-      /* No way to print anything, as we have closed all streams. */
-      _exit (31);
-    }
-
-  /* Parent. */
-  close (rp[1]);
-  *statusfile = fdopen (rp[0], "r");
-  if (!*statusfile)
-    {
-      err = gpg_error_from_errno (errno);
-      log_error ("can't fdopen pipe for reading: %s", strerror (errno));
-      kill (*pid, SIGTERM);
-      return err;
-    }
-
-  return 0;
+  const char *argv[20];
+  int i=0;
+
+  argv[i++] = "--homedir";
+  argv[i++] = opt.homedir;
+  argv[i++] = "--p12-export";
+  argv[i++] = "--have-cert";
+  argv[i++] = "--prompt";
+  argv[i++] = prompt?prompt:"";
+  argv[i++] = "--enable-status-msg";
+  argv[i++] = "--",
+  argv[i++] = keygrip,
+  argv[i] = NULL;
+  assert (i < sizeof argv);
+
+  return gnupg_spawn_process (pgmname, argv, infile, outfile,
+                              setup_pinentry_env,
+                              statusfile, pid);
 }
 
 
 static gpg_error_t
-export_p12 (const unsigned char *certimg, size_t certimglen,
+export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen,
             const char *prompt, const char *keygrip,
             FILE **retfp)
 {
   const char *pgmname;
   gpg_error_t err = 0, child_err = 0;
-  int i, c, cont_line;
+  int c, cont_line;
   unsigned int pos;
   FILE *infp = NULL, *outfp = NULL, *fp = NULL;
   char buffer[1024];
   pid_t pid = -1;
+  int bad_pass = 0;
 
   if (!opt.protect_tool_program || !*opt.protect_tool_program)
     pgmname = GNUPG_DEFAULT_PROTECT_TOOL;
@@ -630,14 +558,14 @@ export_p12 (const unsigned char *certimg, size_t certimglen,
   infp = tmpfile ();
   if (!infp)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("error creating temporary file: %s\n"), strerror (errno));
       goto cleanup;
     }
 
   if (fwrite (certimg, certimglen, 1, infp) != 1)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("error writing to temporary file: %s\n"),
                  strerror (errno));
       goto cleanup;
@@ -646,7 +574,7 @@ export_p12 (const unsigned char *certimg, size_t certimglen,
   outfp = tmpfile ();
   if (!outfp)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("error creating temporary file: %s\n"), strerror (errno));
       goto cleanup;
     }
@@ -669,13 +597,27 @@ export_p12 (const unsigned char *certimg, size_t certimglen,
          protect tool to figure out better error codes for
          CHILD_ERR. */
       buffer[pos++] = c;
-      if (pos >= 5 /*sizeof buffer - 1*/ || c == '\n')
+      if (pos >= sizeof buffer - 5 || c == '\n')
         {
           buffer[pos - (c == '\n')] = 0;
           if (cont_line)
             log_printf ("%s", buffer);
           else
-            log_info ("%s", buffer);
+            {
+              if (!strncmp (buffer, "gpg-protect-tool: [PROTECT-TOOL:] ",34))
+                {
+                  char *p, *pend;
+
+                  p = buffer + 34;
+                  pend = strchr (p, ' ');
+                  if (pend)
+                    *pend = 0;
+                  if ( !strcmp (p, "bad-passphrase"))
+                    bad_pass++;
+                }
+              else 
+                log_info ("%s", buffer);
+            }
           pos = 0;
           cont_line = (c != '\n');
         }
@@ -705,21 +647,7 @@ export_p12 (const unsigned char *certimg, size_t certimglen,
     fclose (fp);
   if (pid != -1)
     {
-      int status;
-
-      while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
-        ;
-      if (i == -1)
-        log_error (_("waiting for protect-tools to terminate failed: %s\n"),
-                   strerror (errno));
-      else if (WIFEXITED (status) && WEXITSTATUS (status) == 31)
-        log_error (_("error running `%s': probably not installed\n"), pgmname);
-      else if (WIFEXITED (status) && WEXITSTATUS (status))
-        log_error (_("error running `%s': exit status %d\n"), pgmname,
-                     WEXITSTATUS (status));
-      else if (!WIFEXITED (status))
-        log_error (_("error running `%s': terminated\n"), pgmname);
-      else 
+      if (!gnupg_wait_process (pgmname, pid))
         child_err = 0;
     }
   if (!err)
@@ -731,6 +659,14 @@ export_p12 (const unsigned char *certimg, size_t certimglen,
     }
   else
     *retfp = outfp;
+  if (bad_pass)
+    {
+      /* During export this is the passphrase used to unprotect the
+         key and not the pkcs#12 thing as in export.  Therefore we can
+         issue the regular passphrase status.  FIXME: replace the all
+         zero keyid by a regular one. */
+      gpgsm_status (ctrl, STATUS_BAD_PASSPHRASE, "0000000000000000");
+    }
   return err;
 }