* sign.c (gpgsm_sign): Print an error message on all failures.
[gnupg.git] / sm / import.c
index 8dc36c1..2bc6e69 100644 (file)
@@ -1,5 +1,5 @@
 /* import.c - Import certificates
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <time.h>
 #include <assert.h>
 
+#include "gpgsm.h"
 #include <gcrypt.h>
 #include <ksba.h>
 
-#include "gpgsm.h"
 #include "keydb.h"
 #include "i18n.h"
 
-struct reader_cb_parm_s {
-  FILE *fp;
+struct stats_s {
+  unsigned long count;
+  unsigned long imported;
+  unsigned long unchanged;
+  unsigned long not_imported;
 };
 
 
-static int
-reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
+
+static void
+print_imported_status (CTRL ctrl, ksba_cert_t cert)
 {
-  struct reader_cb_parm_s *parm = cb_value;
-  size_t n;
-  int c = 0;
+  char *fpr;
+  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+  gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL);
+  xfree (fpr);
+}
 
-  *nread = 0;
-  if (!buffer)
-    return -1; /* not supported */
 
-  for (n=0; n < count; n++)
+/* Print an IMPORT_PROBLEM status.  REASON is one of:
+   0 := "No specific reason given".
+   1 := "Invalid Certificate".
+   2 := "Issuer Certificate missing".
+   3 := "Certificate Chain too long".
+   4 := "Error storing certificate".
+*/
+static void
+print_import_problem (CTRL ctrl, ksba_cert_t cert, int reason)
+{
+  char *fpr = NULL;
+  char buf[25];
+  int i;
+
+  sprintf (buf, "%d", reason);
+  if (cert)
     {
-      c = getc (parm->fp);
-      if (c == EOF)
+      fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+      /* detetect an error (all high) value */
+      for (i=0; fpr[i] == 'F'; i++)
+        ;
+      if (!fpr[i])
         {
-          if ( ferror (parm->fp) )
-            return -1;
-          if (n)
-            break; /* return what we have before an EOF */
-          return -1;
+          xfree (fpr);
+          fpr = NULL;
         }
-      *(byte *)buffer++ = c;
     }
+  gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL);
+  xfree (fpr);
+}
 
-  *nread = n;
-  return 0;
+
+void
+print_imported_summary (CTRL ctrl, struct stats_s *stats)
+{
+  char buf[14*25];
+
+  if (!opt.quiet)
+    {
+      log_info (_("total number processed: %lu\n"), stats->count);
+      if (stats->imported) 
+        {
+          log_info (_("              imported: %lu"), stats->imported );
+          log_printf ("\n");
+       }
+      if (stats->unchanged)
+        log_info (_("             unchanged: %lu\n"), stats->unchanged);
+      if (stats->not_imported)
+        log_info (_("          not imported: %lu\n"), stats->not_imported);
+    }
+
+  sprintf (buf, "%lu 0 %lu 0 %lu 0 0 0 0 0 0 0 0 %lu",
+           stats->count,
+           stats->imported,
+           stats->unchanged,
+           stats->not_imported
+           );
+  gpgsm_status (ctrl, STATUS_IMPORT_RES, buf);
 }
 
 
+
 static void
-store_cert (KsbaCert cert)
+check_and_store (CTRL ctrl, struct stats_s *stats, ksba_cert_t cert, int depth)
 {
-  KEYDB_HANDLE kh;
   int rc;
 
-  kh = keydb_new (0);
-  if (!kh)
+  stats->count++;
+  if ( depth >= 50 )
     {
-      log_error (_("failed to allocated keyDB handle\n"));
+      log_error (_("certificate chain too long\n"));
+      stats->not_imported++;
+      print_import_problem (ctrl, cert, 3);
       return;
     }
-  rc = keydb_locate_writable (kh, 0);
-  if (rc)
-      log_error (_("error finding writable keyDB: %s\n"), gpgsm_strerror (rc));
 
-  rc = keydb_insert_cert (kh, cert);
-  if (rc)
+  rc = gpgsm_basic_cert_check (cert);
+  if (!rc)
+    {
+      int existed;
+
+      if (!keydb_store_cert (cert, 0, &existed))
+        {
+          ksba_cert_t next = NULL;
+
+          if (!existed)
+            {
+              print_imported_status (ctrl, cert);
+              stats->imported++;
+            }
+          else
+            stats->unchanged++;
+            
+          if (opt.verbose > 1 && existed)
+            {
+              if (depth)
+                log_info ("issuer certificate already in DB\n");
+              else
+                log_info ("certificate already in DB\n");
+            }
+          else if (opt.verbose && !existed)
+            {
+              if (depth)
+                log_info ("issuer certificate imported\n");
+              else
+                log_info ("certificate imported\n");
+            }
+          /* Now lets walk up the chain and import all certificates up
+             the chain.*/
+          else if (!gpgsm_walk_cert_chain (cert, &next))
+            {
+              check_and_store (ctrl, stats, next, depth+1);
+              ksba_cert_release (next);
+            }
+        }
+      else
+        {
+          log_error (_("error storing certificate\n"));
+          stats->not_imported++;
+          print_import_problem (ctrl, cert, 4);
+        }
+    }
+  else
     {
-      log_error (_("error storing certificate: %s\n"), gpgsm_strerror (rc));
+      log_error (_("basic certificate checks failed - not imported\n"));
+      stats->not_imported++;
+      print_import_problem (ctrl, cert,
+                            gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 :
+                            gpg_err_code (rc) == GPG_ERR_BAD_CERT?     1 : 0);
     }
-  keydb_release (kh);               
 }
 
 
-
 \f
-int
-gpgsm_import (int in_fd)
+
+static int
+import_one (CTRL ctrl, struct stats_s *stats, int in_fd)
 {
   int rc;
-  KsbaReader reader = NULL;
-  KsbaCert cert = NULL;
-  struct reader_cb_parm_s rparm;
+  Base64Context b64reader = NULL;
+  ksba_reader_t reader;
+  ksba_cert_t cert = NULL;
+  ksba_cms_t cms = NULL;
+  FILE *fp = NULL;
+  ksba_content_type_t ct;
 
-  memset (&rparm, 0, sizeof rparm);
-
-  rparm.fp = fdopen ( dup (in_fd), "rb");
-  if (!rparm.fp)
+  fp = fdopen ( dup (in_fd), "rb");
+  if (!fp)
     {
+      rc = gpg_error (gpg_err_code_from_errno (errno));
       log_error ("fdopen() failed: %s\n", strerror (errno));
-      rc = seterr (IO_Error);
       goto leave;
     }
 
-  /* setup a skaba reader which uses a callback function so that we can 
-     strip off a base64 encoding when necessary */
-  reader = ksba_reader_new ();
-  if (!reader)
-    {
-      rc = seterr (Out_Of_Core);
-      goto leave;
-    }
-
-  rc = ksba_reader_set_cb (reader, reader_cb, &rparm );
+  rc = gpgsm_create_reader (&b64reader, ctrl, fp, &reader);
   if (rc)
     {
-      ksba_reader_release (reader);
-      rc = map_ksba_err (rc);
+      log_error ("can't create reader: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
-  cert = ksba_cert_new ();
-  if (!cert)
-    {
-      rc = seterr (Out_Of_Core);
-      goto leave;
-    }
+  ct = ksba_cms_identify (reader);
+  if (ct == KSBA_CT_SIGNED_DATA)
+    { /* This is probably a signed-only message - import the certs */
+      ksba_stop_reason_t stopreason;
+      int i;
 
-  rc = ksba_cert_read_der (cert, reader);
-  if (rc)
-    {
-      rc = map_ksba_err (rc);
-      goto leave;
+      rc = ksba_cms_new (&cms);
+      if (rc)
+        goto leave;
+
+      rc = ksba_cms_set_reader_writer (cms, reader, NULL);
+      if (rc)
+        {
+          log_error ("ksba_cms_set_reader_writer failed: %s\n",
+                     gpg_strerror (rc));
+          goto leave;
+        }
+
+
+      do 
+        {
+          rc = ksba_cms_parse (cms, &stopreason);
+          if (rc)
+            {
+              log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
+              goto leave;
+            }
+
+          if (stopreason == KSBA_SR_BEGIN_DATA)
+              log_info ("not a certs-only message\n");
+        }
+      while (stopreason != KSBA_SR_READY);   
+      
+      for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
+        {
+          check_and_store (ctrl, stats, cert, 0);
+          ksba_cert_release (cert); 
+          cert = NULL;
+        }
+      if (!i)
+        log_error ("no certificate found\n");
     }
+  else if (ct == KSBA_CT_NONE)
+    { /* Failed to identify this message - assume a certificate */
 
-  if ( !gpgsm_validate_path (cert) )
-    store_cert (cert);
+      rc = ksba_cert_new (&cert);
+      if (rc)
+        goto leave;
 
+      rc = ksba_cert_read_der (cert, reader);
+      if (rc)
+        goto leave;
+
+      check_and_store (ctrl, stats, cert, 0);
+    }
+  else
+    {
+      log_error ("can't extract certificates from input\n");
+      rc = gpg_error (GPG_ERR_NO_DATA);
+    }
+   
  leave:
+  ksba_cms_release (cms);
   ksba_cert_release (cert);
-  ksba_reader_release (reader);
-  if (rparm.fp)
-    fclose (rparm.fp);
+  gpgsm_destroy_reader (b64reader);
+  if (fp)
+    fclose (fp);
+  return rc;
+}
+
+
+int
+gpgsm_import (CTRL ctrl, int in_fd)
+{
+  int rc;
+  struct stats_s stats;
+
+  memset (&stats, 0, sizeof stats);
+  rc = import_one (ctrl, &stats, in_fd);
+  print_imported_summary (ctrl, &stats);
+  /* If we never printed an error message do it now so that a command
+     line invocation will return with an error (log_error keeps a
+     global errorcount) */
+  if (rc && !log_get_errorcount (0))
+    log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
+  return rc;
+}
+
+
+int
+gpgsm_import_files (CTRL ctrl, int nfiles, char **files,
+                    int (*of)(const char *fname))
+{
+  int rc = 0;
+  struct stats_s stats;
+
+  memset (&stats, 0, sizeof stats);
+  
+  if (!nfiles)
+    rc = import_one (ctrl, &stats, 0);
+  else
+    {
+      for (; nfiles && !rc ; nfiles--, files++)
+        {
+          int fd = of (*files);
+          rc = import_one (ctrl, &stats, fd);
+          close (fd);
+          if (rc == -1)
+            rc = 0;
+        }
+    }
+  print_imported_summary (ctrl, &stats);
+  /* If we never printed an error message do it now so that a command
+     line invocation will return with an error (log_error keeps a
+     global errorcount) */
+  if (rc && !log_get_errorcount (0))
+    log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
   return rc;
 }