(parse_dn_part): Map common OIDs to human readable
[gnupg.git] / g10 / import.c
index 3af8cc7..9c32324 100644 (file)
@@ -1,5 +1,6 @@
-/* import.c
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+/* import.c - Import OpenPGP key material
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ *               2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -56,19 +57,18 @@ struct stats_s {
 };
 
 
-static int import( IOBUF inp, int fast, const char* fname,
+static int import( iobuf_t inp, const char* fname,
                    struct stats_s *stats, unsigned int options );
-static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
+static int read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root );
 static void revocation_present(KBNODE keyblock);
-static void remove_bad_stuff (KBNODE keyblock);
-static int import_one( const char *fname, KBNODE keyblock, int fast,
+static int import_one( const char *fname, KBNODE keyblock,
                        struct stats_s *stats, unsigned int options);
 static int import_secret_one( const char *fname, KBNODE keyblock,
-                              struct stats_s *stats );
+                              struct stats_s *stats, unsigned int options);
 static int import_revoke_cert( const char *fname, KBNODE node,
                                struct stats_s *stats);
 static int chk_self_sigs( const char *fname, KBNODE keyblock,
-                         PKT_public_key *pk, u32 *keyid );
+                         PKT_public_key *pk, u32 *keyid, int *non_self );
 static int delete_inv_parts( const char *fname, KBNODE keyblock,
                             u32 *keyid, unsigned int options );
 static int merge_blocks( const char *fname, KBNODE keyblock_orig,
@@ -83,63 +83,32 @@ static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
 static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
                             const char *fname, u32 *keyid );
 
-
 int
 parse_import_options(char *str,unsigned int *options)
 {
-  char *tok;
-  int hit=0;
-  struct
-  {
-    char *name;
-    unsigned int bit;
-  } import_opts[]=
+  struct parse_options import_opts[]=
     {
       {"allow-local-sigs",IMPORT_ALLOW_LOCAL_SIGS},
-      {"repair-hkp-subkey-bug",IMPORT_REPAIR_HKP_SUBKEY_BUG},
+      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG},
+      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG},
+      {"fast-import",IMPORT_FAST_IMPORT},
+      {"convert-sk-to-pk",IMPORT_SK2PK},
       {NULL,0}
     };
 
-  while((tok=strsep(&str," ,")))
-    {
-      int i,rev=0;
-
-      if(ascii_strncasecmp("no-",tok,3)==0)
-       {
-         rev=1;
-         tok+=3;
-       }
-
-      for(i=0;import_opts[i].name;i++)
-       {
-         if(ascii_strcasecmp(import_opts[i].name,tok)==0)
-           {
-             if(rev)
-               *options&=~import_opts[i].bit;
-             else
-               *options|=import_opts[i].bit;
-             hit=1;
-             break;
-           }
-       }
-
-      if(!hit && !import_opts[i].name)
-       return 0;
-    }
-
-  return hit;
+  return parse_options(str,options,import_opts);
 }
 
 void *
 import_new_stats_handle (void)
 {
-    return m_alloc_clear ( sizeof (struct stats_s) );
+    return xcalloc (1, sizeof (struct stats_s) );
 }
 
 void
 import_release_stats_handle (void *p)
 {
-    m_free (p);
+    xfree (p);
 }
 
 /****************
@@ -174,7 +143,7 @@ import_release_stats_handle (void *p)
  *
  */
 static int
-import_keys_internal( IOBUF inp, char **fnames, int nnames, int fast,
+import_keys_internal( iobuf_t inp, char **fnames, int nnames,
                      void *stats_handle, unsigned int options )
 {
     int i, rc = 0;
@@ -184,7 +153,7 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames, int fast,
         stats = import_new_stats_handle ();
 
     if (inp) {
-        rc = import( inp, fast, "[stream]", stats, options);
+        rc = import( inp, "[stream]", stats, options);
     }
     else {
         if( !fnames && !nnames )
@@ -192,17 +161,19 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames, int fast,
 
        for(i=0; i < nnames; i++ ) {
            const char *fname = fnames? fnames[i] : NULL;
-           IOBUF inp = iobuf_open(fname);
+           iobuf_t inp2 = iobuf_open(fname);
            if( !fname )
                fname = "[stdin]";
-           if( !inp )
+           if( !inp2 )
                log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
            else {
-               rc = import( inp, fast, fname, stats, options );
-               iobuf_close(inp);
+               rc = import( inp2, fname, stats, options );
+               iobuf_close(inp2);
+                /* Must invalidate that ugly cache to actually close it. */
+                iobuf_ioctl (NULL, 2, 0, (char*)fname);
                if( rc )
                    log_error("import from `%s' failed: %s\n", fname,
-                                     g10_errstr(rc) );
+                                     gpg_strerror (rc) );
            }
            if( !fname )
                break;
@@ -212,36 +183,37 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames, int fast,
         import_print_stats (stats);
         import_release_stats_handle (stats);
     }
-    /* If no fast import and we really added new keys or merged new
-       user ids, signatures or revocations, then update/check the
-       trustdb if the user specified by setting interactive or by
-       not setting no-auto-check-trustdb */
-    if (!fast && (stats->imported || stats->n_uids ||
-                  stats->n_sigs || stats->n_revoc)) {
-        if (opt.interactive)
-           update_trustdb();
+    /* If no fast import and the trustdb is dirty (i.e. we added a key
+       or userID that had something other than a selfsig, a signature
+       that was other than a selfsig, or any revocation), then
+       update/check the trustdb if the user specified by setting
+       interactive or by not setting no-auto-check-trustdb */
+    if (!(options&IMPORT_FAST_IMPORT) && trustdb_pending_check())
+      {
+       if (opt.interactive)
+         update_trustdb();
        else if (!opt.no_auto_check_trustdb)
-           check_trustdb();
-    }
+         check_trustdb();
+      }
+
     return rc;
 }
 
 void
-import_keys( char **fnames, int nnames, int fast,
+import_keys( char **fnames, int nnames,
             void *stats_handle, unsigned int options )
 {
-    import_keys_internal( NULL, fnames, nnames, fast, stats_handle, options);
+    import_keys_internal( NULL, fnames, nnames, stats_handle, options);
 }
 
 int
-import_keys_stream( IOBUF inp, int fast,
-                   void *stats_handle, unsigned int options )
+import_keys_stream( iobuf_t inp, void *stats_handle, unsigned int options )
 {
-    return import_keys_internal( inp, NULL, NULL, fast, stats_handle, options);
+    return import_keys_internal( inp, NULL, 0, stats_handle, options);
 }
 
 static int
-import( IOBUF inp, int fast, const char* fname,
+import( iobuf_t inp, const char* fname,
        struct stats_s *stats, unsigned int options )
 {
     PACKET *pending_pkt = NULL;
@@ -251,17 +223,16 @@ import( IOBUF inp, int fast, const char* fname,
     getkey_disable_caches();
 
     if( !opt.no_armor ) { /* armored reading is not disabled */
-       armor_filter_context_t *afx = m_alloc_clear( sizeof *afx );
+       armor_filter_context_t *afx = xcalloc (1, sizeof *afx );
        afx->only_keyblocks = 1;
        iobuf_push_filter2( inp, armor_filter, afx, 1 );
     }
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
-        remove_bad_stuff (keyblock);
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, fast, stats, options );
+           rc = import_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
-                rc = import_secret_one( fname, keyblock, stats );
+                rc = import_secret_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SIGNATURE
                 && keyblock->pkt->pkt.signature->sig_class == 0x20 )
            rc = import_revoke_cert( fname, keyblock, stats );
@@ -275,12 +246,12 @@ import( IOBUF inp, int fast, const char* fname,
        if( rc )
            break;
        if( !(++stats->count % 100) && !opt.quiet )
-           log_info(_("%lu keys so far processed\n"), stats->count );
+           log_info(_("%lu keys processed so far\n"), stats->count );
     }
     if( rc == -1 )
        rc = 0;
-    else if( rc && rc != G10ERR_INV_KEYRING )
-       log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc));
+    else if( rc && rc != GPG_ERR_INV_KEYRING )
+       log_error( _("error reading `%s': %s\n"), fname, gpg_strerror (rc));
 
     return rc;
 }
@@ -353,7 +324,7 @@ import_print_stats (void *hd)
  * Retunr: 0 = okay, -1 no more blocks or another errorcode.
  */
 static int
-read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
+read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root )
 {
     int rc;
     PACKET *pkt;
@@ -367,13 +338,13 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     }
     else
        in_cert = 0;
-    pkt = m_alloc( sizeof *pkt );
+    pkt = xmalloc ( sizeof *pkt );
     init_packet(pkt);
     while( (rc=parse_packet(a, pkt)) != -1 ) {
        if( rc ) {  /* ignore errors */
-           if( rc != G10ERR_UNKNOWN_PACKET ) {
-               log_error("read_block: read error: %s\n", g10_errstr(rc) );
-               rc = G10ERR_INV_KEYRING;
+           if( rc != GPG_ERR_UNKNOWN_PACKET ) {
+               log_error("read_block: read error: %s\n", gpg_strerror (rc) );
+               rc = GPG_ERR_INV_KEYRING;
                goto ready;
            }
            free_packet( pkt );
@@ -395,11 +366,11 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
          case PKT_COMPRESSED:
            if( pkt->pkt.compressed->algorithm < 1
                || pkt->pkt.compressed->algorithm > 2 ) {
-               rc = G10ERR_COMPR_ALGO;
+               rc = GPG_ERR_COMPR_ALGO;
                goto ready;
            }
            {
-               compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx );
+               compress_filter_context_t *cfx = xcalloc (1, sizeof *cfx );
                cfx->algo = pkt->pkt.compressed->algorithm;
                pkt->pkt.compressed->buf = NULL;
                iobuf_push_filter2( a, compress_filter, cfx, 1 );
@@ -428,7 +399,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
                    root = new_kbnode( pkt );
                else
                    add_kbnode( root, new_kbnode( pkt ) );
-               pkt = m_alloc( sizeof *pkt );
+               pkt = xmalloc ( sizeof *pkt );
            }
            init_packet(pkt);
            break;
@@ -443,27 +414,11 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     else
        *ret_root = root;
     free_packet( pkt );
-    m_free( pkt );
+    xfree ( pkt );
     return rc;
 }
 
-
-static void
-remove_bad_stuff (KBNODE keyblock)
-{
-    KBNODE node;
-
-    for (node=keyblock; node; node = node->next ) {
-        if( node->pkt->pkttype == PKT_SIGNATURE ) {
-            /* delete the subpackets we used to use for the
-               verification cache */
-            delete_sig_subpkt (node->pkt->pkt.signature->unhashed,
-                               SIGSUBPKT_PRIV_VERIFY_CACHE);
-        }
-    }
-}
-
-/* Walk through the subkeys on a pk to find if we have the HKP
+/* Walk through the subkeys on a pk to find if we have the PKS
    disease: multiple subkeys with their binding sigs stripped, and the
    sig for the first subkey placed after the last subkey.  That is,
    instead of "pk uid sig sub1 bind1 sub2 bind2 sub3 bind3" we have
@@ -473,7 +428,7 @@ remove_bad_stuff (KBNODE keyblock)
    sub2 sub3".  Returns TRUE if the keyblock was modified. */
 
 static int
-fix_hkp_corruption(KBNODE keyblock)
+fix_pks_corruption(KBNODE keyblock)
 {
   int changed=0,keycount=0;
   KBNODE node,last=NULL,sknode=NULL;
@@ -504,6 +459,10 @@ fix_hkp_corruption(KBNODE keyblock)
          sknode->next=node;
          last->next=NULL;
 
+         /* Note we aren't checking whether this binding sig is a
+            selfsig.  This is not necessary here as the subkey and
+            binding sig will be rejected later if that is the
+            case. */
          if(check_key_signature(keyblock,node,NULL))
            {
              /* Not a match, so undo the changes. */
@@ -549,6 +508,27 @@ print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
   write_status_text (STATUS_IMPORT_OK, buf);
 }
 
+void
+print_import_check (PKT_public_key * pk, PKT_user_id * id)
+{
+    char * buf;
+    byte fpr[24];
+    u32 keyid[2];
+    size_t i, pos = 0, n;
+
+    buf = xmalloc (17+41+id->len+32);
+    keyid_from_pk (pk, keyid);
+    sprintf (buf, "%08X%08X ", keyid[0], keyid[1]);
+    pos = 17;
+    fingerprint_from_pk (pk, fpr, &n);
+    for (i = 0; i < n; i++, pos += 2)
+        sprintf (buf+pos, "%02X", fpr[i]);
+    strcat (buf, " ");
+    pos += 1;
+    strcat (buf, id->name);
+    write_status_text (STATUS_IMPORT_CHECK, buf);
+    xfree (buf);
+}
 
 /****************
  * Try to import one keyblock. Return an error only in serious cases, but
@@ -557,7 +537,7 @@ print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
  * which called g10.
  */
 static int
-import_one( const char *fname, KBNODE keyblock, int fast,
+import_one( const char *fname, KBNODE keyblock,
             struct stats_s *stats, unsigned int options )
 {
     PKT_public_key *pk;
@@ -568,6 +548,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     int rc = 0;
     int new_key = 0;
     int mod_key = 0;
+    int non_self = 0;
 
     /* get the key and print some info about it */
     node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
@@ -578,7 +559,11 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if( opt.verbose ) {
+    if(pk->pubkey_algo==PUBKEY_ALGO_ELGAMAL)
+      log_info(_("NOTE: Elgamal primary key detected - "
+                "this may take some time to import\n"));
+
+    if( opt.verbose && !opt.interactive ) {
        log_info( "pub  %4u%c/%08lX %s   ",
                  nbits_from_pk( pk ),
                  pubkey_letter( pk->pubkey_algo ),
@@ -594,6 +579,9 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     }
     
     if (opt.interactive) {
+        if(is_status_enabled())
+         print_import_check (pk, uidnode->pkt->pkt.user_id);
+       merge_keys_and_selfsig (keyblock);
         tty_printf ("\n");
         show_basic_key_info (keyblock);
         tty_printf ("\n");
@@ -604,11 +592,12 @@ import_one( const char *fname, KBNODE keyblock, int fast,
 
     clear_kbnode_flags( keyblock );
 
-    if((options&IMPORT_REPAIR_HKP_SUBKEY_BUG) && fix_hkp_corruption(keyblock))
-      log_info(_("key %08lX: HKP subkey corruption repaired\n"),
+    if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock)
+       && opt.verbose)
+      log_info(_("key %08lX: PKS subkey corruption repaired\n"),
               (ulong)keyid[1]);
 
-    rc = chk_self_sigs( fname, keyblock , pk, keyid );
+    rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self );
     if( rc )
        return rc== -1? 0:rc;
 
@@ -622,31 +611,29 @@ import_one( const char *fname, KBNODE keyblock, int fast,
            node->flag |= 1;
            log_info( _("key %08lX: accepted non self-signed user ID '%s'\n"),
                      (ulong)keyid[1],user);
-           m_free(user);
+           xfree (user);
          }
 
     if( !delete_inv_parts( fname, keyblock, keyid, options ) ) {
-       if( !opt.quiet ) {
-           log_info( _("key %08lX: no valid user IDs\n"),
-                                                       (ulong)keyid[1]);
+        log_error ( _("key %08lX: no valid user IDs\n"), (ulong)keyid[1]);
+       if( !opt.quiet )
            log_info(_("this may be caused by a missing self-signature\n"));
-       }
        stats->no_user_id++;
        return 0;
     }
 
     /* do we have this key already in one of our pubrings ? */
-    pk_orig = m_alloc_clear( sizeof *pk_orig );
-    rc = get_pubkey( pk_orig, keyid );
-    if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) {
+    pk_orig = xcalloc (1, sizeof *pk_orig );
+    rc = get_pubkey_fast ( pk_orig, keyid );
+    if( rc && gpg_err_code (rc) != GPG_ERR_NO_PUBKEY
+        && gpg_err_code (rc) != GPG_ERR_UNUSABLE_PUBKEY ) {
        log_error( _("key %08lX: public key not found: %s\n"),
-                               (ulong)keyid[1], g10_errstr(rc));
+                               (ulong)keyid[1], gpg_strerror (rc));
     }
     else if ( rc && opt.merge_only ) {
        if( opt.verbose )
            log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] );
        rc = 0;
-       fast = 1; /* so that we don't get into the trustdb update */
        stats->skipped_new_keys++;
     }
     else if( rc ) { /* insert this key */
@@ -654,18 +641,27 @@ import_one( const char *fname, KBNODE keyblock, int fast,
 
         rc = keydb_locate_writable (hd, NULL);
        if (rc) {
-           log_error (_("no writable keyring found: %s\n"), g10_errstr (rc));
+           log_error (_("no writable keyring found: %s\n"), gpg_strerror (rc));
             keydb_release (hd);
-           return G10ERR_GENERAL;
+           return GPG_ERR_GENERAL;
        }
        if( opt.verbose > 1 )
            log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
        rc = keydb_insert_keyblock (hd, keyblock );
         if (rc)
           log_error (_("error writing keyring `%s': %s\n"),
-                      keydb_get_resource_name (hd), g10_errstr(rc));
+                      keydb_get_resource_name (hd), gpg_strerror (rc));
        else
-          revalidation_mark ();
+         {
+           /* This should not be possible since we delete the
+              ownertrust when a key is deleted, but it can happen if
+              the keyring and trustdb are out of sync.  It can also
+              be made to happen with the trusted-key command. */
+
+           clear_ownertrusts (pk);
+           if(non_self)
+             revalidation_mark ();
+         }
         keydb_release (hd);
 
        /* we are ready */
@@ -673,12 +669,12 @@ import_one( const char *fname, KBNODE keyblock, int fast,
            char *p=get_user_id_printable (keyid);
            log_info( _("key %08lX: public key \"%s\" imported\n"),
                      (ulong)keyid[1],p);
-           m_free(p);
+           xfree (p);
        }
        if( is_status_enabled() ) {
            char *us = get_long_user_id_string( keyid );
            write_status_text( STATUS_IMPORTED, us );
-           m_free(us);
+           xfree (us);
             print_import_ok (pk,NULL, 1);
        }
        stats->imported++;
@@ -711,14 +707,14 @@ import_one( const char *fname, KBNODE keyblock, int fast,
         }
        if( rc ) {
            log_error (_("key %08lX: can't locate original keyblock: %s\n"),
-                                    (ulong)keyid[1], g10_errstr(rc));
+                                    (ulong)keyid[1], gpg_strerror (rc));
             keydb_release (hd);
            goto leave;
        }
        rc = keydb_get_keyblock (hd, &keyblock_orig );
        if (rc) {
            log_error (_("key %08lX: can't read original keyblock: %s\n"),
-                                           (ulong)keyid[1], g10_errstr(rc));
+                                           (ulong)keyid[1], gpg_strerror (rc));
             keydb_release (hd);
            goto leave;
        }
@@ -740,8 +736,8 @@ import_one( const char *fname, KBNODE keyblock, int fast,
            rc = keydb_update_keyblock (hd, keyblock_orig);
             if (rc)
                log_error (_("error writing keyring `%s': %s\n"),
-                            keydb_get_resource_name (hd), g10_errstr(rc) );
-           else
+                            keydb_get_resource_name (hd), gpg_strerror (rc) );
+           else if(non_self)
              revalidation_mark ();
 
            /* we are ready */
@@ -765,7 +761,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
                else if( n_subk )
                    log_info( _("key %08lX: \"%s\" %d new subkeys\n"),
                                             (ulong)keyid[1], p, n_subk );
-               m_free(p);
+               xfree (p);
            }
 
            stats->n_uids +=n_uids;
@@ -784,7 +780,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
                char *p=get_user_id_printable(keyid);
                log_info( _("key %08lX: \"%s\" not changed\n"),
                          (ulong)keyid[1],p);
-               m_free(p);
+               xfree (p);
            }
            stats->unchanged++;
        }
@@ -800,6 +796,65 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     return rc;
 }
 
+/* Walk a secret keyblock and produce a public keyblock out of it. */
+static KBNODE
+sec_to_pub_keyblock(KBNODE sec_keyblock)
+{
+  KBNODE secnode,pub_keyblock=NULL,ctx=NULL;
+
+  while((secnode=walk_kbnode(sec_keyblock,&ctx,0)))
+    {
+      KBNODE pubnode;
+
+      if(secnode->pkt->pkttype==PKT_SECRET_KEY ||
+        secnode->pkt->pkttype==PKT_SECRET_SUBKEY)
+       {
+         /* Make a public key.  We only need to convert enough to
+            write the keyblock out. */
+
+         PKT_secret_key *sk=secnode->pkt->pkt.secret_key;
+         PACKET *pkt=xcalloc (1,sizeof(PACKET));
+         PKT_public_key *pk=xcalloc (1,sizeof(PKT_public_key));
+         int n;
+
+         if(secnode->pkt->pkttype==PKT_SECRET_KEY)
+           pkt->pkttype=PKT_PUBLIC_KEY;
+         else
+           pkt->pkttype=PKT_PUBLIC_SUBKEY;
+
+         pkt->pkt.public_key=pk;
+
+         pk->version=sk->version;
+         pk->timestamp=sk->timestamp;
+         pk->expiredate=sk->expiredate;
+         pk->pubkey_algo=sk->pubkey_algo;
+
+         n=pubkey_get_npkey(pk->pubkey_algo);
+         if(n==0)
+           pk->pkey[0]=mpi_copy(sk->skey[0]);
+         else
+           {
+             int i;
+
+             for(i=0;i<n;i++)
+               pk->pkey[i]=mpi_copy(sk->skey[i]);
+           }
+
+         pubnode=new_kbnode(pkt);
+       }
+      else
+       {
+         pubnode=clone_kbnode(secnode);
+       }
+
+      if(pub_keyblock==NULL)
+       pub_keyblock=pubnode;
+      else
+       add_kbnode(pub_keyblock,pubnode);
+    }
+
+  return pub_keyblock;
+}
 
 /****************
  * Ditto for secret keys.  Handling is simpler than for public keys.
@@ -809,7 +864,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
  */
 static int
 import_secret_one( const char *fname, KBNODE keyblock, 
-                   struct stats_s *stats)
+                   struct stats_s *stats, unsigned int options)
 {
     PKT_secret_key *sk;
     KBNODE node, uidnode;
@@ -842,24 +897,32 @@ import_secret_one( const char *fname, KBNODE keyblock,
        return 0;
     }
 
+    if(sk->protect.algo>110)
+      {
+       log_error(_("key %08lX: secret key with invalid cipher %d "
+                   "- skipped\n"),(ulong)keyid[1],sk->protect.algo);
+       return 0;
+      }
+
     clear_kbnode_flags( keyblock );
 
     /* do we have this key already in one of our secrings ? */
     rc = seckey_available( keyid );
-    if( rc == G10ERR_NO_SECKEY && !opt.merge_only ) { /* simply insert this key */
+    if( gpg_err_code (rc) == GPG_ERR_NO_SECKEY && !opt.merge_only ) { 
+        /* simply insert this key */
         KEYDB_HANDLE hd = keydb_new (1);
 
        /* get default resource */
         rc = keydb_locate_writable (hd, NULL);
        if (rc) {
-           log_error (_("no default secret keyring: %s\n"), g10_errstr (rc));
+           log_error (_("no default secret keyring: %s\n"), gpg_strerror (rc));
             keydb_release (hd);
-           return G10ERR_GENERAL;
+           return GPG_ERR_GENERAL;
        }
        rc = keydb_insert_keyblock (hd, keyblock );
         if (rc)
            log_error (_("error writing keyring `%s': %s\n"),
-                       keydb_get_resource_name (hd), g10_errstr(rc) );
+                       keydb_get_resource_name (hd), gpg_strerror (rc) );
         keydb_release (hd);
        /* we are ready */
        if( !opt.quiet )
@@ -867,6 +930,16 @@ import_secret_one( const char *fname, KBNODE keyblock,
        stats->secret_imported++;
         if (is_status_enabled ()) 
              print_import_ok (NULL, sk, 1|16);
+
+       if(options&IMPORT_SK2PK)
+         {
+           /* Try and make a public key out of this. */
+
+           KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
+           import_one(fname,pub_keyblock,stats,opt.import_options);
+           release_kbnode(pub_keyblock);
+         }
+
     }
     else if( !rc ) { /* we can't merge secret keys */
        log_error( _("key %08lX: already in secret keyring\n"),
@@ -874,10 +947,13 @@ import_secret_one( const char *fname, KBNODE keyblock,
        stats->secret_dups++;
         if (is_status_enabled ()) 
              print_import_ok (NULL, sk, 16);
+
+       /* TODO: if we ever do merge secret keys, make sure to handle
+          the sec_to_pub_keyblock feature as well. */
     }
     else
        log_error( _("key %08lX: secret key not found: %s\n"),
-                               (ulong)keyid[1], g10_errstr(rc));
+                               (ulong)keyid[1], gpg_strerror (rc));
 
     return rc;
 }
@@ -902,17 +978,17 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     keyid[0] = node->pkt->pkt.signature->keyid[0];
     keyid[1] = node->pkt->pkt.signature->keyid[1];
 
-    pk = m_alloc_clear( sizeof *pk );
+    pk = xcalloc (1, sizeof *pk );
     rc = get_pubkey( pk, keyid );
-    if( rc == G10ERR_NO_PUBKEY ) {
-       log_info( _("key %08lX: no public key - "
-                "can't apply revocation certificate\n"), (ulong)keyid[1]);
+    if( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) {
+       log_error ( _("key %08lX: no public key - "
+                      "can't apply revocation certificate\n"), (ulong)keyid[1]);
        rc = 0;
        goto leave;
     }
     else if( rc ) {
        log_error( _("key %08lX: public key not found: %s\n"),
-                                      (ulong)keyid[1], g10_errstr(rc));
+                                      (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
 
@@ -929,13 +1005,13 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     }
     if (rc) {
        log_error (_("key %08lX: can't locate original keyblock: %s\n"),
-                   (ulong)keyid[1], g10_errstr(rc));
+                   (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
     rc = keydb_get_keyblock (hd, &keyblock );
     if (rc) {
        log_error (_("key %08lX: can't read original keyblock: %s\n"),
-                   (ulong)keyid[1], g10_errstr(rc));
+                   (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
 
@@ -946,7 +1022,7 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     rc = check_key_signature( keyblock, node, NULL);
     if( rc ) {
        log_error( _("key %08lX: invalid revocation certificate"
-                 ": %s - rejected\n"), (ulong)keyid[1], g10_errstr(rc));
+                 ": %s - rejected\n"), (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
 
@@ -956,12 +1032,12 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
        if( onode->pkt->pkttype == PKT_USER_ID )
            break;
        else if( onode->pkt->pkttype == PKT_SIGNATURE
-                && onode->pkt->pkt.signature->sig_class == 0x20
-                && keyid[0] == onode->pkt->pkt.signature->keyid[0]
-                && keyid[1] == onode->pkt->pkt.signature->keyid[1] ) {
-           rc = 0;
-           goto leave; /* yes, we already know about it */
-       }
+                 && !cmp_signatures(node->pkt->pkt.signature,
+                                    onode->pkt->pkt.signature))
+          {
+            rc = 0;
+            goto leave; /* yes, we already know about it */
+          }
     }
 
 
@@ -972,16 +1048,23 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     rc = keydb_update_keyblock (hd, keyblock );
     if (rc)
        log_error (_("error writing keyring `%s': %s\n"),
-                   keydb_get_resource_name (hd), g10_errstr(rc) );
+                   keydb_get_resource_name (hd), gpg_strerror (rc) );
     keydb_release (hd); hd = NULL;
     /* we are ready */
     if( !opt.quiet ) {
         char *p=get_user_id_printable (keyid);
        log_info( _("key %08lX: \"%s\" revocation certificate imported\n"),
                                        (ulong)keyid[1],p);
-       m_free(p);
+       xfree (p);
     }
     stats->n_revoc++;
+
+    /* If the key we just revoked was ultimately trusted, remove its
+       ultimate trust.  This doesn't stop the user from putting the
+       ultimate trust back, but is a reasonable solution for now. */
+    if(get_ownertrust(pk)==TRUST_ULTIMATE)
+      clear_ownertrusts(pk);
+
     revalidation_mark ();
 
   leave:
@@ -998,10 +1081,12 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
  * Mark all user-ids with an invalid self-signature by setting bit 1.
  * This works also for subkeys, here the subkey is marked.  Invalid or
  * extra subkey sigs (binding or revocation) are marked for deletion.
+ * non_self is set to true if there are any sigs other than self-sigs
+ * in this keyblock.
  */
 static int
 chk_self_sigs( const char *fname, KBNODE keyblock,
-              PKT_public_key *pk, u32 *keyid )
+              PKT_public_key *pk, u32 *keyid, int *non_self )
 {
     KBNODE n,knode=NULL;
     PKT_signature *sig;
@@ -1017,11 +1102,18 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
          rsdate=0;
          bsnode=NULL;
          rsnode=NULL;
+         continue;
        }
       else if( n->pkt->pkttype != PKT_SIGNATURE )
            continue;
        sig = n->pkt->pkt.signature;
        if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
+
+         /* This just caches the sigs for later use.  That way we
+            import a fully-cached key which speeds things up. */
+         if(!opt.no_sig_cache)
+           check_key_signature(keyblock,n,NULL);
+
            if( (sig->sig_class&~3) == 0x10 ) {
                KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
                if( !unode )  {
@@ -1035,17 +1127,20 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                  rc = check_key_signature( keyblock, n, NULL);
                  if( rc )
                    {
-                     char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
-                                     strlen(unode->pkt->pkt.user_id->name),0);
-                     log_info( rc == G10ERR_PUBKEY_ALGO ?
-                               _("key %08lX: unsupported public key "
-                                 "algorithm on user id \"%s\"\n"):
-                               _("key %08lX: invalid self-signature "
-                                 "on user id \"%s\"\n"),
-                               (ulong)keyid[1],p);
-                     m_free(p);
-                   }
-                 else
+                      if (opt.verbose)
+                        {
+                          char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+                                       strlen(unode->pkt->pkt.user_id->name),0);
+                          log_info( gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
+                                    _("key %08lX: unsupported public key "
+                                      "algorithm on user id \"%s\"\n"):
+                                    _("key %08lX: invalid self-signature "
+                                      "on user id \"%s\"\n"),
+                                    (ulong)keyid[1],p);
+                          xfree (p);
+                        }
+                    }
+                  else
                    unode->flag |= 1; /* mark that signature checked */
                }
            }
@@ -1054,39 +1149,49 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                 like the rest of gpg.  If the standard gets
                 revocation targets, this may need to be revised. */
 
-               if( !knode ) {
-                   log_info( _("key %08lX: no subkey for key binding\n"),
-                                           (ulong)keyid[1]);
-                   n->flag |= 4; /* delete this */
-               }
-               else {
-                 rc = check_key_signature( keyblock, n, NULL);
-                 if( rc ) {
-                   log_info(  rc == G10ERR_PUBKEY_ALGO ?
-                           _("key %08lX: unsupported public key algorithm\n"):
+               if( !knode )
+                  {
+                    if (opt.verbose)
+                      log_info( _("key %08lX: no subkey for subkey "
+                                  "binding signature\n"),(ulong)keyid[1]);
+                    n->flag |= 4; /* delete this */
+                  }
+               else
+                  {
+                    rc = check_key_signature( keyblock, n, NULL);
+                    if( rc )
+                      {
+                        if (opt.verbose)
+                          log_info(  gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
+                            _("key %08lX: unsupported public key algorithm\n"):
                            _("key %08lX: invalid subkey binding\n"),
-                           (ulong)keyid[1]);
-                   n->flag|=4;
-                 }
-                 else {
-                   /* It's valid, so is it newer? */
-                   if(sig->timestamp>=bsdate) {
-                     knode->flag |= 1;  /* the subkey is valid */
-                     if(bsnode) {
-                       bsnode->flag|=4; /* Delete the last binding
-                                           sig since this one is
-                                           newer */
-                       log_info(_("key %08lX: removed multiple subkey "
-                                  "binding\n"),(ulong)keyid[1]);
-                     }
-
-                     bsnode=n;
-                     bsdate=sig->timestamp;
-                   }
-                   else
-                     n->flag|=4; /* older */
-                 }
-               }
+                                     (ulong)keyid[1]);
+                        n->flag|=4;
+                      }
+                    else
+                      {
+                        /* It's valid, so is it newer? */
+                        if(sig->timestamp>=bsdate) 
+                          {
+                            knode->flag |= 1;  /* the subkey is valid */
+                            if(bsnode)
+                              {
+                                bsnode->flag|=4; /* Delete the last binding
+                                                    sig since this one is
+                                                    newer */
+                                if (opt.verbose)
+                                  log_info(_("key %08lX: removed multiple "
+                                             "subkey binding\n"),
+                                           (ulong)keyid[1]);
+                              }
+                            
+                            bsnode=n;
+                            bsdate=sig->timestamp;
+                          }
+                        else
+                          n->flag|=4; /* older */
+                      }
+                  }
            }
            else if( sig->sig_class == 0x28 ) {
              /* We don't actually mark the subkey as revoked right
@@ -1096,14 +1201,16 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                  See the comment in getkey.c:merge_selfsigs_subkey for
                  more */
                if( !knode ) {
-                   log_info( _("key %08lX: no subkey for key revocation\n"),
-                                           (ulong)keyid[1]);
-                   n->flag |= 4; /* delete this */
+                  if (opt.verbose)
+                   log_info( _("key %08lX: no subkey for subkey "
+                               "revocation signature\n"),(ulong)keyid[1]);
+                  n->flag |= 4; /* delete this */
                }
                else {
                  rc = check_key_signature( keyblock, n, NULL);
                  if( rc ) {
-                   log_info(  rc == G10ERR_PUBKEY_ALGO ?
+                    if (opt.verbose)
+                      log_info(  gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
                            _("key %08lX: unsupported public key algorithm\n"):
                            _("key %08lX: invalid subkey revocation\n"),
                               (ulong)keyid[1]);
@@ -1116,8 +1223,10 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                        rsnode->flag|=4; /* Delete the last revocation
                                            sig since this one is
                                            newer */
-                       log_info(_("key %08lX: removed multiple subkey "
-                                  "revocation\n"),(ulong)keyid[1]);
+                        if (opt.verbose)
+                          log_info(_("key %08lX: removed multiple subkey "
+                                     "revocation signatures\n"),
+                                   (ulong)keyid[1]);
                      }
 
                      rsnode=n;
@@ -1129,6 +1238,8 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                }
            }
        }
+       else
+         *non_self=1;
     }
 
     return 0;
@@ -1191,30 +1302,33 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
              subkey_seen = 1;
        }
        else if( node->pkt->pkttype == PKT_SIGNATURE
-                && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo)
+                && openpgp_pk_test_algo( node->pkt->pkt.signature
+                                                     ->pubkey_algo, 0)
                 && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
            delete_kbnode( node ); /* build_packet() can't handle this */
        else if( node->pkt->pkttype == PKT_SIGNATURE &&
                 !node->pkt->pkt.signature->flags.exportable &&
                 !(options&IMPORT_ALLOW_LOCAL_SIGS) &&
                 seckey_available( node->pkt->pkt.signature->keyid ) ) {
-           /* here we violate the rfc a bit by still allowing
+           /* Here we violate the rfc a bit by still allowing
             * to import non-exportable signature when we have the
             * the secret key used to create this signature - it
-            * seems that this makes sense */
+            * seems that this makes sense. */
+          if (opt.verbose)
            log_info( _("key %08lX: non exportable signature "
                                    "(class %02x) - skipped\n"),
-                                   (ulong)keyid[1],
+                      (ulong)keyid[1],
                                     node->pkt->pkt.signature->sig_class );
-           delete_kbnode( node );
+          delete_kbnode( node );
        }
        else if( node->pkt->pkttype == PKT_SIGNATURE
                 && node->pkt->pkt.signature->sig_class == 0x20 )  {
            if( uid_seen ) {
+              if (opt.verbose)
                log_error( _("key %08lX: revocation certificate "
-                                    "at wrong place - skipped\n"),
+                             "at wrong place - skipped\n"),
                                    (ulong)keyid[1]);
-               delete_kbnode( node );
+              delete_kbnode( node );
            }
            else {
              /* If the revocation cert is from a different key than
@@ -1228,9 +1342,10 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
                  int rc = check_key_signature( keyblock, node, NULL);
                  if( rc )
                    {
-                     log_error( _("key %08lX: invalid revocation "
-                                  "certificate: %s - skipped\n"),
-                                (ulong)keyid[1], g10_errstr(rc));
+                      if (opt.verbose)
+                        log_info ( _("key %08lX: invalid revocation "
+                                     "certificate: %s - skipped\n"),
+                                   (ulong)keyid[1], gpg_strerror (rc));
                      delete_kbnode( node );
                    }
                }
@@ -1240,11 +1355,21 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
                 (node->pkt->pkt.signature->sig_class == 0x18 ||
                  node->pkt->pkt.signature->sig_class == 0x28) &&
                 !subkey_seen ) {
-           log_error( _("key %08lX: subkey signature "
+          if (opt.verbose)
+           log_info ( _("key %08lX: subkey signature "
                         "in wrong place - skipped\n"),
                       (ulong)keyid[1]);
-           delete_kbnode( node );
+          delete_kbnode( node );
        }
+       else if( node->pkt->pkttype == PKT_SIGNATURE
+                && !IS_CERT(node->pkt->pkt.signature))
+         {
+            if (opt.verbose)
+              log_info (_("key %08lX: unexpected signature class (0x%02X) -"
+                          " skipped\n"),(ulong)keyid[1],
+                        node->pkt->pkt.signature->sig_class);
+           delete_kbnode(node);
+         }
        else if( (node->flag & 4) ) /* marked for deletion */
            delete_kbnode( node );
     }
@@ -1338,8 +1463,9 @@ collapse_uids( KBNODE *keyblock )
        kid1 = keyid_from_sk( n->pkt->pkt.secret_key, NULL );
     else
        kid1 = 0;
-    log_info(_("key %08lX: duplicated user ID detected - merged\n"),
-                                                                (ulong)kid1);
+    if (!opt.quiet)
+      log_info (_("key %08lX: duplicated user ID detected - merged\n"),
+                (ulong)kid1);
 
     return 1;
 }
@@ -1389,9 +1515,10 @@ revocation_present(KBNODE keyblock)
                          itself? */
                      int rc;
 
-                     rc=get_pubkey_byfprint(NULL,sig->revkey[idx]->fpr,
-                                            MAX_FINGERPRINT_LEN);
-                     if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
+                     rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr,
+                                                   MAX_FINGERPRINT_LEN);
+                     if ( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
+                          || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY)
                        {
                          /* No, so try and get it */
                          if(opt.keyserver_scheme &&
@@ -1405,12 +1532,13 @@ revocation_present(KBNODE keyblock)
                                                      MAX_FINGERPRINT_LEN);
 
                              /* Do we have it now? */
-                             rc=get_pubkey_byfprint(NULL,
+                             rc=get_pubkey_byfprint_fast (NULL,
                                                     sig->revkey[idx]->fpr,
                                                     MAX_FINGERPRINT_LEN);
                            }
 
-                         if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
+                         if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
+                              || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY)
                            log_info(_("WARNING: key %08lX may be revoked: "
                                       "revocation key %08lX not present.\n"),
                                     (ulong)keyid_from_pk(pk,NULL),
@@ -1454,23 +1582,27 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                    break;
                else if( onode->pkt->pkttype == PKT_SIGNATURE
                         && onode->pkt->pkt.signature->sig_class == 0x20
-                        && node->pkt->pkt.signature->keyid[0]
-                           == onode->pkt->pkt.signature->keyid[0]
-                        && node->pkt->pkt.signature->keyid[1]
-                           == onode->pkt->pkt.signature->keyid[1] ) {
-                   found = 1;
-                   break;
-               }
+                        && !cmp_signatures(onode->pkt->pkt.signature,
+                                           node->pkt->pkt.signature))
+                  {
+                    found = 1;
+                    break;
+                  }
            }
            if( !found ) {
-               char *p=get_user_id_printable (keyid);
                KBNODE n2 = clone_kbnode(node);
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
                 ++*n_sigs;
-               log_info(_("key %08lX: \"%s\" revocation certificate added\n"),
-                                        (ulong)keyid[1],p);
-               m_free(p);
+
+                if (!opt.quiet)
+                  {
+                    char *p=get_user_id_printable (keyid);
+                    log_info(_("key %08lX: \"%s\" "
+                               "revocation certificate added\n"),
+                             (ulong)keyid[1],p);
+                    xfree (p);
+                  }
            }
        }
     }
@@ -1499,8 +1631,9 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
                 ++*n_sigs;
-               log_info( _("key %08lX: direct key signature added\n"),
-                                        (ulong)keyid[1]);
+                if (!opt.quiet)
+                  log_info( _("key %08lX: direct key signature added\n"),
+                            (ulong)keyid[1]);
            }
        }
     }
@@ -1668,20 +1801,12 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
            || n->pkt->pkt.signature->sig_class == 0x28 )
            continue; /* skip signatures which are only valid on subkeys */
        found = 0;
-       for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next){
-           if( n2->pkt->pkttype == PKT_SIGNATURE
-               && n->pkt->pkt.signature->keyid[0]
-                  == n2->pkt->pkt.signature->keyid[0]
-               && n->pkt->pkt.signature->keyid[1]
-                  == n2->pkt->pkt.signature->keyid[1]
-               && n->pkt->pkt.signature->timestamp
-                  <= n2->pkt->pkt.signature->timestamp
-               && n->pkt->pkt.signature->sig_class
-                  == n2->pkt->pkt.signature->sig_class ) {
-               found++;
-               break;
-           }
-       }
+       for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next)
+         if(!cmp_signatures(n->pkt->pkt.signature,n2->pkt->pkt.signature))
+           {
+             found++;
+             break;
+           }   
        if( !found ) {
            /* This signature is new or newer, append N to DST.
             * We add a clone to the original keyblock, because this