Fix bug 1091.
[gnupg.git] / g10 / import.c
index ee4ea95..88eb24e 100644 (file)
@@ -1,12 +1,12 @@
 /* import.c - import a key into our key storage.
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- *               2006 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ *               2007 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -15,9 +15,7 @@
  * GNU General Public License for more details.
  *
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -30,7 +28,7 @@
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "errors.h"
+#include "status.h"
 #include "keydb.h"
 #include "util.h"
 #include "trustdb.h"
@@ -66,7 +64,7 @@ static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
 static void revocation_present(KBNODE keyblock);
 static int import_one(const char *fname, KBNODE keyblock,struct stats_s *stats,
                      unsigned char **fpr,size_t *fpr_len,
-                     unsigned int options);
+                     unsigned int options,int from_sk);
 static int import_secret_one( const char *fname, KBNODE keyblock,
                               struct stats_s *stats, unsigned int options);
 static int import_revoke_cert( const char *fname, KBNODE node,
@@ -243,20 +241,25 @@ import( IOBUF inp, const char* fname,struct stats_s *stats,
        unsigned char **fpr,size_t *fpr_len,unsigned int options )
 {
     PACKET *pending_pkt = NULL;
-    KBNODE keyblock;
+    KBNODE keyblock = NULL;  /* Need to initialize because gcc can't
+                                grasp the return semantics of
+                                read_block. */
     int rc = 0;
 
     getkey_disable_caches();
 
     if( !opt.no_armor ) { /* armored reading is not disabled */
-       armor_filter_context_t *afx = xmalloc_clear( sizeof *afx );
+       armor_filter_context_t *afx;
+
+        afx = new_armor_context ();
        afx->only_keyblocks = 1;
-       iobuf_push_filter2( inp, armor_filter, afx, 1 );
+       push_armor_filter (afx, inp);
+        release_armor_context (afx);
     }
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, stats, fpr, fpr_len, options );
+           rc = import_one( fname, keyblock, stats, fpr, fpr_len, options, 0);
        else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
                 rc = import_secret_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SIGNATURE
@@ -297,9 +300,9 @@ import_print_stats (void *hd)
            log_info(_("          w/o user IDs: %lu\n"), stats->no_user_id );
        if( stats->imported || stats->imported_rsa ) {
            log_info(_("              imported: %lu"), stats->imported );
-           if( stats->imported_rsa )
-               fprintf(stderr, "  (RSA: %lu)", stats->imported_rsa );
-           putc('\n', stderr);
+           if (stats->imported_rsa)
+              log_printf ("  (RSA: %lu)", stats->imported_rsa );
+           log_printf ("\n");
        }
        if( stats->unchanged )
            log_info(_("             unchanged: %lu\n"), stats->unchanged );
@@ -596,13 +599,16 @@ check_prefs(KBNODE keyblock)
 
              if(prefs->type==PREFTYPE_SYM)
                {
-                 if (openpgp_cipher_algo_test (prefs->value))
+                 if (openpgp_cipher_test_algo (prefs->value))
                    {
-                     const char *algo = gcry_cipher_algo_name (prefs->value);
+                     const char *algo = 
+                        (openpgp_cipher_test_algo (prefs->value)
+                         ? num 
+                         : openpgp_cipher_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
                      log_info(_("         \"%s\": preference for cipher"
-                                " algorithm %s\n"),user,algo?algo:num);
+                                " algorithm %s\n"), user, algo);
                      problem=1;
                    }
                }
@@ -610,11 +616,14 @@ check_prefs(KBNODE keyblock)
                {
                  if(openpgp_md_test_algo(prefs->value))
                    {
-                     const char *algo = gcry_md_algo_name (prefs->value);
+                     const char *algo =
+                        (gcry_md_test_algo (prefs->value)
+                         ? num 
+                         : gcry_md_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
                      log_info(_("         \"%s\": preference for digest"
-                                " algorithm %s\n"),user,algo?algo:num);
+                                " algorithm %s\n"), user, algo);
                      problem=1;
                    }
                }
@@ -645,7 +654,7 @@ check_prefs(KBNODE keyblock)
 
       if(!opt.batch)
        {
-         STRLIST sl=NULL,locusr=NULL;
+         strlist_t sl=NULL,locusr=NULL;
          size_t fprlen=0;
          byte fpr[MAX_FINGERPRINT_LEN],*p;
          char username[(MAX_FINGERPRINT_LEN*2)+1];
@@ -673,11 +682,12 @@ check_prefs(KBNODE keyblock)
  * Try to import one keyblock. Return an error only in serious cases, but
  * never for an invalid keyblock.  It uses log_error to increase the
  * internal errorcount, so that invalid input can be detected by programs
- * which called g10.
+ * which called gpg.
  */
 static int
 import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
-           unsigned char **fpr,size_t *fpr_len,unsigned int options )
+           unsigned char **fpr,size_t *fpr_len,unsigned int options,
+           int from_sk )
 {
     PKT_public_key *pk;
     PKT_public_key *pk_orig;
@@ -687,6 +697,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     int rc = 0;
     int new_key = 0;
     int mod_key = 0;
+    int same_key = 0;
     int non_self = 0;
 
     /* get the key and print some info about it */
@@ -696,9 +707,6 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
 
     pk = node->pkt->pkt.public_key;
 
-    if(fpr)
-      *fpr=fingerprint_from_pk(pk,NULL,fpr_len);
-
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
@@ -708,12 +716,14 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
                  nbits_from_pk( pk ),
                  pubkey_letter( pk->pubkey_algo ),
                  keystr_from_pk(pk), datestr_from_pk(pk) );
-       if( uidnode )
-         print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+       if (uidnode)
+         print_utf8_string (log_get_stream (),
+                             uidnode->pkt->pkt.user_id->name,
                             uidnode->pkt->pkt.user_id->len );
-       putc('\n', stderr);
+       log_printf ("\n");
       }
 
+
     if( !uidnode )
       {
        log_error( _("key %s: no user ID\n"), keystr_from_pk(pk));
@@ -951,7 +961,8 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
        }
        else
          {
-           if (is_status_enabled ()) 
+            same_key = 1;
+            if (is_status_enabled ()) 
              print_import_ok (pk, NULL, 0);
 
            if( !opt.quiet )
@@ -968,6 +979,33 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     }
 
   leave:
+    if (mod_key || new_key || same_key)
+      {
+       /* A little explanation for this: we fill in the fingerprint
+          when importing keys as it can be useful to know the
+          fingerprint in certain keyserver-related cases (a keyserver
+          asked for a particular name, but the key doesn't have that
+          name).  However, in cases where we're importing more than
+          one key at a time, we cannot know which key to fingerprint.
+          In these cases, rather than guessing, we do not
+          fingerprinting at all, and we must hope the user ID on the
+          keys are useful.  Note that we need to do this for new
+          keys, merged keys and even for unchanged keys.  This is
+          required because for example the --auto-key-locate feature
+          may import an already imported key and needs to know the
+          fingerprint of the key in all cases.  */
+       if (fpr)
+         {
+           xfree (*fpr);
+            /* Note that we need to compare against 0 here because
+               COUNT gets only incremented after returning form this
+               function.  */
+           if (stats->count == 0)
+             *fpr = fingerprint_from_pk (pk, NULL, fpr_len);
+           else
+             *fpr = NULL;
+         }
+      }
 
     /* Now that the key is definitely incorporated into the keydb, we
        need to check if a designated revocation is present or if the
@@ -976,13 +1014,13 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     if(mod_key)
       {
        revocation_present(keyblock_orig);
-       if(seckey_available(keyid)==0)
+       if(!from_sk && seckey_available(keyid)==0)
          check_prefs(keyblock_orig);
       }
     else if(new_key)
       {
        revocation_present(keyblock);
-       if(seckey_available(keyid)==0)
+       if(!from_sk && seckey_available(keyid)==0)
          check_prefs(keyblock);
       }
 
@@ -1090,7 +1128,7 @@ import_secret_one( const char *fname, KBNODE keyblock,
        if( uidnode )
          print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
                             uidnode->pkt->pkt.user_id->len );
-       putc('\n', stderr);
+       log_printf ("\n");
       }
     stats->secret_read++;
 
@@ -1154,7 +1192,7 @@ import_secret_one( const char *fname, KBNODE keyblock,
            if(pub_keyblock)
              {
                import_one(fname,pub_keyblock,stats,
-                          NULL,NULL,opt.import_options);
+                          NULL,NULL,opt.import_options,1);
                release_kbnode(pub_keyblock);
              }
          }
@@ -1200,6 +1238,8 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     u32 keyid[2];
     int rc = 0;
 
+    (void)fname;
+
     assert( !node->next );
     assert( node->pkt->pkttype == PKT_SIGNATURE );
     assert( node->pkt->pkt.signature->sig_class == 0x20 );
@@ -1327,6 +1367,9 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
     u32 bsdate=0,rsdate=0;
     KBNODE bsnode=NULL,rsnode=NULL;
 
+    (void)fname;
+    (void)pk;
+
     for( n=keyblock; (n = find_next_kbnode(n, 0)); ) {
       if(n->pkt->pkttype==PKT_PUBLIC_SUBKEY)
        {
@@ -1499,6 +1542,8 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
     KBNODE node;
     int nvalid=0, uid_seen=0, subkey_seen=0;
 
+    (void)fname;
+
     for(node=keyblock->next; node; node = node->next ) {
        if( node->pkt->pkttype == PKT_USER_ID ) {
            uid_seen = 1;
@@ -1624,90 +1669,119 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
  * It may happen that the imported keyblock has duplicated user IDs.
  * We check this here and collapse those user IDs together with their
  * sigs into one.
- * Returns: True if the keyblock hash changed.
+ * Returns: True if the keyblock has changed.
  */
 int
 collapse_uids( KBNODE *keyblock )
 {
-    KBNODE n, n2;
-    int in_uid;
-    int any=0;
+  KBNODE uid1;
+  int any=0;
+
+  for(uid1=*keyblock;uid1;uid1=uid1->next)
+    {
+      KBNODE uid2;
 
-  restart:
-    for( n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype != PKT_USER_ID )
+      if(is_deleted_kbnode(uid1))
+       continue;
+
+      if(uid1->pkt->pkttype!=PKT_USER_ID)
+       continue;
+
+      for(uid2=uid1->next;uid2;uid2=uid2->next)
+       {
+         if(is_deleted_kbnode(uid2))
            continue;
-       for( n2 = n->next; n2; n2 = n2->next ) {
-           if( n2->pkt->pkttype == PKT_USER_ID
-               && !cmp_user_ids( n->pkt->pkt.user_id,
-                                 n2->pkt->pkt.user_id ) ) {
-               /* found a duplicate */
-               any = 1;
-               if( !n2->next
-                   || n2->next->pkt->pkttype == PKT_USER_ID
-                   || n2->next->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                   || n2->next->pkt->pkttype == PKT_SECRET_SUBKEY  ) {
-                   /* no more signatures: delete the user ID
-                    * and start over */
-                   remove_kbnode( keyblock, n2 );
-               }
-               else {
-                   /* The simple approach: Move one signature and
-                    * then start over to delete the next one :-( */
-                   move_kbnode( keyblock, n2->next, n->next );
+
+         if(uid2->pkt->pkttype!=PKT_USER_ID)
+           continue;
+
+         if(cmp_user_ids(uid1->pkt->pkt.user_id,
+                         uid2->pkt->pkt.user_id)==0)
+           {
+             /* We have a duplicated uid */
+             KBNODE sig1,last;
+
+             any=1;
+
+             /* Now take uid2's signatures, and attach them to
+                uid1 */
+             for(last=uid2;last->next;last=last->next)
+               {
+                 if(is_deleted_kbnode(last))
+                   continue;
+
+                 if(last->next->pkt->pkttype==PKT_USER_ID
+                    || last->next->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                    || last->next->pkt->pkttype==PKT_SECRET_SUBKEY)
+                   break;
                }
-               goto restart;
-           }
-       }
-    }
-    if( !any )
-       return 0;
 
-  restart_sig:
-    /* now we may have duplicate signatures on one user ID: fix this */
-    for( in_uid = 0, n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype == PKT_USER_ID )
-           in_uid = 1;
-       else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                || n->pkt->pkttype == PKT_SECRET_SUBKEY )
-           in_uid = 0;
-       else if( in_uid ) {
-           n2 = n;
-           do {
-               KBNODE ncmp = NULL;
-               for( ; n2; n2 = n2->next ) {
-                   if(    n2->pkt->pkttype == PKT_USER_ID
-                       || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                       || n2->pkt->pkttype == PKT_SECRET_SUBKEY )
+             /* Snip out uid2 */
+             (find_prev_kbnode(*keyblock,uid2,0))->next=last->next;
+
+             /* Now put uid2 in place as part of uid1 */
+             last->next=uid1->next;
+             uid1->next=uid2;
+             delete_kbnode(uid2);
+
+             /* Now dedupe uid1 */
+             for(sig1=uid1->next;sig1;sig1=sig1->next)
+               {
+                 KBNODE sig2;
+
+                 if(is_deleted_kbnode(sig1))
+                   continue;
+
+                 if(sig1->pkt->pkttype==PKT_USER_ID
+                    || sig1->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                    || sig1->pkt->pkttype==PKT_SECRET_SUBKEY)
+                   break;
+
+                 if(sig1->pkt->pkttype!=PKT_SIGNATURE)
+                   continue;
+
+                 for(sig2=sig1->next,last=sig1;sig2;last=sig2,sig2=sig2->next)
+                   {
+                     if(is_deleted_kbnode(sig2))
+                       continue;
+
+                     if(sig2->pkt->pkttype==PKT_USER_ID
+                        || sig2->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                        || sig2->pkt->pkttype==PKT_SECRET_SUBKEY)
                        break;
-                   if( n2->pkt->pkttype != PKT_SIGNATURE )
-                       ;
-                   else if( !ncmp )
-                       ncmp = n2;
-                   else if( !cmp_signatures( ncmp->pkt->pkt.signature,
-                                               n2->pkt->pkt.signature )) {
-                       remove_kbnode( keyblock, n2 );
-                       goto restart_sig;
+
+                     if(sig2->pkt->pkttype!=PKT_SIGNATURE)
+                       continue;
+
+                     if(cmp_signatures(sig1->pkt->pkt.signature,
+                                       sig2->pkt->pkt.signature)==0)
+                       {
+                         /* We have a match, so delete the second
+                            signature */
+                         delete_kbnode(sig2);
+                         sig2=last;
+                       }
                    }
                }
-               n2 = ncmp? ncmp->next : NULL;
-           } while( n2 );
+           }
        }
     }
 
-    if(!opt.quiet)
-      {
-       const char *key="???";
+  commit_kbnode(keyblock);
 
-       if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
-         key=keystr_from_pk(n->pkt->pkt.public_key);
-       else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
-         key=keystr_from_sk(n->pkt->pkt.secret_key);
+  if(any && !opt.quiet)
+    {
+      const char *key="???";
 
-       log_info(_("key %s: duplicated user ID detected - merged\n"),key);
-      }
+      if( (uid1=find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
+       key=keystr_from_pk(uid1->pkt->pkt.public_key);
+      else if( (uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
+       key=keystr_from_sk(uid1->pkt->pkt.secret_key);
 
-    return 1;
+      log_info(_("key %s: duplicated user ID detected - merged\n"),key);
+    }
+
+  return any;
 }
 
 /* Check for a 0x20 revocation from a revocation key that is not
@@ -1980,11 +2054,14 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
  * append the userid starting with NODE and all signatures to KEYBLOCK.
  */
 static int
-append_uidKBNODE keyblock, KBNODE node, int *n_sigs,
-                                         const char *fname, u32 *keyid )
+append_uid (KBNODE keyblock, KBNODE node, int *n_sigs,
+            const char *fname, u32 *keyid )
 {
     KBNODE n, n_where=NULL;
 
+    (void)fname;
+    (void)keyid;
+
     assert(node->pkt->pkttype == PKT_USER_ID );
 
     /* find the position */
@@ -2032,6 +2109,9 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
     KBNODE n, n2;
     int found=0;
 
+    (void)fname;
+    (void)keyid;
+
     assert(dst->pkt->pkttype == PKT_USER_ID );
     assert(src->pkt->pkttype == PKT_USER_ID );
 
@@ -2067,12 +2147,15 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
  * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY.
  */
 static int
-merge_keysigsKBNODE dst, KBNODE src, int *n_sigs,
-                                   const char *fname, u32 *keyid )
+merge_keysigs (KBNODE dst, KBNODE src, int *n_sigs,
+               const char *fname, u32 *keyid)
 {
     KBNODE n, n2;
     int found=0;
 
+    (void)fname;
+    (void)keyid;
+
     assert(   dst->pkt->pkttype == PKT_PUBLIC_SUBKEY
           || dst->pkt->pkttype == PKT_SECRET_SUBKEY );
 
@@ -2120,11 +2203,14 @@ merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
  * Mark all new and copied packets by setting flag bit 0.
  */
 static int
-append_keyKBNODE keyblock, KBNODE node, int *n_sigs,
-                                         const char *fname, u32 *keyid )
+append_key (KBNODE keyblock, KBNODE node, int *n_sigs,
+            const char *fname, u32 *keyid)
 {
     KBNODE n;
 
+    (void)fname;
+    (void)keyid;
+
     assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
           || node->pkt->pkttype == PKT_SECRET_SUBKEY );
 
@@ -2318,7 +2404,8 @@ auto_create_card_key_stub ( const char *serialnostr,
     size_t an;
 
     fingerprint_from_pk (pk, afp, &an);
-    memset (afp, 0, MAX_FINGERPRINT_LEN);
+    if (an < MAX_FINGERPRINT_LEN)
+      memset (afp+an, 0, MAX_FINGERPRINT_LEN-an);
     rc = keydb_search_fpr (hd, afp);
   }
 
@@ -2373,4 +2460,3 @@ auto_create_card_key_stub ( const char *serialnostr,
   keydb_release (hd);
   return rc;
 }
-