Backport bug fix. Fixes bug#1240.
[gnupg.git] / g10 / getkey.c
index 8664c0f..54843cf 100644 (file)
@@ -1,12 +1,12 @@
 /* getkey.c -  Get a key from the database
- * 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, 2008 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>
 #include <string.h>
 #include <assert.h>
 #include <ctype.h>
+
+#include "gpg.h"
 #include "util.h"
 #include "packet.h"
-#include "memory.h"
 #include "iobuf.h"
 #include "keydb.h"
 #include "options.h"
@@ -48,7 +47,8 @@ struct getkey_ctx_s {
     int exact;
     KBNODE keyblock;
     KBPOS  kbpos;
-    KBNODE found_key; /* pointer into some keyblock */
+    KBNODE found_key; /* Pointer into some keyblock. */
+    strlist_t extra_list;  /* Will be freed when releasing the context.  */
     int last_rc;
     int req_usage;
     int req_algo;
@@ -163,6 +163,21 @@ cache_public_key( PKT_public_key *pk )
 }
 
 
+/* Return a const utf-8 string with the text "[User ID not found]".
+   This fucntion is required so that we don't need to switch gettext's
+   encoding temporary. */
+static const char *
+user_id_not_found_utf8 (void)
+{
+  static char *text;
+
+  if (!text)
+    text = native_to_utf8 (_("[User ID not found]"));
+  return text;
+}
+
+
+
 /*
  * Return the user ID from the given keyblock.
  * We use the primary uid flag which has been set by the merge_selfsigs
@@ -183,9 +198,7 @@ get_primary_uid ( KBNODE keyblock, size_t *uidlen )
             return k->pkt->pkt.user_id->name;
         }
     } 
-    /* fixme: returning translatable constants instead of a user ID is 
-     * not good because they are probably not utf-8 encoded. */
-    s = _("[User ID not found]");
+    s = user_id_not_found_utf8 ();
     *uidlen = strlen (s);
     return s;
 }
@@ -564,6 +577,7 @@ seckey_available( u32 *keyid )
  *   Words are delimited by white space or "()<>[]{}.@-+_,;/&!"
  *   (note that you can't search for these characters). Compare
  *   is not case sensitive.
+ * - If the userid starts with a '&' a 40 hex digits keygrip is expected.
  */
 
 int
@@ -630,7 +644,7 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
 #endif
 
        case '#':  /* local user id */
-            return 0; /* This is now obsolete and van't not be used anymore*/
+            return 0; /* This is now obsolete and can't not be used anymore*/
         
         case ':': /*Unified fingerprint */
             {  
@@ -655,6 +669,9 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
             } 
             break;
            
+       case '&':  /* keygrip */
+          return 0; /* Not yet implememted. */
+
        default:
            if (s[0] == '0' && s[1] == 'x') {
                hexprefix = 1;
@@ -743,10 +760,12 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
 
 
 static int
-skip_unusable(void *dummy,u32 *keyid,PKT_user_id *uid)
+skip_unusable (void *dummy, u32 *keyid, PKT_user_id *uid)
 {
   int unusable=0;
   KBNODE keyblock;
+  
+  (void)dummy;
 
   keyblock=get_pubkeyblock(keyid);
   if(!keyblock)
@@ -794,14 +813,14 @@ skip_unusable(void *dummy,u32 *keyid,PKT_user_id *uid)
  */
 
 static int
-key_byname( GETKEY_CTX *retctx, STRLIST namelist,
+key_byname( GETKEY_CTX *retctx, strlist_t namelist,
            PKT_public_key *pk, PKT_secret_key *sk,
            int secmode, int include_unusable,
             KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd )
 {
     int rc = 0;
     int n;
-    STRLIST r;
+    strlist_t r;
     GETKEY_CTX ctx;
     KBNODE help_kb = NULL;
     
@@ -896,79 +915,122 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist,
 /* Find a public key from NAME and return the keyblock or the key.  If
    ret_kdb is not NULL, the KEYDB handle used to locate this keyblock
    is returned and the caller is responsible for closing it.  If a key
-   was not found and NAME is a valid RFC822 mailbox and PKA retrieval
-   has been enabled, we try to import the pkea via the PKA
-   mechanism. */
+   was not found (or if local search has been disabled) and NAME is a
+   valid RFC822 mailbox and --auto-key-locate has been enabled, we try
+   to import the key via the online mechanisms defined by
+   --auto-key-locate.  */
 int
-get_pubkey_byname (PKT_public_key *pk,
+get_pubkey_byname (GETKEY_CTX *retctx, PKT_public_key *pk,
                   const char *name, KBNODE *ret_keyblock,
-                   KEYDB_HANDLE *ret_kdbhd, int include_unusable )
+                   KEYDB_HANDLE *ret_kdbhd, int include_unusable, 
+                   int no_akl)
 {
   int rc;
-  STRLIST namelist = NULL;
+  strlist_t namelist = NULL;
+  struct akl *akl;
+  int is_mbox;
+  int nodefault = 0;
+  int anylocalfirst = 0;
+
+  if (retctx)
+    *retctx = NULL;
+
+  is_mbox = is_valid_mailbox (name);
+
+  /* Check whether we the default local search has been disabled.
+     This is the case if either the "nodefault" or the "local" keyword
+     are in the list of auto key locate mechanisms. 
+
+     ANYLOCALFIRST is set if the search order has the local method
+     before any other or if "local" is used first by default.  This
+     makes sure that if a RETCTX is used it gets only set if a local
+     search has precedence over the other search methods and only then
+     a followup call to get_pubkey_next shall succeed.  */
+  if (!no_akl)
+    {
+      for (akl=opt.auto_key_locate; akl; akl=akl->next)
+        if (akl->type == AKL_NODEFAULT || akl->type == AKL_LOCAL)
+          {
+            nodefault = 1;
+            break;
+          }
+      for (akl=opt.auto_key_locate; akl; akl=akl->next)
+        if (akl->type != AKL_NODEFAULT)
+          {
+            if (akl->type == AKL_LOCAL)
+              anylocalfirst = 1;
+            break;
+          }
+    }
 
-  add_to_strlist( &namelist, name );
+  if (!nodefault)
+    anylocalfirst = 1;
 
-  rc = key_byname( NULL, namelist, pk, NULL, 0,
-                   include_unusable, ret_keyblock, ret_kdbhd);
+  if (nodefault && is_mbox)
+    {
+      /* Nodefault but a mailbox - let the AKL locate the key.  */
+      rc = G10ERR_NO_PUBKEY;
+    }
+  else
+    {
+      add_to_strlist (&namelist, name);
+      rc = key_byname (retctx, namelist, pk, NULL, 0,
+                       include_unusable, ret_keyblock, ret_kdbhd);
+    }
 
   /* If the requested name resembles a valid mailbox and automatic
      retrieval has been enabled, we try to import the key. */
-
-  if (rc == G10ERR_NO_PUBKEY && is_valid_mailbox(name))
+  if (gpg_err_code (rc) == G10ERR_NO_PUBKEY && !no_akl && is_mbox)
     {
-      int res;
-      struct akl *akl;
-
-      for(akl=opt.auto_key_locate;akl;akl=akl->next)
+      for (akl=opt.auto_key_locate; akl; akl=akl->next)
        {
-         switch(akl->type)
+         unsigned char *fpr = NULL;
+         size_t fpr_len;
+          int did_key_byname = 0;
+          int no_fingerprint = 0;
+          const char *mechanism = "?";
+          
+          switch(akl->type)
            {
+            case AKL_NODEFAULT:
+              /* This is a dummy mechanism.  */
+              mechanism = "None";
+              rc = G10ERR_NO_PUBKEY;
+              break;
+
+            case AKL_LOCAL:
+              mechanism = "Local";
+              did_key_byname = 1;
+              if (retctx)
+                {
+                  get_pubkey_end (*retctx);
+                  *retctx = NULL;
+                }
+              add_to_strlist (&namelist, name);
+              rc = key_byname (anylocalfirst? retctx:NULL,
+                               namelist, pk, NULL, 0,
+                               include_unusable, ret_keyblock, ret_kdbhd);
+              break;
+
            case AKL_CERT:
+              mechanism = "DNS CERT";
              glo_ctrl.in_auto_key_retrieve++;
-             res=keyserver_import_cert(name);
+             rc=keyserver_import_cert(name,&fpr,&fpr_len);
              glo_ctrl.in_auto_key_retrieve--;
-
-             if(res==0)
-               log_info(_("Automatically retrieved `%s' via %s\n"),
-                        name,"DNS CERT");
              break;
 
            case AKL_PKA:
-             {
-               unsigned char fpr[MAX_FINGERPRINT_LEN];
-
-               glo_ctrl.in_auto_key_retrieve++;
-               res=keyserver_import_pka(name,fpr);
-               glo_ctrl.in_auto_key_retrieve--;
-
-               if(res==0)
-                 {
-                   int i;
-                   char fpr_string[MAX_FINGERPRINT_LEN*2+1];
-
-                   log_info(_("Automatically retrieved `%s' via %s\n"),
-                            name,"PKA");
-
-                   free_strlist(namelist);
-                   namelist=NULL;
-
-                   for(i=0;i<MAX_FINGERPRINT_LEN;i++)
-                     sprintf(fpr_string+2*i,"%02X",fpr[i]);
-
-                   add_to_strlist( &namelist, fpr_string );
-                 }
-             }
+              mechanism = "PKA";
+             glo_ctrl.in_auto_key_retrieve++;
+             rc=keyserver_import_pka(name,&fpr,&fpr_len);
+             glo_ctrl.in_auto_key_retrieve--;
              break;
 
            case AKL_LDAP:
+              mechanism = "LDAP";
              glo_ctrl.in_auto_key_retrieve++;
-             res=keyserver_import_ldap(name);
+             rc=keyserver_import_ldap(name,&fpr,&fpr_len);
              glo_ctrl.in_auto_key_retrieve--;
-
-             if(res==0)
-               log_info(_("Automatically retrieved `%s' via %s\n"),
-                        name,"LDAP");
              break;
 
            case AKL_KEYSERVER:
@@ -978,46 +1040,108 @@ get_pubkey_byname (PKT_public_key *pk,
                 and getting a whole lot of keys back. */
              if(opt.keyserver)
                {
+                  mechanism = opt.keyserver->uri;
                  glo_ctrl.in_auto_key_retrieve++;
-                 res=keyserver_import_name(name,opt.keyserver);
+                 rc=keyserver_import_name(name,&fpr,&fpr_len,opt.keyserver);
                  glo_ctrl.in_auto_key_retrieve--;
-
-                 if(res==0)
-                   log_info(_("Automatically retrieved `%s' via %s\n"),
-                            name,opt.keyserver->uri);
                }
+              else
+                {
+                  mechanism = "Unconfigured keyserver";
+                  rc = G10ERR_NO_PUBKEY;
+                }
              break;
 
            case AKL_SPEC:
              {
                struct keyserver_spec *keyserver;
 
+                mechanism = akl->spec->uri;
                keyserver=keyserver_match(akl->spec);
                glo_ctrl.in_auto_key_retrieve++;
-               res=keyserver_import_name(name,keyserver);
+               rc=keyserver_import_name(name,&fpr,&fpr_len,keyserver);
                glo_ctrl.in_auto_key_retrieve--;
-
-               if(res==0)
-                 log_info(_("Automatically retrieved `%s' via %s\n"),
-                          name,akl->spec->uri);
              }
              break;
            }
+          
+         /* Use the fingerprint of the key that we actually fetched.
+            This helps prevent problems where the key that we fetched
+            doesn't have the same name that we used to fetch it.  In
+            the case of CERT and PKA, this is an actual security
+            requirement as the URL might point to a key put in by an
+            attacker.  By forcing the use of the fingerprint, we
+            won't use the attacker's key here. */
+         if (!rc && fpr)
+           {
+             char fpr_string[MAX_FINGERPRINT_LEN*2+1];
 
-         rc = key_byname( NULL, namelist, pk, NULL, 0,
-                          include_unusable, ret_keyblock, ret_kdbhd);
-         if(rc!=G10ERR_NO_PUBKEY)
-           break;
+             assert(fpr_len<=MAX_FINGERPRINT_LEN);
+
+             free_strlist(namelist);
+             namelist=NULL;
+
+              bin2hex (fpr, fpr_len, fpr_string);
+              
+             if(opt.verbose)
+               log_info("auto-key-locate found fingerprint %s\n",fpr_string);
+
+             add_to_strlist( &namelist, fpr_string );
+           }
+          else if (!rc && !fpr && !did_key_byname)
+            {
+              no_fingerprint = 1;
+              rc = G10ERR_NO_PUBKEY;
+            }
+          xfree (fpr);
+          fpr = NULL;
+
+          if (!rc && !did_key_byname)
+            {
+              if (retctx)
+                {
+                  get_pubkey_end (*retctx);
+                  *retctx = NULL;
+                }
+              rc = key_byname (anylocalfirst?retctx:NULL,
+                               namelist, pk, NULL, 0,
+                               include_unusable, ret_keyblock, ret_kdbhd);
+            }
+         if (!rc)
+            {
+              /* Key found.  */
+              log_info (_("automatically retrieved `%s' via %s\n"),
+                        name, mechanism);
+              break;  
+            }
+          if (rc != G10ERR_NO_PUBKEY || opt.verbose || no_fingerprint)
+            log_info (_("error retrieving `%s' via %s: %s\n"),
+                      name, mechanism, 
+                      no_fingerprint? _("No fingerprint"):g10_errstr(rc));
        }
     }
 
-  free_strlist( namelist );
+  
+  if (rc && retctx)
+    {
+      get_pubkey_end (*retctx);
+      *retctx = NULL;
+    }
+
+  if (retctx && *retctx)
+    {
+      assert (!(*retctx)->extra_list);
+      (*retctx)->extra_list = namelist;
+    }
+  else
+    free_strlist (namelist);
   return rc;
 }
 
+
 int
 get_pubkey_bynames( GETKEY_CTX *retctx, PKT_public_key *pk,
-                   STRLIST names, KBNODE *ret_keyblock )
+                   strlist_t names, KBNODE *ret_keyblock )
 {
     return key_byname( retctx, names, pk, NULL, 0, 1, ret_keyblock, NULL);
 }
@@ -1040,6 +1164,7 @@ get_pubkey_end( GETKEY_CTX ctx )
     if( ctx ) {
         memset (&ctx->kbpos, 0, sizeof ctx->kbpos);
         keydb_release (ctx->kr_handle);
+        free_strlist (ctx->extra_list);
        if( !ctx->not_allocated )
            xfree( ctx );
     }
@@ -1168,7 +1293,7 @@ get_seckey_byname2( GETKEY_CTX *retctx,
                    PKT_secret_key *sk, const char *name, int unprotect,
                    KBNODE *retblock )
 {
-  STRLIST namelist = NULL;
+  strlist_t namelist = NULL;
   int rc,include_unusable=1;
 
   /* If we have no name, try to use the default secret key.  If we
@@ -1201,7 +1326,7 @@ get_seckey_byname( PKT_secret_key *sk, const char *name, int unlock )
 
 int
 get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk,
-                   STRLIST names, KBNODE *ret_keyblock )
+                   strlist_t names, KBNODE *ret_keyblock )
 {
     return key_byname( retctx, names, NULL, sk, 1, 1, ret_keyblock, NULL );
 }
@@ -1459,18 +1584,23 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
 
     sig->flags.chosen_selfsig = 1; /* we chose this one */
     uid->created = 0; /* not created == invalid */
-    if ( IS_UID_REV ( sig ) ) {
+    if ( IS_UID_REV ( sig ) ) 
+      {
         uid->is_revoked = 1;
         return; /* has been revoked */
-    }
+      }
+    else
+      uid->is_revoked = 0;
 
     uid->expiredate = sig->expiredate;
 
-    if(sig->flags.expired)
+    if (sig->flags.expired)
       {
        uid->is_expired = 1;
        return; /* has expired */
       }
+    else
+      uid->is_expired = 0;
 
     uid->created = sig->timestamp; /* this one is okay */
     uid->selfsigversion = sig->version;
@@ -1480,12 +1610,12 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
     /* store the key flags in the helper variable for later processing */
     uid->help_key_usage=parse_key_usage(sig);
 
-    /* ditto or the key expiration */
-    uid->help_key_expire = 0;
+    /* ditto for the key expiration */
     p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
-    if ( p ) { 
-        uid->help_key_expire = keycreated + buffer_to_u32(p);
-    }
+    if( p && buffer_to_u32(p) )
+      uid->help_key_expire = keycreated + buffer_to_u32(p);
+    else
+      uid->help_key_expire = 0;
 
     /* Set the primary user ID flag - we will later wipe out some
      * of them to only have one in our keyblock */
@@ -1697,7 +1827,7 @@ merge_selfsigs_main(KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo)
        key_usage=parse_key_usage(sig);
 
        p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
-       if ( p )
+       if( p && buffer_to_u32(p) )
          {
            key_expire = keytimestamp + buffer_to_u32(p);
            key_expire_seen = 1;
@@ -2008,6 +2138,26 @@ merge_selfsigs_main(KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo)
       }
 }
 
+/* Convert a buffer to a signature.  Useful for 0x19 embedded sigs.
+   Caller must free the signature when they are done. */
+static PKT_signature *
+buf_to_sig(const byte *buf,size_t len)
+{
+  PKT_signature *sig=xmalloc_clear(sizeof(PKT_signature));
+  IOBUF iobuf=iobuf_temp_with_content(buf,len);
+  int save_mode=set_packet_list_mode(0);
+
+  if(parse_signature(iobuf,PKT_SIGNATURE,len,sig)!=0)
+    {
+      xfree(sig);
+      sig=NULL;
+    }
+
+  set_packet_list_mode(save_mode);
+  iobuf_close(iobuf);
+
+  return sig;
+}
 
 static void
 merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
@@ -2101,7 +2251,7 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
     subpk->pubkey_usage = key_usage;
     
     p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
-    if ( p 
+    if ( p && buffer_to_u32(p) )
         key_expire = keytimestamp + buffer_to_u32(p);
     else
         key_expire = 0;
@@ -2109,50 +2259,79 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
     subpk->expiredate = key_expire;
 
     /* algo doesn't exist */
-    if(check_pubkey_algo(subpk->pubkey_algo))
+    if(openpgp_pk_test_algo(subpk->pubkey_algo))
       return;
 
     subpk->is_valid = 1;
 
-    /* Find the first 0x19 embedded signature on our self-sig. */
+    /* Find the most recent 0x19 embedded signature on our self-sig. */
     if(subpk->backsig==0)
       {
        int seq=0;
        size_t n;
+       PKT_signature *backsig=NULL;
+
+       sigdate=0;
 
        /* We do this while() since there may be other embedded
           signatures in the future.  We only want 0x19 here. */
+
        while((p=enum_sig_subpkt(sig->hashed,
                                 SIGSUBPKT_SIGNATURE,&n,&seq,NULL)))
          if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19)))
-           break;
+           {
+             PKT_signature *tempsig=buf_to_sig(p,n);
+             if(tempsig)
+               {
+                 if(tempsig->timestamp>sigdate)
+                   {
+                     if(backsig)
+                       free_seckey_enc(backsig);
 
-       if(p==NULL)
-         {
-           seq=0;
-           /* It is safe to have this in the unhashed area since the
-              0x19 is located on the selfsig for convenience, not
-              security. */
-           while((p=enum_sig_subpkt(sig->unhashed,SIGSUBPKT_SIGNATURE,
-                                    &n,&seq,NULL)))
-             if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19)))
-               break;
-         }
+                     backsig=tempsig;
+                     sigdate=backsig->timestamp;
+                   }
+                 else
+                   free_seckey_enc(tempsig);
+               }
+           }
+
+       seq=0;
+
+       /* It is safe to have this in the unhashed area since the 0x19
+          is located on the selfsig for convenience, not security. */
+
+       while((p=enum_sig_subpkt(sig->unhashed,SIGSUBPKT_SIGNATURE,
+                                &n,&seq,NULL)))
+         if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19)))
+           {
+             PKT_signature *tempsig=buf_to_sig(p,n);
+             if(tempsig)
+               {
+                 if(tempsig->timestamp>sigdate)
+                   {
+                     if(backsig)
+                       free_seckey_enc(backsig);
+
+                     backsig=tempsig;
+                     sigdate=backsig->timestamp;
+                   }
+                 else
+                   free_seckey_enc(tempsig);
+               }
+           }
 
-       if(p)
+       if(backsig)
          {
-           PKT_signature *backsig=xmalloc_clear(sizeof(PKT_signature));
-           IOBUF backsig_buf=iobuf_temp_with_content(p,n);
+           /* At ths point, backsig contains the most recent 0x19 sig.
+              Let's see if it is good. */
 
-           if(parse_signature(backsig_buf,PKT_SIGNATURE,n,backsig)==0)
-             {
-               if(check_backsig(mainpk,subpk,backsig)==0)
-                 subpk->backsig=2;
-               else
-                 subpk->backsig=1;
-             }
+           /* 2==valid, 1==invalid, 0==didn't check */
+           if(check_backsig(mainpk,subpk,backsig)==0)
+             subpk->backsig=2;
+           else
+             subpk->backsig=1;
 
-           iobuf_close(backsig_buf);
            free_seckey_enc(backsig);
          }
       }
@@ -2461,16 +2640,6 @@ finish_lookup (GETKEY_CTX ctx)
         goto found;
     }
     
-    if (!req_usage) {
-        PKT_public_key *pk = foundk->pkt->pkt.public_key;
-        if (pk->user_id)
-            free_user_id (pk->user_id);
-        pk->user_id = scopy_user_id (foundu);
-        ctx->found_key = foundk;
-        cache_user_id( keyblock );
-        return 1; /* found */
-    }
-    
     latest_date = 0;
     latest_key  = NULL;
     /* do not look at subkeys if a certification key is requested */
@@ -2517,8 +2686,13 @@ finish_lookup (GETKEY_CTX ctx)
             }
 
             if (DBG_CACHE)
-                log_debug( "\tsubkey looks fine\n");
-            if ( pk->timestamp > latest_date ) {
+                log_debug( "\tsubkey might be fine\n");
+            /* In case a key has a timestamp of 0 set, we make sure
+               that it is used.  A better change would be to compare
+               ">=" but that might also change the selected keys and
+               is as such a more intrusive change.  */
+            if ( pk->timestamp > latest_date
+                 || (!pk->timestamp && !latest_date)) {
                 latest_date = pk->timestamp;
                 latest_key  = k;
             }
@@ -2603,7 +2777,7 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode )
     rc = 0;
     while (!(rc = keydb_search (ctx->kr_handle, ctx->items, ctx->nitems))) {
         /* If we are searching for the first key we have to make sure
-           that the next interation does not no an implicit reset.
+           that the next iteration does not do an implicit reset.
            This can be triggered by an empty key ring. */
         if (ctx->nitems && ctx->items->mode == KEYDB_SEARCH_MODE_FIRST)
             ctx->items->mode = KEYDB_SEARCH_MODE_NEXT;
@@ -2873,7 +3047,7 @@ get_user_id( u32 *keyid, size_t *rn )
             }
         }
     } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
-    p = xstrdup( _("[User ID not found]") );
+    p = xstrdup( user_id_not_found_utf8 () );
     *rn = strlen(p);
     return p;
 }
@@ -2914,6 +3088,7 @@ release_akl(void)
     }
 }
 
+/* Returns false on error. */
 int
 parse_auto_key_locate(char *options)
 {
@@ -2921,14 +3096,19 @@ parse_auto_key_locate(char *options)
 
   while((tok=optsep(&options)))
     {
-      struct akl *akl,*last;
+      struct akl *akl,*check,*last=NULL;
+      int dupe=0;
 
       if(tok[0]=='\0')
        continue;
 
       akl=xmalloc_clear(sizeof(*akl));
 
-      if(ascii_strcasecmp(tok,"ldap")==0)
+      if(ascii_strcasecmp(tok,"nodefault")==0)
+       akl->type=AKL_NODEFAULT;
+      else if(ascii_strcasecmp(tok,"local")==0)
+       akl->type=AKL_LOCAL;
+      else if(ascii_strcasecmp(tok,"ldap")==0)
        akl->type=AKL_LDAP;
       else if(ascii_strcasecmp(tok,"keyserver")==0)
        akl->type=AKL_KEYSERVER;
@@ -2949,23 +3129,27 @@ parse_auto_key_locate(char *options)
        }
 
       /* We must maintain the order the user gave us */
-      for(last=opt.auto_key_locate;last && last->next;last=last->next)
+      for(check=opt.auto_key_locate;check;last=check,check=check->next)
        {
          /* Check for duplicates */
-         if(last && last->type==akl->type
+         if(check->type==akl->type
             && (akl->type!=AKL_SPEC
                 || (akl->type==AKL_SPEC
-                    && strcmp(last->spec->uri,akl->spec->uri)==0)))
+                    && strcmp(check->spec->uri,akl->spec->uri)==0)))
            {
+             dupe=1;
              free_akl(akl);
-             return 0;
+             break;
            }
        }
 
-      if(last)
-       last->next=akl;
-      else
-       opt.auto_key_locate=akl;
+      if(!dupe)
+       {
+         if(last)
+           last->next=akl;
+         else
+           opt.auto_key_locate=akl;
+       }
     }
 
   return 1;