* keyserver.c (keyserver_spawn): Use the full 64-bit keyid in the INFO
[gnupg.git] / g10 / import.c
index c5bcb59..60b8ff2 100644 (file)
@@ -1,6 +1,6 @@
 /* import.c
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- *               Free Software Foundation, Inc.
+ *               2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -84,19 +84,20 @@ 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)
+parse_import_options(char *str,unsigned int *options,int noisy)
 {
   struct parse_options import_opts[]=
     {
-      {"allow-local-sigs",IMPORT_ALLOW_LOCAL_SIGS},
-      {"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}
+      {"allow-local-sigs",IMPORT_ALLOW_LOCAL_SIGS,NULL},
+      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL},
+      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL},
+      {"fast-import",IMPORT_FAST_IMPORT,NULL},
+      {"convert-sk-to-pk",IMPORT_SK2PK,NULL},
+      {"merge-only",IMPORT_MERGE_ONLY,NULL},
+      {NULL,0,NULL}
     };
 
-  return parse_options(str,options,import_opts);
+  return parse_options(str,options,import_opts,noisy);
 }
 
 void *
@@ -508,7 +509,7 @@ print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
   write_status_text (STATUS_IMPORT_OK, buf);
 }
 
-void
+static void
 print_import_check (PKT_public_key * pk, PKT_user_id * id)
 {
     char * buf;
@@ -530,6 +531,114 @@ print_import_check (PKT_public_key * pk, PKT_user_id * id)
     m_free (buf);
 }
 
+static void
+check_prefs(KBNODE keyblock)
+{
+  KBNODE node;
+  u32 keyid[2];
+  int problem=0;
+  
+  merge_keys_and_selfsig(keyblock);
+  keyid_from_pk(keyblock->pkt->pkt.public_key,keyid);
+
+  for(node=keyblock;node;node=node->next)
+    {
+      if(node->pkt->pkttype==PKT_USER_ID
+        && node->pkt->pkt.user_id->created
+        && node->pkt->pkt.user_id->prefs)
+       {
+         PKT_user_id *uid=node->pkt->pkt.user_id;
+         prefitem_t *prefs=uid->prefs;
+         char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+
+         for(;prefs->type;prefs++)
+           {
+             char num[10]; /* prefs->value is a byte, so we're over
+                              safe here */
+
+             sprintf(num,"%u",prefs->value);
+
+             if(prefs->type==PREFTYPE_SYM)
+               {
+                 if(check_cipher_algo(prefs->value))
+                   {
+                     const char *algo=cipher_algo_to_string(prefs->value);
+                     if(!problem)
+                       log_info(_("WARNING: key %08lX contains preferences"
+                                  " for unavailable algorithms:\n"),
+                                (ulong)keyid[1]);
+                     log_info(_("         \"%s\": preference for cipher"
+                                " algorithm %s\n"),user,algo?algo:num);
+                     problem=1;
+                   }
+               }
+             else if(prefs->type==PREFTYPE_HASH)
+               {
+                 if(check_digest_algo(prefs->value))
+                   {
+                     const char *algo=digest_algo_to_string(prefs->value);
+                     if(!problem)
+                       log_info(_("WARNING: key %08lX contains preferences"
+                                  " for unavailable algorithms:\n"),
+                                (ulong)keyid[1]);
+                     log_info(_("         \"%s\": preference for digest"
+                                " algorithm %s\n"),user,algo?algo:num);
+                     problem=1;
+                   }
+               }
+             else if(prefs->type==PREFTYPE_ZIP)
+               {
+                 if(check_compress_algo(prefs->value))
+                   {
+                     const char *algo=compress_algo_to_string(prefs->value);
+                     if(!problem)
+                       log_info(_("WARNING: key %08lX contains preferences"
+                                  " for unavailable algorithms:\n"),
+                                (ulong)keyid[1]);
+                     log_info(_("         \"%s\": preference for compression"
+                                " algorithm %s\n"),user,algo?algo:num);
+                     problem=1;
+                   }
+               }
+           }
+
+         m_free(user);
+       }
+    }
+
+  if(problem)
+    {
+      log_info(_("it is strongly suggested that you update"
+                " your preferences and\n"));
+      log_info(_("re-distribute this key to avoid potential algorithm"
+                " mismatch problems\n"));
+
+      if(!opt.batch)
+       {
+         STRLIST sl=NULL,locusr=NULL;
+         size_t fprlen=0;
+         byte fpr[MAX_FINGERPRINT_LEN],*p;
+         char username[(MAX_FINGERPRINT_LEN*2)+1];
+         unsigned int i;
+
+         p=fingerprint_from_pk(keyblock->pkt->pkt.public_key,fpr,&fprlen);
+         for(i=0;i<fprlen;i++,p++)
+           sprintf(username+2*i,"%02X",*p);
+         add_to_strlist(&locusr,username);
+
+         append_to_strlist(&sl,"updpref");
+         append_to_strlist(&sl,"save");
+
+         keyedit_menu( username, locusr, sl, 1, 1 );
+         free_strlist(sl);
+         free_strlist(locusr);
+       }
+      else if(!opt.quiet)
+       log_info(_("you can update your preferences with:"
+                  " gpg --edit-key %08lX updpref save\n"),(ulong)keyid[1]);
+    }
+}
+
 /****************
  * 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
@@ -559,10 +668,6 @@ import_one( const char *fname, KBNODE keyblock,
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if(pk->pubkey_algo==PUBKEY_ALGO_ELGAMAL)
-      log_info(_("key %08lX: Elgamal primary key -"
-                " this may take some time to import\n"),(ulong)keyid[1]);
-
     if( opt.verbose && !opt.interactive ) {
        log_info( "pub  %4u%c/%08lX %s   ",
                  nbits_from_pk( pk ),
@@ -629,12 +734,13 @@ import_one( const char *fname, KBNODE keyblock,
        log_error( _("key %08lX: public key not found: %s\n"),
                                (ulong)keyid[1], g10_errstr(rc));
     }
-    else if ( rc && opt.merge_only ) {
+    else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) )
+      {
        if( opt.verbose )
-           log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] );
+         log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] );
        rc = 0;
        stats->skipped_new_keys++;
-    }
+      }
     else if( rc ) { /* insert this key */
         KEYDB_HANDLE hd = keydb_new (0);
 
@@ -771,27 +877,47 @@ import_one( const char *fname, KBNODE keyblock,
                  print_import_ok (pk, NULL,
                                   ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
        }
-       else {
-             if (is_status_enabled ()) 
-                  print_import_ok (pk, NULL, 0);
-       
-           if( !opt.quiet ) {
-               char *p=get_user_id_printable(keyid);
+       else
+         {
+           if (is_status_enabled ()) 
+             print_import_ok (pk, NULL, 0);
+
+           if( !opt.quiet )
+             {
+               char *p=get_user_id_printable(keyid);
                log_info( _("key %08lX: \"%s\" not changed\n"),
                          (ulong)keyid[1],p);
                m_free(p);
-           }
+             }
+
            stats->unchanged++;
-       }
+         }
+
         keydb_release (hd); hd = NULL;
     }
 
   leave:
+
+    /* Now that the key is definitely incorporated into the keydb, we
+       need to check if a designated revocation is present or if the
+       prefs are not rational so we can warn the user. */
+
+    if(mod_key)
+      {
+       revocation_present(keyblock_orig);
+       if(seckey_available(keyid)==0)
+         check_prefs(keyblock_orig);
+      }
+    else if(new_key)
+      {
+       revocation_present(keyblock);
+       if(seckey_available(keyid)==0)
+         check_prefs(keyblock);
+      }
+
     release_kbnode( keyblock_orig );
     free_public_key( pk_orig );
 
-    revocation_present(keyblock);
-
     return rc;
 }
 
@@ -830,7 +956,12 @@ sec_to_pub_keyblock(KBNODE sec_keyblock)
 
          n=pubkey_get_npkey(pk->pubkey_algo);
          if(n==0)
-           pk->pkey[0]=mpi_copy(sk->skey[0]);
+           {
+             /* we can't properly extract the pubkey without knowing
+                the number of MPIs */
+             release_kbnode(pub_keyblock);
+             return NULL;
+           }
          else
            {
              int i;
@@ -907,38 +1038,52 @@ import_secret_one( const char *fname, KBNODE 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( rc == G10ERR_NO_SECKEY && !(opt.import_options&IMPORT_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));
-            keydb_release (hd);
-           return G10ERR_GENERAL;
+         log_error (_("no default secret keyring: %s\n"), g10_errstr (rc));
+         keydb_release (hd);
+         return G10ERR_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) );
+         log_error (_("error writing keyring `%s': %s\n"),
+                    keydb_get_resource_name (hd), g10_errstr(rc) );
         keydb_release (hd);
        /* we are ready */
        if( !opt.quiet )
-           log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]);
+         log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]);
        stats->secret_imported++;
         if (is_status_enabled ()) 
-             print_import_ok (NULL, sk, 1|16);
+         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);
+           if(pub_keyblock)
+             {
+               import_one(fname,pub_keyblock,stats,opt.import_options);
+               release_kbnode(pub_keyblock);
+             }
          }
 
-    }
+       /* Now that the key is definitely incorporated into the keydb,
+          if we have the public part of this key, we need to check if
+          the prefs are rational. */
+       node=get_pubkeyblock(keyid);
+       if(node)
+         {
+           check_prefs(node);
+           release_kbnode(node);
+         }
+      }
     else if( !rc ) { /* we can't merge secret keys */
        log_error( _("key %08lX: already in secret keyring\n"),
                                                        (ulong)keyid[1]);
@@ -1458,8 +1603,8 @@ collapse_uids( KBNODE *keyblock )
 }
 
 /* Check for a 0x20 revocation from a revocation key that is not
-   present.  This gets called without the benefit of merge_xxxx so you
-   can't rely on pk->revkey and friends. */
+   present.  This may be called without the benefit of merge_xxxx so
+   you can't rely on pk->revkey and friends. */
 static void
 revocation_present(KBNODE keyblock)
 {