Fixes pertaining to revocation creation with subkey-only exported card keys
[gnupg.git] / g10 / pkclist.c
index 671b568..6558f0d 100644 (file)
@@ -1,5 +1,6 @@
 /* pkclist.c
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ *               2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,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 "trustdb.h"
 #include "ttyio.h"
 #include "status.h"
+#include "photoid.h"
 #include "i18n.h"
 
-
 #define CONTROL_D ('D' - 'A' + 1)
 
-
 /****************
  * Show the revocation reason as it is stored with the given signature
  */
@@ -156,74 +157,6 @@ show_revocation_reason( PKT_public_key *pk, int mode )
 }
 
 
-static void
-show_paths (const PKT_public_key *pk, int only_first )
-{
-    log_debug("not yet implemented\n");
-#if 0    
-    void *context = NULL;
-    unsigned otrust, validity;
-    int last_level, level;
-
-    last_level = 0;
-    while( (level=enum_cert_paths( &context, &lid, &otrust, &validity)) != -1){
-       char *p;
-       int c, rc;
-       size_t n;
-       u32 keyid[2];
-       PKT_public_key *pk ;
-
-       if( level < last_level && only_first )
-           break;
-       last_level = level;
-
-       rc = keyid_from_lid( lid, keyid );
-
-       if( rc ) {
-           log_error("ooops: can't get keyid for lid %lu\n", lid);
-           return;
-       }
-
-       pk = m_alloc_clear( sizeof *pk );
-       rc = get_pubkey( pk, keyid );
-       if( rc ) {
-           log_error("key %08lX: public key not found: %s\n",
-                                   (ulong)keyid[1], g10_errstr(rc) );
-           return;
-       }
-
-       tty_printf("%*s%4u%c/%08lX.%lu %s \"",
-                 level*2, "",
-                 nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ),
-                 (ulong)keyid[1], lid, datestr_from_pk( pk ) );
-
-       c = trust_letter(otrust);
-       if( c )
-           putchar( c );
-       else
-           printf( "%02x", otrust );
-       putchar('/');
-       c = trust_letter(validity);
-       if( c )
-           putchar( c );
-       else
-           printf( "%02x", validity );
-       putchar(' ');
-
-       p = get_user_id( keyid, &n );
-       tty_print_utf8_string( p, n ),
-       m_free(p);
-       tty_printf("\"\n");
-       free_public_key( pk );
-    }
-    enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */
-#endif
-    tty_printf("\n");
-}
-
-
-
-
 /****************
  * mode: 0 = standard
  *       1 = Without key info and additional menu option 'm'
@@ -239,52 +172,126 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
                     unsigned *new_trust, int defer_help )
 {
   char *p;
-  size_t n;
   u32 keyid[2];
   int changed=0;
   int quit=0;
   int show=0;
+  int min_num;
   int did_help=defer_help;
+  unsigned int minimum=get_min_ownertrust(pk);
+
+  switch(minimum)
+    {
+    default:              min_num=0; break;
+    case TRUST_UNDEFINED: min_num=1; break;
+    case TRUST_NEVER:     min_num=2; break;
+    case TRUST_MARGINAL:  min_num=3; break;
+    case TRUST_FULLY:     min_num=4; break;
+    }
 
   keyid_from_pk (pk, keyid);
   for(;;) {
-    /* a string with valid answers */
+    /* A string with valid answers.
+
+       Note to translators: These are the allowed answers in lower and
+       uppercase.  Below you will find the matching strings which
+       should be translated accordingly and the letter changed to
+       match the one in the answer string.
+    
+         i = please show me more information
+         m = back to the main menu
+         s = skip this key
+        q = quit
+    */
     const char *ans = _("iImMqQsS");
 
     if( !did_help ) 
       {
         if( !mode ) 
           {
-            tty_printf(_("No trust value assigned to:\n"
-                         "%4u%c/%08lX %s \""),
-                       nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ),
-                       (ulong)keyid[1], datestr_from_pk( pk ) );
-            p = get_user_id( keyid, &n );
-            tty_print_utf8_string( p, n ),
-              m_free(p);
-            tty_printf("\"\n");
+            KBNODE keyblock, un;
+
+            tty_printf(_("No trust value assigned to:\n"));
+           tty_printf("%4u%c/%s %s\n",nbits_from_pk( pk ),
+                      pubkey_letter( pk->pubkey_algo ),
+                       keystr(keyid), datestr_from_pk( pk ) );
+           p=get_user_id_native(keyid);
+           tty_printf(_("      \"%s\"\n"),p);
+           xfree(p);
+
+            keyblock = get_pubkeyblock (keyid);
+            if (!keyblock)
+                BUG ();
+            for (un=keyblock; un; un = un->next)
+             {
+                if (un->pkt->pkttype != PKT_USER_ID )
+                 continue;
+                if (un->pkt->pkt.user_id->is_revoked )
+                 continue;
+                if (un->pkt->pkt.user_id->is_expired )
+                 continue;
+               /* Only skip textual primaries */
+                if (un->pkt->pkt.user_id->is_primary
+                   && !un->pkt->pkt.user_id->attrib_data )
+                 continue;
+                
+               if((opt.verify_options&VERIFY_SHOW_PHOTOS)
+                  && un->pkt->pkt.user_id->attrib_data)
+                 show_photos(un->pkt->pkt.user_id->attribs,
+                             un->pkt->pkt.user_id->numattribs,pk,NULL);
+
+               p=utf8_to_native(un->pkt->pkt.user_id->name,
+                                un->pkt->pkt.user_id->len,0);
+
+               tty_printf(_("  aka \"%s\"\n"),p);
+             }
+        
             print_fingerprint (pk, NULL, 2);
             tty_printf("\n");
+           release_kbnode (keyblock);
           }
-        tty_printf (_(
-                     "Please decide how far you trust this user to correctly\n"
-                     "verify other users' keys (by looking at passports,\n"
-                     "checking fingerprints from different sources...)?\n\n"));
-        tty_printf (_(" %d = Don't know\n"), 1);
-        tty_printf (_(" %d = I do NOT trust\n"), 2);
-        tty_printf (_(" %d = I trust marginally\n"), 3);
-        tty_printf (_(" %d = I trust fully\n"), 4);
+
+       if(opt.trust_model==TM_DIRECT)
+         {
+           tty_printf(_("How much do you trust that this key actually "
+                        "belongs to the named user?\n"));
+           tty_printf("\n");
+         }
+       else
+         {
+           /* This string also used in keyedit.c:trustsig_prompt */
+           tty_printf(_("Please decide how far you trust this user to"
+                        " correctly verify other users' keys\n"
+                        "(by looking at passports, checking fingerprints from"
+                        " different sources, etc.)\n"));
+           tty_printf("\n");
+         }
+
+       if(min_num<=1)
+         tty_printf (_("  %d = I don't know or won't say\n"), 1);
+       if(min_num<=2)
+         tty_printf (_("  %d = I do NOT trust\n"), 2);
+       if(min_num<=3)
+         tty_printf (_("  %d = I trust marginally\n"), 3);
+       if(min_num<=4)
+         tty_printf (_("  %d = I trust fully\n"), 4);
         if (mode)
-          tty_printf (_(" %d = I trust ultimately\n"), 5);
-        tty_printf (_(" i = please show me more information\n") );
+          tty_printf (_("  %d = I trust ultimately\n"), 5);
+#if 0
+       /* not yet implemented */
+        tty_printf ("  i = please show me more information\n");
+#endif
         if( mode )
-          tty_printf(_(" m = back to the main menu\n"));
+          tty_printf(_("  m = back to the main menu\n"));
         else
          {
-           tty_printf(_(" s = skip this key\n"));
-           tty_printf(_(" q = quit\n"));
+           tty_printf(_("  s = skip this key\n"));
+           tty_printf(_("  q = quit\n"));
          }
         tty_printf("\n");
+       if(minimum)
+         tty_printf(_("The minimum trust level for this key is: %s\n\n"),
+                    trust_value_to_string(minimum));
         did_help = 1;
       }
     if( strlen(ans) != 8 )
@@ -296,7 +303,7 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
       did_help = 0;
     else if( *p && p[1] )
       ;
-    else if( !p[1] && (*p >= '1' && *p <= (mode?'5':'4')) ) 
+    else if( !p[1] && ((*p >= '0'+min_num) && *p <= (mode?'5':'4')) ) 
       {
         unsigned int trust;
         switch( *p )
@@ -311,7 +318,7 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
         if (trust == TRUST_ULTIMATE
             && !cpr_get_answer_is_yes ("edit_ownertrust.set_ultimate.okay",
                                        _("Do you really want to set this key"
-                                         " to ultimate trust? ")))
+                                         " to ultimate trust? (y/N) ")))
           ; /* no */
         else
           {
@@ -320,12 +327,15 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
             break;
           }
       }
+#if 0
+    /* not yet implemented */
     else if( *p == ans[0] || *p == ans[1] ) 
       {
         tty_printf(_("Certificates leading to an ultimately trusted key:\n"));
         show = 1;
         break;
       }
+#endif
     else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) 
       {
         break ; /* back to the menu */
@@ -339,9 +349,9 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
         quit = 1;
         break ; /* back to the menu */
       }
-    m_free(p); p = NULL;
+    xfree(p); p = NULL;
   }
-  m_free(p);
+  xfree(p);
   return show? -2: quit? -1 : changed;
 }
 
@@ -363,7 +373,6 @@ edit_ownertrust (PKT_public_key *pk, int mode )
         case -1: /* quit */
           return -1;
         case -2: /* show info */
-          show_paths(pk, 1);
           no_help = 1;
           break;
         case 1: /* trust value set */
@@ -383,94 +392,54 @@ edit_ownertrust (PKT_public_key *pk, int mode )
  * Returns: true if we trust.
  */
 static int
-do_we_trust( PKT_public_key *pk, unsigned int *trustlevel )
+do_we_trust( PKT_public_key *pk, unsigned int trustlevel )
 {
-    unsigned int trustmask = 0;
-
-    /* FIXME: get_pubkey_byname already checks the validity and won't
-     * return keys which are either expired or revoked - so these
-     * question here won't get triggered.  We have to find a solution
-     * for this.  It might make sense to have a function in getkey.c
-     * which does only the basic checks and returns even revoked and
-     * expired keys.  This fnction could then also returhn a list of
-     * keys if the speicified name is ambiguous
-     */
-    if( (*trustlevel & TRUST_FLAG_REVOKED) ) {
-       log_info(_("key %08lX: key has been revoked!\n"),
-                                       (ulong)keyid_from_pk( pk, NULL) );
-       show_revocation_reason( pk, 0 );
-       if( opt.batch )
-          return 0; /* no */
-
-       if( !cpr_get_answer_is_yes("revoked_key.override",
-                                   _("Use this key anyway? ")) )
-          return 0; /* no */
-       trustmask |= TRUST_FLAG_REVOKED;
+  /* We should not be able to get here with a revoked or expired
+     key */
+  if(trustlevel & TRUST_FLAG_REVOKED
+     || trustlevel & TRUST_FLAG_SUB_REVOKED
+     || (trustlevel & TRUST_MASK) == TRUST_EXPIRED)
+    BUG();
+
+  if( opt.trust_model==TM_ALWAYS )
+    {
+      if( opt.verbose )
+       log_info("No trust check due to `--trust-model always' option\n");
+      return 1;
     }
-    if( (*trustlevel & TRUST_FLAG_SUB_REVOKED) ) {
-       log_info(_("key %08lX: subkey has been revoked!\n"),
-                                       (ulong)keyid_from_pk( pk, NULL) );
-       show_revocation_reason( pk, 0 );
-       if( opt.batch )
-           return 0;
 
-       if( !cpr_get_answer_is_yes("revoked_key.override",
-                                   _("Use this key anyway? ")) )
-           return 0;
-       trustmask |= TRUST_FLAG_SUB_REVOKED;
-    }
-    *trustlevel &= ~trustmask;
+  switch(trustlevel & TRUST_MASK)
+    {
+    default:
+      log_error ("invalid trustlevel %u returned from validation layer\n",
+                trustlevel);
+      /* fall thru */
+    case TRUST_UNKNOWN: 
+    case TRUST_UNDEFINED:
+      log_info(_("%s: There is no assurance this key belongs"
+                " to the named user\n"),keystr_from_pk(pk));
+      return 0; /* no */
 
-    if( opt.always_trust) {
-       if( opt.verbose )
-           log_info("No trust check due to --always-trust option\n");
-       return 1;
-    }
+    case TRUST_MARGINAL:
+      log_info(_("%s: There is limited assurance this key belongs"
+                " to the named user\n"),keystr_from_pk(pk));
+      return 1; /* yes */
+
+    case TRUST_FULLY:
+      if( opt.verbose )
+       log_info(_("This key probably belongs to the named user\n"));
+      return 1; /* yes */
 
-    switch( (*trustlevel & TRUST_MASK) ) {
-      case TRUST_EXPIRED:
-       log_info(_("%08lX: key has expired\n"),
-                                   (ulong)keyid_from_pk( pk, NULL) );
-       return 0; /* no */
-
-      default:
-         log_error ("invalid trustlevel %u returned from validation layer\n",
-                    *trustlevel);
-         /* fall thru */
-      case TRUST_UNKNOWN: 
-      case TRUST_UNDEFINED:
-        log_info(_("%08lX: There is no indication that this key "
-                   "really belongs to the owner\n"),
-                 (ulong)keyid_from_pk( pk, NULL) );
-       return 0; /* no */
-
-      case TRUST_NEVER:
-       log_info(_("%08lX: We do NOT trust this key\n"),
-                                       (ulong)keyid_from_pk( pk, NULL) );
-       return 0; /* no */
-
-      case TRUST_MARGINAL:
-       log_info(
-       _("%08lX: It is not sure that this key really belongs to the owner\n"
-        "but it is accepted anyway\n"), (ulong)keyid_from_pk( pk, NULL) );
-       return 1; /* yes */
-
-      case TRUST_FULLY:
-       if( opt.verbose )
-           log_info(_("This key probably belongs to the owner\n"));
-       return 1; /* yes */
-
-      case TRUST_ULTIMATE:
-       if( opt.verbose )
-           log_info(_("This key belongs to us\n"));
-       return 1; /* yes */
+    case TRUST_ULTIMATE:
+      if( opt.verbose )
+       log_info(_("This key belongs to us\n"));
+      return 1; /* yes */
     }
 
-    return 1; /* yes */
+  return 1; /* yes */
 }
 
 
-
 /****************
  * wrapper around do_we_trust, so we can ask whether to use the
  * key anyway.
@@ -478,52 +447,34 @@ do_we_trust( PKT_public_key *pk, unsigned int *trustlevel )
 static int
 do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel )
 {
-    int rc;
+  int rc;
 
-    rc = do_we_trust( pk, &trustlevel );
+  rc = do_we_trust( pk, trustlevel );
 
-    if( (trustlevel & TRUST_FLAG_REVOKED) && !rc )
-       return 0;
-    if( (trustlevel & TRUST_FLAG_SUB_REVOKED) && !rc )
-       return 0;
+  if( !opt.batch && !rc )
+    {
+      print_pubkey_info(NULL,pk);
+      print_fingerprint (pk, NULL, 2);
+      tty_printf("\n");
 
-    if( !opt.batch && !rc ) {
-       char *p;
-       u32 keyid[2];
-       size_t n;
-
-       keyid_from_pk( pk, keyid);
-       tty_printf( "%4u%c/%08lX %s \"",
-                 nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ),
-                 (ulong)keyid[1], datestr_from_pk( pk ) );
-       p = get_user_id( keyid, &n );
-       tty_print_utf8_string( p, n ),
-       m_free(p);
-       tty_printf("\"\n");
-        print_fingerprint (pk, NULL, 2);
-       tty_printf("\n");
-
-       tty_printf(_(
-"It is NOT certain that the key belongs to its owner.\n"
-"If you *really* know what you are doing, you may answer\n"
-"the next question with yes\n\n") );
-
-       if( cpr_get_answer_is_yes("untrusted_key.override",
-                                 _("Use this key anyway? "))  )
-           rc = 1;
-
-       /* Hmmm: Should we set a flag to tell the user about
-        *       his decision the next time he encrypts for this recipient?
-        */
-    }
-    else if( opt.always_trust && !rc ) {
-       if( !opt.quiet )
-           log_info(_("WARNING: Using untrusted key!\n"));
+      tty_printf(
+              _("It is NOT certain that the key belongs to the person named\n"
+                "in the user ID.  If you *really* know what you are doing,\n"
+                "you may answer the next question with yes.\n"));
+
+      tty_printf("\n");
+
+      if( cpr_get_answer_is_yes("untrusted_key.override",
+                               _("Use this key anyway? (y/N) "))  )
        rc = 1;
+
+      /* Hmmm: Should we set a flag to tell the user about
+       *        his decision the next time he encrypts for this recipient?
+       */
     }
-    return rc;
-}
 
+  return rc;
+}
 
 
 /****************
@@ -533,11 +484,19 @@ do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel )
 int
 check_signatures_trust( PKT_signature *sig )
 {
-  PKT_public_key *pk = m_alloc_clear( sizeof *pk );
+  PKT_public_key *pk = xmalloc_clear( sizeof *pk );
   unsigned int trustlevel;
   int rc=0;
 
-  if ( opt.always_trust)
+  rc = get_pubkey( pk, sig->keyid );
+  if (rc) 
+    { /* this should not happen */
+      log_error("Ooops; the key vanished  - can't check the trust\n");
+      rc = G10ERR_NO_PUBKEY;
+      goto leave;
+    }
+
+  if ( opt.trust_model==TM_ALWAYS )
     {
       if( !opt.quiet )
         log_info(_("WARNING: Using untrusted key!\n"));
@@ -546,21 +505,21 @@ check_signatures_trust( PKT_signature *sig )
       goto leave;
     }
 
-  rc = get_pubkey( pk, sig->keyid );
-  if (rc) 
-    { /* this should not happen */
-      log_error("Ooops; the key vanished  - can't check the trust\n");
-      rc = G10ERR_NO_PUBKEY;
-      goto leave;
-    }
+  if(pk->maybe_revoked && !pk->is_revoked)
+    log_info(_("WARNING: this key might be revoked (revocation key"
+              " not present)\n"));
 
   trustlevel = get_validity (pk, NULL);
 
   if ( (trustlevel & TRUST_FLAG_REVOKED) ) 
     {
       write_status( STATUS_KEYREVOKED );
-      log_info(_("WARNING: This key has been revoked by its owner!\n"));
-      log_info(_("         This could mean that the signature is forgery.\n"));
+      if(pk->is_revoked==2)
+       log_info(_("WARNING: This key has been revoked by its"
+                  " designated revoker!\n"));
+      else
+       log_info(_("WARNING: This key has been revoked by its owner!\n"));
+      log_info(_("         This could mean that the signature is forged.\n"));
       show_revocation_reason( pk, 0 );
     }
   else if ((trustlevel & TRUST_FLAG_SUB_REVOKED) ) 
@@ -573,6 +532,55 @@ check_signatures_trust( PKT_signature *sig )
   if ((trustlevel & TRUST_FLAG_DISABLED))
     log_info (_("Note: This key has been disabled.\n"));
 
+  /* If we have PKA information adjust the trustlevel. */
+  if (sig->pka_info && sig->pka_info->valid)
+    {
+      unsigned char fpr[MAX_FINGERPRINT_LEN];
+      PKT_public_key *primary_pk;
+      size_t fprlen;
+      int okay;
+
+
+      primary_pk = xmalloc_clear (sizeof *primary_pk);
+      get_pubkey (primary_pk, pk->main_keyid);
+      fingerprint_from_pk (primary_pk, fpr, &fprlen);
+      free_public_key (primary_pk);
+
+      if ( fprlen == 20 && !memcmp (sig->pka_info->fpr, fpr, 20) )
+        {
+          okay = 1;
+          log_info (_("Note: Verified signer's address is `%s'\n"),
+                    sig->pka_info->email);
+        }
+      else
+        {
+          okay = 0;
+          log_info (_("Note: Signer's address `%s' "
+                      "does not match DNS entry\n"), sig->pka_info->email);
+        }
+
+      switch ( (trustlevel & TRUST_MASK) ) 
+        {
+        case TRUST_UNKNOWN: 
+        case TRUST_UNDEFINED:
+        case TRUST_MARGINAL:
+          if (okay)
+            {
+              trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_FULLY);
+              log_info ("trustlevel adjusted to FULL due to valid PKA info\n");
+            }
+          /* (fall through) */
+        case TRUST_FULLY:
+          if (!okay)
+            {
+              trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_NEVER);
+              log_info ("trustlevel adjusted to NEVER due to bad PKA info\n");
+            }
+          break;
+        }
+    }
+
+  /* Now let the user know what up with the trustlevel. */
   switch ( (trustlevel & TRUST_MASK) ) 
     {
     case TRUST_EXPIRED:
@@ -640,7 +648,7 @@ release_pk_list( PK_LIST pk_list )
     for( ; pk_list; pk_list = pk_rover ) {
        pk_rover = pk_list->next;
        free_public_key( pk_list->pk );
-       m_free( pk_list );
+       xfree( pk_list );
     }
 }
 
@@ -669,10 +677,10 @@ default_recipient(void)
     int i;
 
     if( opt.def_recipient )
-       return m_strdup( opt.def_recipient );
+       return xstrdup( opt.def_recipient );
     if( !opt.def_recipient_self )
        return NULL;
-    sk = m_alloc_clear( sizeof *sk );
+    sk = xmalloc_clear( sizeof *sk );
     i = get_seckey_byname( sk, NULL, 0 );
     if( i ) {
        free_secret_key( sk );
@@ -681,7 +689,7 @@ default_recipient(void)
     n = MAX_FINGERPRINT_LEN;
     fingerprint_from_sk( sk, fpr, &n );
     free_secret_key( sk );
-    p = m_alloc( 2*n+3 );
+    p = xmalloc( 2*n+3 );
     *p++ = '0';
     *p++ = 'x';
     for(i=0; i < n; i++ )
@@ -755,15 +763,28 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
      * list of the encrypt-to ones (we always trust them) */
     for( rov = remusr; rov; rov = rov->next ) {
        if( !(rov->flags & 1) )
+         {
            any_recipients = 1;
+
+           if((rov->flags&2) && (PGP2 || PGP6 || PGP7 || PGP8))
+             {
+               log_info(_("you may not use %s while in %s mode\n"),
+                        "--hidden-recipient",
+                        compliance_option_string());
+
+               compliance_failure();
+             }
+         }
        else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) {
-           pk = m_alloc_clear( sizeof *pk );
+           pk = xmalloc_clear( sizeof *pk );
            pk->req_usage = use;
-           if( (rc = get_pubkey_byname( pk, rov->d, NULL, NULL )) ) {
+           /* We can encrypt-to a disabled key */
+           if( (rc = get_pubkey_byname( pk, rov->d, NULL, NULL, 1 )) ) {
                free_public_key( pk ); pk = NULL;
                log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
                 write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
                                               rov->d, strlen (rov->d), -1);
+               goto fail;
             }
            else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) {
                /* Skip the actual key if the key is already present
@@ -775,11 +796,20 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                }
                else {
                    PK_LIST r;
-                   r = m_alloc( sizeof *r );
+                   r = xmalloc( sizeof *r );
                    r->pk = pk; pk = NULL;
                    r->next = pk_list;
-                   r->mark = 0;
+                   r->flags = (rov->flags&2)?1:0;
                    pk_list = r;
+
+                   if(r->flags&1 && (PGP2 || PGP6 || PGP7 || PGP8))
+                     {
+                       log_info(_("you may not use %s while in %s mode\n"),
+                                "--hidden-encrypt-to",
+                                compliance_option_string());
+
+                       compliance_failure();
+                     }
                }
            }
            else {
@@ -787,6 +817,7 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
                 write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
                                               rov->d, strlen (rov->d), -1);
+               goto fail;
            }
        }
     }
@@ -796,6 +827,8 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
        char *answer=NULL;
        STRLIST backlog=NULL;
 
+       if(pk_list)
+         any_recipients = 1;
        def_rec = default_recipient();
        have_def_rec = !!def_rec;
        if( !have_def_rec )
@@ -803,7 +836,7 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                "You did not specify a user ID. (you may use \"-r\")\n"));
        for(;;) {
            rc = 0;
-           m_free(answer);
+           xfree(answer);
            if( have_def_rec ) {
                answer = def_rec;
                def_rec = NULL;
@@ -811,23 +844,52 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
            else if(backlog) {
              answer=pop_strlist(&backlog);
            }
-           else {
+           else
+             {
+               PK_LIST iter;
+
+               tty_printf("\n");
+               tty_printf(_("Current recipients:\n"));
+               for(iter=pk_list;iter;iter=iter->next)
+                 {
+                   u32 keyid[2];
+
+                   keyid_from_pk(iter->pk,keyid);
+                   tty_printf("%4u%c/%s %s \"",
+                              nbits_from_pk(iter->pk),
+                              pubkey_letter(iter->pk->pubkey_algo),
+                              keystr(keyid),
+                              datestr_from_pk(iter->pk));
+
+                   if(iter->pk->user_id)
+                     tty_print_utf8_string(iter->pk->user_id->name,
+                                           iter->pk->user_id->len);
+                   else
+                     {
+                       size_t n;
+                       char *p = get_user_id( keyid, &n );
+                       tty_print_utf8_string( p, n );
+                       xfree(p);
+                     }
+                   tty_printf("\"\n");
+                 }
+
                answer = cpr_get_utf8("pklist.user_id.enter",
                         _("\nEnter the user ID.  End with an empty line: "));
                trim_spaces(answer);
                cpr_kill_prompt();
-           }
+             }
            if( !answer || !*answer ) {
-               m_free(answer);
+               xfree(answer);
                break;
            }
            if(expand_id(answer,&backlog,0))
              continue;
            if( pk )
                free_public_key( pk );
-           pk = m_alloc_clear( sizeof *pk );
+           pk = xmalloc_clear( sizeof *pk );
            pk->req_usage = use;
-           rc = get_pubkey_byname( pk, answer, NULL, NULL );
+           rc = get_pubkey_byname( pk, answer, NULL, NULL, 0 );
            if( rc )
                tty_printf(_("No such user ID.\n"));
            else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) {
@@ -838,10 +900,10 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                                   "already set as default recipient\n") );
                    }
                    else {
-                       PK_LIST r = m_alloc( sizeof *r );
+                       PK_LIST r = xmalloc( sizeof *r );
                        r->pk = pk; pk = NULL;
                        r->next = pk_list;
-                       r->mark = 0;
+                       r->flags = 0; /* no throwing default ids */
                        pk_list = r;
                    }
                    any_recipients = 1;
@@ -849,8 +911,8 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                }
                else {
                    int trustlevel;
-
-                   trustlevel = get_validity (pk, NULL);
+                   
+                   trustlevel = get_validity (pk, pk->user_id);
                    if( (trustlevel & TRUST_FLAG_DISABLED) ) {
                        tty_printf(_("Public key is disabled.\n") );
                    }
@@ -863,25 +925,10 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                        }
                        else {
                            PK_LIST r;
-                           char *p;
-                           size_t n;
-                           u32 keyid[2];
-
-                           keyid_from_pk( pk, keyid);
-                           tty_printf("Added %4u%c/%08lX %s \"",
-                                      nbits_from_pk( pk ),
-                                      pubkey_letter( pk->pubkey_algo ),
-                                      (ulong)keyid[1],
-                                      datestr_from_pk( pk ) );
-                           p = get_user_id( keyid, &n );
-                           tty_print_utf8_string( p, n );
-                           m_free(p);
-                           tty_printf("\"\n");
-
-                           r = m_alloc( sizeof *r );
+                           r = xmalloc( sizeof *r );
                            r->pk = pk; pk = NULL;
                            r->next = pk_list;
-                           r->mark = 0;
+                           r->flags = 0; /* no throwing interactive ids */
                            pk_list = r;
                        }
                        any_recipients = 1;
@@ -889,7 +936,7 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                    }
                }
            }
-           m_free(def_rec); def_rec = NULL;
+           xfree(def_rec); def_rec = NULL;
            have_def_rec = 0;
        }
        if( pk ) {
@@ -898,11 +945,12 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
        }
     }
     else if( !any_recipients && (def_rec = default_recipient()) ) {
-       pk = m_alloc_clear( sizeof *pk );
+       pk = xmalloc_clear( sizeof *pk );
        pk->req_usage = use;
-       rc = get_pubkey_byname( pk, def_rec, NULL, NULL );
+       /* The default recipient may be disabled */
+       rc = get_pubkey_byname( pk, def_rec, NULL, NULL, 1 );
        if( rc )
-           log_error(_("unknown default recipient `%s'\n"), def_rec );
+           log_error(_("unknown default recipient \"%s\"\n"), def_rec );
        else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) {
          /* Mark any_recipients here since the default recipient
              would have been used if it wasn't already there.  It
@@ -912,10 +960,10 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
          if (key_present_in_pk_list(pk_list, pk) == 0)
            log_info(_("skipped: public key already set as default recipient\n"));
          else {
-           PK_LIST r = m_alloc( sizeof *r );
+           PK_LIST r = xmalloc( sizeof *r );
            r->pk = pk; pk = NULL;
            r->next = pk_list;
-           r->mark = 0;
+           r->flags = 0; /* no throwing default ids */
            pk_list = r;
          }
        }
@@ -923,7 +971,7 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
            free_public_key( pk );
            pk = NULL;
        }
-       m_free(def_rec); def_rec = NULL;
+       xfree(def_rec); def_rec = NULL;
     }
     else {
        any_recipients = 0;
@@ -931,19 +979,20 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
            if( (remusr->flags & 1) )
                continue; /* encrypt-to keys are already handled */
 
-           pk = m_alloc_clear( sizeof *pk );
+           pk = xmalloc_clear( sizeof *pk );
            pk->req_usage = use;
-           if( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL )) ) {
+           if( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL, 0 )) ) {
                free_public_key( pk ); pk = NULL;
                log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
                 write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
                                               remusr->d, strlen (remusr->d),
                                               -1);
+               goto fail;
            }
            else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) {
                int trustlevel;
 
-               trustlevel = get_validity (pk, pk->namehash);
+               trustlevel = get_validity (pk, pk->user_id);
                if( (trustlevel & TRUST_FLAG_DISABLED) ) {
                    free_public_key(pk); pk = NULL;
                    log_info(_("%s: skipped: public key is disabled\n"),
@@ -952,6 +1001,8 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                                                   remusr->d,
                                                   strlen (remusr->d),
                                                   -1);
+                   rc=G10ERR_UNU_PUBKEY;
+                   goto fail;
                }
                else if( do_we_trust_pre( pk, trustlevel ) ) {
                    /* note: do_we_trust may have changed the trustlevel */
@@ -969,19 +1020,21 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                    }
                    else {
                        PK_LIST r;
-                       r = m_alloc( sizeof *r );
+                       r = xmalloc( sizeof *r );
                        r->pk = pk; pk = NULL;
                        r->next = pk_list;
-                       r->mark = 0;
+                       r->flags = (remusr->flags&2)?1:0;
                        pk_list = r;
                    }
                }
                else { /* we don't trust this pk */
                    free_public_key( pk ); pk = NULL;
-                    write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
+                    write_status_text_and_buffer (STATUS_INV_RECP, "10 ",
                                                   remusr->d,
                                                   strlen (remusr->d),
                                                   -1);
+                   rc=G10ERR_UNU_PUBKEY;
+                   goto fail;
                }
            }
            else {
@@ -991,6 +1044,7 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
                                               strlen (remusr->d),
                                               -1);
                log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
+               goto fail;
            }
        }
     }
@@ -1001,6 +1055,8 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
        rc = G10ERR_NO_USER_ID;
     }
 
+ fail:
+
     if( rc )
        release_pk_list( pk_list );
     else
@@ -1013,55 +1069,75 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
 
 /* In pgp6 mode, disallow all ciphers except IDEA (1), 3DES (2), and
    CAST5 (3), all hashes except MD5 (1), SHA1 (2), and RIPEMD160 (3),
-   and all compressions except none (0) and ZIP (1).  pgp7 mode
-   expands the cipher list to include AES128 (7), AES192 (8), AES256
-   (9), and TWOFISH (10).  For a true PGP key all of this is unneeded
-   as they are the only items present in the preferences subpacket,
-   but checking here covers the weird case of encrypting to a key that
-   had preferences from a different implementation which was then used
-   with PGP.  I am not completely comfortable with this as the right
-   thing to do, as it slightly alters the list of what the user is
-   supposedly requesting.  It is not against the RFC however, as the
-   preference chosen will never be one that the user didn't specify
-   somewhere ("The implementation may use any mechanism to pick an
-   algorithm in the intersection"), and PGP has no mechanism to fix
-   such a broken preference list, so I'm including it. -dms */
+   and all compressions except none (0) and ZIP (1).  pgp7 and pgp8
+   mode expands the cipher list to include AES128 (7), AES192 (8),
+   AES256 (9), and TWOFISH (10).  pgp8 adds the SHA-256 hash (8).  For
+   a true PGP key all of this is unneeded as they are the only items
+   present in the preferences subpacket, but checking here covers the
+   weird case of encrypting to a key that had preferences from a
+   different implementation which was then used with PGP.  I am not
+   completely comfortable with this as the right thing to do, as it
+   slightly alters the list of what the user is supposedly requesting.
+   It is not against the RFC however, as the preference chosen will
+   never be one that the user didn't specify somewhere ("The
+   implementation may use any mechanism to pick an algorithm in the
+   intersection"), and PGP has no mechanism to fix such a broken
+   preference list, so I'm including it. -dms */
 
-static int
-algo_available( int preftype, int algo, void *hint )
+int
+algo_available( preftype_t preftype, int algo, void *hint )
 {
-    if( preftype == PREFTYPE_SYM ) {
-        if( opt.pgp6 && ( algo != 1 && algo != 2 && algo != 3) )
-         return 0;
+  if( preftype == PREFTYPE_SYM )
+    {
+      if(PGP6 && (algo != CIPHER_ALGO_IDEA
+                 && algo != CIPHER_ALGO_3DES
+                 && algo != CIPHER_ALGO_CAST5))
+       return 0;
+      
+      if(PGP7 && (algo != CIPHER_ALGO_IDEA
+                 && algo != CIPHER_ALGO_3DES
+                 && algo != CIPHER_ALGO_CAST5
+                 && algo != CIPHER_ALGO_AES
+                 && algo != CIPHER_ALGO_AES192
+                 && algo != CIPHER_ALGO_AES256
+                 && algo != CIPHER_ALGO_TWOFISH))
+       return 0;
 
-        if( opt.pgp7 && (algo != 1 && algo != 2 && algo != 3 &&
-                        algo != 7 && algo != 8 && algo != 9 && algo != 10) )
-         return 0;
+      /* PGP8 supports all the ciphers we do.. */
 
-       return algo && !check_cipher_algo( algo );
+      return algo && !check_cipher_algo( algo );
     }
-    else if( preftype == PREFTYPE_HASH ) {
-        int bits=0;
+  else if( preftype == PREFTYPE_HASH )
+    {
+      if(hint && ((*(int *)hint) != md_digest_length(algo)))
+       return 0;
 
-       if(hint)
-         bits=*(int *)hint;
+      if((PGP6 || PGP7) && (algo != DIGEST_ALGO_MD5
+                           && algo != DIGEST_ALGO_SHA1
+                           && algo != DIGEST_ALGO_RMD160))
+       return 0;
 
-       if(bits && (bits != md_digest_length(algo)))
-         return 0;
 
-        if( (opt.pgp6 || opt.pgp7 ) && ( algo != 1 && algo != 2 && algo != 3) )
-         return 0;
+      if(PGP8 && (algo != DIGEST_ALGO_MD5
+                 && algo != DIGEST_ALGO_SHA1
+                 && algo != DIGEST_ALGO_RMD160
+                 && algo != DIGEST_ALGO_SHA256))
+       return 0;
 
-       return algo && !check_digest_algo( algo );
+      return algo && !check_digest_algo( algo );
     }
-    else if( preftype == PREFTYPE_ZIP ) {
-        if ( ( opt.pgp6 || opt.pgp7 ) && ( algo !=0 && algo != 1) )
-         return 0;
+  else if( preftype == PREFTYPE_ZIP )
+    {
+      if((PGP6 || PGP7) && (algo != COMPRESS_ALGO_NONE
+                           && algo != COMPRESS_ALGO_ZIP))
+       return 0;
 
-       return !check_compress_algo( algo );
+      /* PGP8 supports all the compression algos we do */
+
+      return !check_compress_algo( algo );
     }
-    else
-       return 0;
+  else
+    return 0;
 }
 
 
@@ -1088,7 +1164,7 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint)
 
        memset( mask, 0, 8 * sizeof *mask );
        if( preftype == PREFTYPE_SYM ) {
-         if( opt.pgp2 &&
+         if( PGP2 &&
              pkr->pk->version < 4 &&
              pkr->pk->selfsigversion < 4 )
            mask[0] |= (1<<1); /* IDEA is implicitly there for v3 keys
@@ -1106,7 +1182,7 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint)
             wasn't locked at MD5, we don't support sign+encrypt in
             --pgp2 mode, and that's the only time PREFTYPE_HASH is
             used anyway. -dms */
-         if( opt.pgp2 &&
+         if( PGP2 &&
              pkr->pk->version < 4 &&
              pkr->pk->selfsigversion < 4 )
            mask[0] |= (1<<1); /* MD5 is there for v3 keys with v3
@@ -1137,18 +1213,18 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint)
            compr_hack = 1;
        }
 
-      #if 0
+#if 0
        log_debug("pref mask=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
               (ulong)mask[7], (ulong)mask[6], (ulong)mask[5], (ulong)mask[4],
             (ulong)mask[3], (ulong)mask[2], (ulong)mask[1], (ulong)mask[0]);
-      #endif
+#endif
        for(i=0; i < 8; i++ )
            bits[i] &= mask[i];
-      #if 0
+#if 0
        log_debug("pref bits=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
               (ulong)bits[7], (ulong)bits[6], (ulong)bits[5], (ulong)bits[4],
             (ulong)bits[3], (ulong)bits[2], (ulong)bits[1], (ulong)bits[0]);
-      #endif
+#endif
     }
     /* usable algorithms are now in bits
      * We now use the last key from pk_list to select
@@ -1159,6 +1235,11 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint)
     i = -1;
     any = 0;
 
+    /* Can we use the requested algorithm? */
+    if(request>-1 && (bits[request/32] & (1<<(request%32))) &&
+       algo_available(preftype,request,hint))
+      return request;
+
     /* If we have personal prefs set, use them instead of the last key */
     if(preftype==PREFTYPE_SYM && opt.personal_cipher_prefs)
       prefs=opt.personal_cipher_prefs;
@@ -1190,19 +1271,15 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint)
            }
     }
 
-    /* Can we use the requested algorithm? */
-    if(request>-1 && request==i)
-      return i;
-
-  #if 0
+#if 0
     log_debug("prefs of type %d: selected %d\n", preftype, i );
-  #endif
+#endif
     if( compr_hack && !i ) {
        /* selected no compression, but we should check whether
         * algorithm 1 is also available (the ordering is not relevant
         * in this case). */
        if( bits[0] & (1<<1) )
-           i = 1;  /* yep; we can use compression algo 1 */
+           i = 1; /* yep; we can use compression algo 1 */
     }
 
     /* "If you are building an authentication system, the recipient
@@ -1210,7 +1287,7 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint)
        would be foolish to use a weak algorithm simply because the
        recipient requests it." RFC2440:13.  If we settle on MD5, and
        SHA1 is also available, use SHA1 instead.  Of course, if the
-       user intentinally chose MD5 (by putting it in their personal
+       user intentionally chose MD5 (by putting it in their personal
        prefs), then we should do what they say. */
 
     if(preftype==PREFTYPE_HASH &&