* main.h, seskey.c (encode_md_value): Modify to allow a q size greater
[gnupg.git] / g10 / getkey.c
index fab0f6c..1c85fb4 100644 (file)
@@ -1,5 +1,6 @@
 /* getkey.c -  Get a key from the database
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006 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 "main.h"
 #include "trustdb.h"
 #include "i18n.h"
+#include "keyserver-internal.h"
 
-#define MAX_PK_CACHE_ENTRIES   200
-#define MAX_UID_CACHE_ENTRIES  200
+#define MAX_PK_CACHE_ENTRIES   PK_UID_CACHE_SIZE
+#define MAX_UID_CACHE_ENTRIES  PK_UID_CACHE_SIZE
 
 #if MAX_PK_CACHE_ENTRIES < 2
-  #error We need the cache for key creation
+#error We need the cache for key creation
 #endif
 
-
 struct getkey_ctx_s {
     int exact;
     KBNODE keyblock;
@@ -83,7 +85,7 @@ typedef struct keyid_list {
 #endif
 
 #if MAX_UID_CACHE_ENTRIES < 5
-    #error we really need the userid cache
+#error we really need the userid cache
 #endif
 typedef struct user_id_db {
     struct user_id_db *next;
@@ -118,7 +120,7 @@ print_stats()
 void
 cache_public_key( PKT_public_key *pk )
 {
-  #if MAX_PK_CACHE_ENTRIES
+#if MAX_PK_CACHE_ENTRIES
     pk_cache_entry_t ce;
     u32 keyid[2];
 
@@ -151,13 +153,13 @@ cache_public_key( PKT_public_key *pk )
        return;
     }
     pk_cache_entries++;
-    ce = m_alloc( sizeof *ce );
+    ce = xmalloc( sizeof *ce );
     ce->next = pk_cache;
     pk_cache = ce;
     ce->pk = copy_public_key( NULL, pk );
     ce->keyid[0] = keyid[0];
     ce->keyid[1] = keyid[1];
-  #endif
+#endif
 }
 
 
@@ -183,7 +185,7 @@ get_primary_uid ( KBNODE keyblock, size_t *uidlen )
     } 
     /* 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]");
     *uidlen = strlen (s);
     return s;
 }
@@ -194,7 +196,7 @@ release_keyid_list ( keyid_list_t k )
 {
     while (  k ) {
         keyid_list_t k2 = k->next;
-        m_free (k);
+        xfree (k);
         k = k2;
     }
 }
@@ -215,7 +217,7 @@ cache_user_id( KBNODE keyblock )
     for (k=keyblock; k; k = k->next ) {
         if ( k->pkt->pkttype == PKT_PUBLIC_KEY
              || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
-            keyid_list_t a = m_alloc_clear ( sizeof *a );
+            keyid_list_t a = xmalloc_clear ( sizeof *a );
             /* Hmmm: For a long list of keyids it might be an advantage
              * to append the keys */
             keyid_from_pk( k->pkt->pkt.public_key, a->keyid );
@@ -228,7 +230,7 @@ cache_user_id( KBNODE keyblock )
                         if( DBG_CACHE )
                             log_debug("cache_user_id: already in cache\n");
                         release_keyid_list ( keyids );
-                        m_free ( a );
+                        xfree ( a );
                         return;
                     }
                 }
@@ -249,10 +251,10 @@ cache_user_id( KBNODE keyblock )
        r = user_id_db;
        user_id_db = r->next;
         release_keyid_list ( r->keyids );
-       m_free(r);
+       xfree(r);
        uid_cache_entries--;
     }
-    r = m_alloc( sizeof *r + uidlen-1 );
+    r = xmalloc( sizeof *r + uidlen-1 );
     r->keyids = keyids;
     r->len = uidlen;
     memcpy(r->name, uid, r->len);
@@ -265,20 +267,20 @@ cache_user_id( KBNODE keyblock )
 void
 getkey_disable_caches()
 {
-  #if MAX_PK_CACHE_ENTRIES
+#if MAX_PK_CACHE_ENTRIES
     {
        pk_cache_entry_t ce, ce2;
 
        for( ce = pk_cache; ce; ce = ce2 ) {
            ce2 = ce->next;
            free_public_key( ce->pk );
-           m_free( ce );
+           xfree( ce );
        }
        pk_cache_disabled=1;
        pk_cache_entries = 0;
        pk_cache = NULL;
     }
-  #endif
+#endif
     /* fixme: disable user id cache ? */
 }
 
@@ -318,21 +320,26 @@ get_pubkey( PKT_public_key *pk, u32 *keyid )
     int internal = 0;
     int rc = 0;
 
-  #if MAX_PK_CACHE_ENTRIES
-    {  /* Try to get it from the cache */
+#if MAX_PK_CACHE_ENTRIES
+    if(pk)
+      {
+       /* Try to get it from the cache.  We don't do this when pk is
+          NULL as it does not guarantee that the user IDs are
+          cached. */
        pk_cache_entry_t ce;
-       for( ce = pk_cache; ce; ce = ce->next ) {
-           if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) {
-               if( pk )
-                   copy_public_key( pk, ce->pk );
+       for( ce = pk_cache; ce; ce = ce->next )
+         {
+           if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] )
+             {
+               copy_public_key( pk, ce->pk );
                return 0;
-           }
-       }
-    }
-  #endif
+             }
+         }
+      }
+#endif
     /* more init stuff */
     if( !pk ) {
-       pk = m_alloc_clear( sizeof *pk );
+       pk = xmalloc_clear( sizeof *pk );
        internal++;
     }
 
@@ -371,6 +378,68 @@ get_pubkey( PKT_public_key *pk, u32 *keyid )
 }
 
 
+/* Get a public key and store it into the allocated pk.  This function
+   differs from get_pubkey() in that it does not do a check of the key
+   to avoid recursion.  It should be used only in very certain cases.
+   It will only retrieve primary keys. */
+int
+get_pubkey_fast (PKT_public_key *pk, u32 *keyid)
+{
+  int rc = 0;
+  KEYDB_HANDLE hd;
+  KBNODE keyblock;
+  u32 pkid[2];
+  
+  assert (pk);
+#if MAX_PK_CACHE_ENTRIES
+  { /* Try to get it from the cache */
+    pk_cache_entry_t ce;
+
+    for (ce = pk_cache; ce; ce = ce->next)
+      {
+        if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1])
+          {
+            if (pk)
+              copy_public_key (pk, ce->pk);
+            return 0;
+          }
+      }
+  }
+#endif
+
+  hd = keydb_new (0);
+  rc = keydb_search_kid (hd, keyid);
+  if (rc == -1)
+    {
+      keydb_release (hd);
+      return G10ERR_NO_PUBKEY;
+    }
+  rc = keydb_get_keyblock (hd, &keyblock);
+  keydb_release (hd);
+  if (rc) 
+    {
+      log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+      return G10ERR_NO_PUBKEY;
+    }
+
+  assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY
+           ||  keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY );
+
+  keyid_from_pk(keyblock->pkt->pkt.public_key,pkid);
+  if(keyid[0]==pkid[0] && keyid[1]==pkid[1])
+    copy_public_key (pk, keyblock->pkt->pkt.public_key );
+  else
+    rc=G10ERR_NO_PUBKEY;
+
+  release_kbnode (keyblock);
+
+  /* Not caching key here since it won't have all of the fields
+     properly set. */
+
+  return rc;
+}
+
+
 KBNODE
 get_pubkeyblock( u32 *keyid )
 {
@@ -497,33 +566,37 @@ seckey_available( u32 *keyid )
  *   is not case sensitive.
  */
 
-static int
-classify_user_id2( const char *name, 
-                   KEYDB_SEARCH_DESC *desc,
-                   int *force_exact )
+int
+classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
 {
     const char *s;
     int hexprefix = 0;
     int hexlength;
     int mode = 0;   
-    
+    KEYDB_SEARCH_DESC dummy_desc;
+
+    if (!desc)
+        desc = &dummy_desc;
+
     /* clear the structure so that the mode field is set to zero unless
      * we set it to the correct value right at the end of this function */
     memset (desc, 0, sizeof *desc);
-    *force_exact = 0;
+
     /* skip leading spaces.  Fixme: what is with trailing spaces? */
-    for(s = name; *s && isspace(*s); s++ )
+    for(s = name; *s && spacep (s); s++ )
        ;
 
     switch (*s) {
        case 0:    /* empty string is an error */
            return 0;
 
+#if 0
        case '.':  /* an email address, compare from end */
            mode = KEYDB_SEARCH_MODE_MAILEND;
            s++;
             desc->u.name = s;
            break;
+#endif
 
        case '<':  /* an email address */
            mode = KEYDB_SEARCH_MODE_MAIL;
@@ -548,11 +621,13 @@ classify_user_id2( const char *name,
             desc->u.name = s;
            break;
 
+#if 0
        case '+':  /* compare individual words */
            mode = KEYDB_SEARCH_MODE_WORDS;
            s++;
             desc->u.name = s;
            break;
+#endif
 
        case '#':  /* local user id */
             return 0; /* This is now obsolete and van't not be used anymore*/
@@ -588,19 +663,19 @@ classify_user_id2( const char *name,
 
            hexlength = strspn(s, "0123456789abcdefABCDEF");
             if (hexlength >= 8 && s[hexlength] =='!') {
-                *force_exact = 1;
+               desc->exact = 1;
                 hexlength++; /* just for the following check */
             }
 
            /* check if a hexadecimal number is terminated by EOS or blank */
-           if (hexlength && s[hexlength] && !isspace(s[hexlength])) {
+           if (hexlength && s[hexlength] && !spacep(s+hexlength)) {
                if (hexprefix)      /* a "0x" prefix without correct */
                    return 0;       /* termination is an error */
                else                /* The first chars looked like */
                    hexlength = 0;  /* a hex number, but really were not. */
            }
 
-            if (*force_exact)
+            if (desc->exact)
                 hexlength--;
 
            if (hexlength == 8
@@ -656,7 +731,7 @@ classify_user_id2( const char *name,
                if (hexprefix)  /* This was a hex number with a prefix */
                    return 0;   /* and a wrong length */
 
-                *force_exact = 0;
+               desc->exact = 0;
                 desc->u.name = s;
                mode = KEYDB_SEARCH_MODE_SUBSTR;   /* default mode */
            }
@@ -666,29 +741,62 @@ classify_user_id2( const char *name,
     return mode;
 }
 
-int
-classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc)
+
+static int
+skip_unusable(void *dummy,u32 *keyid,PKT_user_id *uid)
 {
-    int dummy;
-    KEYDB_SEARCH_DESC dummy_desc;
+  int unusable=0;
+  KBNODE keyblock;
 
-    if (!desc)
-        desc = &dummy_desc;
-    return classify_user_id2 (name, desc, &dummy);
+  keyblock=get_pubkeyblock(keyid);
+  if(!keyblock)
+    {
+      log_error("error checking usability status of %s\n",keystr(keyid));
+      goto leave;
+    }
+
+  /* Is the user ID in question revoked/expired? */
+  if(uid)
+    {
+      KBNODE node;
+
+      for(node=keyblock;node;node=node->next)
+       {
+         if(node->pkt->pkttype==PKT_USER_ID)
+           {
+             if(cmp_user_ids(uid,node->pkt->pkt.user_id)==0
+                && (node->pkt->pkt.user_id->is_revoked
+                    || node->pkt->pkt.user_id->is_expired))
+               {
+                 unusable=1;
+                 break;
+               }
+           }
+       }
+    }
+
+  if(!unusable)
+    unusable=pk_is_disabled(keyblock->pkt->pkt.public_key);
+
+ leave:
+  release_kbnode(keyblock);
+  return unusable;
 }
 
 /****************
  * Try to get the pubkey by the userid. This function looks for the
- * first pubkey certificate which has the given name in a user_id.
- * if pk/sk has the pubkey algo set, the function will only return
- * a pubkey with that algo.
- * The caller should provide storage for either the pk or the sk.
- * If ret_kb is not NULL the function will return the keyblock there.
+ * first pubkey certificate which has the given name in a user_id.  if
+ * pk/sk has the pubkey algo set, the function will only return a
+ * pubkey with that algo.  If namelist is NULL, the first key is
+ * returned.  The caller should provide storage for either the pk or
+ * the sk.  If ret_kb is not NULL the function will return the
+ * keyblock there.
  */
 
 static int
 key_byname( GETKEY_CTX *retctx, STRLIST namelist,
-           PKT_public_key *pk, PKT_secret_key *sk, int secmode,
+           PKT_public_key *pk, PKT_secret_key *sk,
+           int secmode, int include_unusable,
             KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd )
 {
     int rc = 0;
@@ -696,7 +804,6 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist,
     STRLIST r;
     GETKEY_CTX ctx;
     KBNODE help_kb = NULL;
-    int exact;
     
     if( retctx ) {/* reset the returned context in case of error */
         assert (!ret_kdbhd);  /* not allowed because the handle is
@@ -706,22 +813,43 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist,
     if (ret_kdbhd)
         *ret_kdbhd = NULL;
 
-    /* build the search context */
-    for(n=0, r=namelist; r; r = r->next )
-       n++;
-    ctx = m_alloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items );
-    ctx->nitems = n;
+    if(!namelist)
+      {
+       ctx = xmalloc_clear (sizeof *ctx);
+       ctx->nitems = 1;
+       ctx->items[0].mode=KEYDB_SEARCH_MODE_FIRST;
+       if(!include_unusable)
+         ctx->items[0].skipfnc=skip_unusable;
+      }
+    else
+      {
+       /* build the search context */
+       for(n=0, r=namelist; r; r = r->next )
+         n++;
 
-    for(n=0, r=namelist; r; r = r->next, n++ ) {
-       classify_user_id2 (r->d, &ctx->items[n], &exact);
+       ctx = xmalloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items );
+       ctx->nitems = n;
+
+       for(n=0, r=namelist; r; r = r->next, n++ )
+         {
+           classify_user_id (r->d, &ctx->items[n]);
         
-        if (exact)
-            ctx->exact = 1;
-        if (!ctx->items[n].mode) {
-           m_free (ctx);
-           return G10ERR_INV_USER_ID;
-       }
-    }
+           if (ctx->items[n].exact)
+             ctx->exact = 1;
+           if (!ctx->items[n].mode)
+             {
+               xfree (ctx);
+               return G10ERR_INV_USER_ID;
+             }
+           if(!include_unusable
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_SHORT_KID
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_LONG_KID
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR16
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR20
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR)
+             ctx->items[n].skipfnc=skip_unusable;
+         }
+      }
 
     ctx->kr_handle = keydb_new (secmode);
     if ( !ret_kb ) 
@@ -763,30 +891,147 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist,
     return rc;
 }
 
-/*
- * Find a public key from NAME and returh 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.
- */
+
+
+/* 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. */
 int
 get_pubkey_byname (PKT_public_key *pk,
                   const char *name, KBNODE *ret_keyblock,
-                   KEYDB_HANDLE *ret_kdbhd ) 
+                   KEYDB_HANDLE *ret_kdbhd, int include_unusable )
 {
-    int rc;
-    STRLIST namelist = NULL;
+  int rc;
+  STRLIST namelist = NULL;
 
-    add_to_strlist( &namelist, name );
-    rc = key_byname( NULL, namelist, pk, NULL, 0, ret_keyblock, ret_kdbhd);
-    free_strlist( namelist );
-    return rc;
+  add_to_strlist( &namelist, name );
+
+  rc = key_byname( NULL, 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))
+    {
+      struct akl *akl;
+
+      for(akl=opt.auto_key_locate;akl;akl=akl->next)
+       {
+         unsigned char *fpr;
+         size_t fpr_len;
+
+         switch(akl->type)
+           {
+           case AKL_CERT:
+             glo_ctrl.in_auto_key_retrieve++;
+             rc=keyserver_import_cert(name,&fpr,&fpr_len);
+             glo_ctrl.in_auto_key_retrieve--;
+
+             if(rc==0)
+               log_info(_("Automatically retrieved `%s' via %s\n"),
+                        name,"DNS CERT");
+             break;
+
+           case AKL_PKA:
+             glo_ctrl.in_auto_key_retrieve++;
+             rc=keyserver_import_pka(name,&fpr,&fpr_len);
+
+             if(rc==0)
+               log_info(_("Automatically retrieved `%s' via %s\n"),
+                        name,"PKA");
+             break;
+
+           case AKL_LDAP:
+             glo_ctrl.in_auto_key_retrieve++;
+             rc=keyserver_import_ldap(name,&fpr,&fpr_len);
+             glo_ctrl.in_auto_key_retrieve--;
+
+             if(rc==0)
+               log_info(_("Automatically retrieved `%s' via %s\n"),
+                        name,"LDAP");
+             break;
+
+           case AKL_KEYSERVER:
+             /* Strictly speaking, we don't need to only use a valid
+                mailbox for the getname search, but it helps cut down
+                on the problem of searching for something like "john"
+                and getting a whole lot of keys back. */
+             if(opt.keyserver)
+               {
+                 glo_ctrl.in_auto_key_retrieve++;
+                 rc=keyserver_import_name(name,&fpr,&fpr_len,opt.keyserver);
+                 glo_ctrl.in_auto_key_retrieve--;
+
+                 if(rc==0)
+                   log_info(_("Automatically retrieved `%s' via %s\n"),
+                            name,opt.keyserver->uri);
+               }
+             break;
+
+           case AKL_SPEC:
+             {
+               struct keyserver_spec *keyserver;
+
+               keyserver=keyserver_match(akl->spec);
+               glo_ctrl.in_auto_key_retrieve++;
+               rc=keyserver_import_name(name,&fpr,&fpr_len,keyserver);
+               glo_ctrl.in_auto_key_retrieve--;
+
+               if(rc==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==0 && fpr)
+           {
+             int i;
+             char fpr_string[MAX_FINGERPRINT_LEN*2+1];
+
+             assert(fpr_len<=MAX_FINGERPRINT_LEN);
+
+             free_strlist(namelist);
+             namelist=NULL;
+
+             for(i=0;i<fpr_len;i++)
+               sprintf(fpr_string+2*i,"%02X",fpr[i]);
+
+             if(opt.verbose)
+               log_info("auto-key-locate found fingerprint %s\n",fpr_string);
+
+             add_to_strlist( &namelist, fpr_string );
+
+             xfree(fpr);
+           }
+
+         rc = key_byname( NULL, namelist, pk, NULL, 0,
+                          include_unusable, ret_keyblock, ret_kdbhd);
+         if(rc!=G10ERR_NO_PUBKEY)
+           break;
+       }
+    }
+
+  free_strlist( namelist );
+  return rc;
 }
 
 int
 get_pubkey_bynames( GETKEY_CTX *retctx, PKT_public_key *pk,
                    STRLIST names, KBNODE *ret_keyblock )
 {
-    return key_byname( retctx, names, pk, NULL, 0, ret_keyblock, NULL);
+    return key_byname( retctx, names, pk, NULL, 0, 1, ret_keyblock, NULL);
 }
 
 int
@@ -801,7 +1046,6 @@ get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock )
     return rc;
 }
 
-
 void
 get_pubkey_end( GETKEY_CTX ctx )
 {
@@ -809,13 +1053,11 @@ get_pubkey_end( GETKEY_CTX ctx )
         memset (&ctx->kbpos, 0, sizeof ctx->kbpos);
         keydb_release (ctx->kr_handle);
        if( !ctx->not_allocated )
-           m_free( ctx );
+           xfree( ctx );
     }
 }
 
 
-
-
 /****************
  * Search for a key with the given fingerprint.
  * FIXME:
@@ -851,6 +1093,54 @@ get_pubkey_byfprint( PKT_public_key *pk,
     return rc;
 }
 
+
+/* Get a public key and store it into the allocated pk.  This function
+   differs from get_pubkey_byfprint() in that it does not do a check
+   of the key to avoid recursion.  It should be used only in very
+   certain cases.  PK may be NULL to check just for the existance of
+   the key. */
+int
+get_pubkey_byfprint_fast (PKT_public_key *pk,
+                          const byte *fprint, size_t fprint_len)
+{
+  int rc = 0;
+  KEYDB_HANDLE hd;
+  KBNODE keyblock;
+  byte fprbuf[MAX_FINGERPRINT_LEN];
+  int i;
+  
+  for (i=0; i < MAX_FINGERPRINT_LEN && i < fprint_len; i++)
+    fprbuf[i] = fprint[i];
+  while (i < MAX_FINGERPRINT_LEN) 
+    fprbuf[i++] = 0;
+
+  hd = keydb_new (0);
+  rc = keydb_search_fpr (hd, fprbuf);
+  if (rc == -1)
+    {
+      keydb_release (hd);
+      return G10ERR_NO_PUBKEY;
+    }
+  rc = keydb_get_keyblock (hd, &keyblock);
+  keydb_release (hd);
+  if (rc) 
+    {
+      log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+      return G10ERR_NO_PUBKEY;
+    }
+  
+  assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY
+           ||  keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY );
+  if (pk)
+    copy_public_key (pk, keyblock->pkt->pkt.public_key );
+  release_kbnode (keyblock);
+
+  /* Not caching key here since it won't have all of the fields
+     properly set. */
+
+  return 0;
+}
+
 /****************
  * Search for a key with the given fingerprint and return the
  * complete keyblock which may have more than only this key.
@@ -887,44 +1177,31 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint,
  */
 static int
 get_seckey_byname2( GETKEY_CTX *retctx,
-                   PKT_secret_key *sk, const char *name, int unprotect,
-                   KBNODE *retblock )
+                   PKT_secret_key *sk, const char *name, int unprotect,
+                   KBNODE *retblock )
 {
-    STRLIST namelist = NULL;
-    int rc;
+  STRLIST namelist = NULL;
+  int rc,include_unusable=1;
 
-    if( !name && opt.def_secret_key && *opt.def_secret_key ) {
-       add_to_strlist( &namelist, opt.def_secret_key );
-       rc = key_byname( retctx, namelist, NULL, sk, 1, retblock, NULL );
-    }
-    else if( !name ) { /* use the first one as default key */
-       struct getkey_ctx_s ctx;
-        KBNODE kb = NULL;
+  /* If we have no name, try to use the default secret key.  If we
+     have no default, we'll use the first usable one. */
 
-        assert (!retctx ); /* do we need this at all */
-        assert (!retblock);
-       memset( &ctx, 0, sizeof ctx );
-       ctx.not_allocated = 1;
-        ctx.kr_handle = keydb_new (1);
-       ctx.nitems = 1;
-       ctx.items[0].mode = KEYDB_SEARCH_MODE_FIRST;
-       rc = lookup( &ctx, &kb, 1 );
-        if (!rc && sk )
-            sk_from_block ( &ctx, sk, kb );
-        release_kbnode ( kb );
-       get_seckey_end( &ctx );
-    }
-    else {
-       add_to_strlist( &namelist, name );
-       rc = key_byname( retctx, namelist, NULL, sk, 1, retblock, NULL );
-    }
+  if( !name && opt.def_secret_key && *opt.def_secret_key )
+    add_to_strlist( &namelist, opt.def_secret_key );
+  else if(name)
+    add_to_strlist( &namelist, name );
+  else
+    include_unusable=0;
 
-    free_strlist( namelist );
+  rc = key_byname( retctx, namelist, NULL, sk, 1, include_unusable,
+                  retblock, NULL );
 
-    if( !rc && unprotect )
-       rc = check_secret_key( sk, 0 );
+  free_strlist( namelist );
 
-    return rc;
+  if( !rc && unprotect )
+    rc = check_secret_key( sk, 0 );
+
+  return rc;
 }
 
 int 
@@ -938,7 +1215,7 @@ int
 get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk,
                    STRLIST names, KBNODE *ret_keyblock )
 {
-    return key_byname( retctx, names, NULL, sk, 1, ret_keyblock, NULL );
+    return key_byname( retctx, names, NULL, sk, 1, 1, ret_keyblock, NULL );
 }
 
 
@@ -990,13 +1267,41 @@ get_seckey_byfprint( PKT_secret_key *sk,
         if (!rc && sk )
             sk_from_block ( &ctx, sk, kb );
         release_kbnode ( kb );
-       get_pubkey_end( &ctx );
+       get_seckey_end( &ctx );
     }
     else
        rc = G10ERR_GENERAL; /* Oops */
     return rc;
 }
 
+
+/* Search for a secret key with the given fingerprint and return the
+   complete keyblock which may have more than only this key. */
+int
+get_seckeyblock_byfprint (KBNODE *ret_keyblock, const byte *fprint,
+                          size_t fprint_len )
+{
+  int rc;
+  struct getkey_ctx_s ctx;
+  
+  if (fprint_len != 20 && fprint_len == 16)
+    return G10ERR_GENERAL; /* Oops */
+    
+  memset (&ctx, 0, sizeof ctx);
+  ctx.not_allocated = 1;
+  ctx.kr_handle = keydb_new (1);
+  ctx.nitems = 1;
+  ctx.items[0].mode = (fprint_len==16
+                       ? KEYDB_SEARCH_MODE_FPR16
+                       : KEYDB_SEARCH_MODE_FPR20);
+  memcpy (ctx.items[0].u.fpr, fprint, fprint_len);
+  rc = lookup (&ctx, ret_keyblock, 1);
+  get_seckey_end (&ctx);
+  
+  return rc;
+}
+
+
 \f
 /************************************************
  ************* Merging stuff ********************
@@ -1093,6 +1398,59 @@ merge_keys_and_selfsig( KBNODE keyblock )
     }
 }
 
+static int
+parse_key_usage(PKT_signature *sig)
+{
+  int key_usage=0;
+  const byte *p;
+  size_t n;
+  byte flags;
+
+  p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_KEY_FLAGS,&n);
+  if(p && n)
+    {
+      /* first octet of the keyflags */
+      flags=*p;
+
+      if(flags & 1)
+       {
+         key_usage |= PUBKEY_USAGE_CERT;
+         flags&=~1;
+       }
+
+      if(flags & 2)
+       {
+         key_usage |= PUBKEY_USAGE_SIG;
+         flags&=~2;
+       }
+
+      /* We do not distinguish between encrypting communications and
+        encrypting storage. */
+      if(flags & (0x04|0x08))
+       {
+         key_usage |= PUBKEY_USAGE_ENC;
+         flags&=~(0x04|0x08);
+       }
+
+      if(flags & 0x20)
+       {
+         key_usage |= PUBKEY_USAGE_AUTH;
+         flags&=~0x20;
+       }
+
+      if(flags)
+       key_usage |= PUBKEY_USAGE_UNKNOWN;
+    }
+
+  /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a
+     capability that we do not handle.  This serves to distinguish
+     between a zero key usage which we handle as the default
+     capabilities for that algorithm, and a usage that we do not
+     handle. */
+
+  return key_usage;
+}
+
 /*
  * Apply information from SIGNODE (which is the valid self-signature
  * associated with that UID) to the UIDNODE:
@@ -1111,30 +1469,28 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
     const byte *p, *sym, *hash, *zip;
     size_t n, nsym, nhash, nzip;
 
+    sig->flags.chosen_selfsig = 1; /* we chose this one */
     uid->created = 0; /* not created == invalid */
     if ( IS_UID_REV ( sig ) ) {
         uid->is_revoked = 1;
         return; /* has been revoked */
     }
 
+    uid->expiredate = sig->expiredate;
+
+    if(sig->flags.expired)
+      {
+       uid->is_expired = 1;
+       return; /* has expired */
+      }
+
     uid->created = sig->timestamp; /* this one is okay */
     uid->selfsigversion = sig->version;
     /* If we got this far, it's not expired :) */
     uid->is_expired = 0;
-    uid->expiredate = sig->expiredate;
 
     /* store the key flags in the helper variable for later processing */
-    uid->help_key_usage = 0;
-    p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n );
-    if ( p && n ) {
-        /* first octet of the keyflags */   
-        if ( (*p & 3) )
-            uid->help_key_usage |= PUBKEY_USAGE_SIG;
-        if ( (*p & 12) )    
-            uid->help_key_usage |= PUBKEY_USAGE_ENC;
-        /* Note: we do not set the CERT flag here because it can be assumed
-         * that thre is no real policy to set it. */
-    }
+    uid->help_key_usage=parse_key_usage(sig);
 
     /* ditto or the key expiration */
     uid->help_key_expire = 0;
@@ -1148,7 +1504,7 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
     uid->is_primary = 0;
     p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL );
     if ( p && *p )
-        uid->is_primary = 1;
+        uid->is_primary = 2;
     /* We could also query this from the unhashed area if it is not in
      * the hased area and then later try to decide which is the better
      * there should be no security problem with this.
@@ -1165,12 +1521,12 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
     p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PREF_COMPR, &n );
     zip = p; nzip = p?n:0;
     if (uid->prefs) 
-        m_free (uid->prefs);
+        xfree (uid->prefs);
     n = nsym + nhash + nzip;
     if (!n)
         uid->prefs = NULL;
     else {
-        uid->prefs = m_alloc (sizeof (*uid->prefs) * (n+1));
+        uid->prefs = xmalloc (sizeof (*uid->prefs) * (n+1));
         n = 0;
         for (; nsym; nsym--, n++) {
             uid->prefs[n].type = PREFTYPE_SYM;
@@ -1189,20 +1545,34 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
     }
 
     /* see whether we have the MDC feature */
-    uid->mdc_feature = 0;
+    uid->flags.mdc = 0;
     p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n);
     if (p && n && (p[0] & 0x01))
-        uid->mdc_feature = 1;
-            
+        uid->flags.mdc = 1;
+
+    /* and the keyserver modify flag */
+    uid->flags.ks_modify = 1;
+    p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n);
+    if (p && n && (p[0] & 0x80))
+        uid->flags.ks_modify = 0;
+}
+
+static void
+sig_to_revoke_info(PKT_signature *sig,struct revoke_info *rinfo)
+{
+  rinfo->date = sig->timestamp;
+  rinfo->algo = sig->pubkey_algo;
+  rinfo->keyid[0] = sig->keyid[0];
+  rinfo->keyid[1] = sig->keyid[1];
 }
 
 static void
-merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
+merge_selfsigs_main(KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo)
 {
     PKT_public_key *pk = NULL;
     KBNODE k;
     u32 kid[2];
-    u32 sigdate = 0, uiddate=0, uiddate2;
+    u32 sigdate, uiddate, uiddate2;
     KBNODE signode, uidnode, uidnode2;
     u32 curtime = make_timestamp ();
     unsigned int key_usage = 0;
@@ -1212,6 +1582,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
     byte sigversion = 0;
 
     *r_revoked = 0;
+    memset(rinfo,0,sizeof(*rinfo));
+
     if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY )
         BUG ();
     pk = keyblock->pkt->pkt.public_key;
@@ -1234,7 +1606,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
      */
 
     /* In case this key was already merged */
-    m_free(pk->revkey);
+    xfree(pk->revkey);
     pk->revkey=NULL;
     pk->numrevkeys=0;
 
@@ -1257,6 +1629,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                      * that key.
                      */ 
                     *r_revoked = 1;
+                   sig_to_revoke_info(sig,rinfo);
                 }
                 else if ( IS_KEY_SIG (sig) ) {
                  /* Add any revocation keys onto the pk.  This is
@@ -1270,7 +1643,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                    int i;
 
                    pk->revkey=
-                     m_realloc(pk->revkey,sizeof(struct revocation_key)*
+                     xrealloc(pk->revkey,sizeof(struct revocation_key)*
                                (pk->numrevkeys+sig->numrevkeys));
 
                    for(i=0;i<sig->numrevkeys;i++)
@@ -1285,7 +1658,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                     else {
                         sigdate = sig->timestamp;
                         signode = k;
-                       sigversion = sig->version;
+                       if( sig->version > sigversion )
+                         sigversion = sig->version;
 
                    }
                  }
@@ -1320,44 +1694,38 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
          }
 
        if(changed)
-         pk->revkey=m_realloc(pk->revkey,
+         pk->revkey=xrealloc(pk->revkey,
                               pk->numrevkeys*sizeof(struct revocation_key));
       }
 
-    if ( signode ) {
+    if ( signode )
+      {
         /* some information from a direct key signature take precedence
          * over the same information given in UID sigs.
          */
         PKT_signature *sig = signode->pkt->pkt.signature;
         const byte *p;
-        size_t n;
-        
-        p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n );
-        if ( p && n ) {
-            /* first octet of the keyflags */   
-            if ( (*p & 3) )
-                key_usage |= PUBKEY_USAGE_SIG;
-            if ( (*p & 12) )    
-                key_usage |= PUBKEY_USAGE_ENC;
-        }
+
+       key_usage=parse_key_usage(sig);
 
        p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
-       if ( p ) {
-         key_expire = keytimestamp + buffer_to_u32(p);
-         key_expire_seen = 1;
-        }
+       if ( p )
+         {
+           key_expire = keytimestamp + buffer_to_u32(p);
+           key_expire_seen = 1;
+         }
 
         /* mark that key as valid: one direct key signature should 
          * render a key as valid */
         pk->is_valid = 1;
-    }
+      }
 
     /* pass 1.5: look for key revocation signatures that were not made
        by the key (i.e. did a revocation key issue a revocation for
        us?).  Only bother to do this if there is a revocation key in
-       the first place. */
+       the first place and we're not revoked already. */
 
-    if(pk->revkey)
+    if(!*r_revoked && pk->revkey)
       for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next )
        {
          if ( k->pkt->pkttype == PKT_SIGNATURE )
@@ -1367,15 +1735,26 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
              if(IS_KEY_REV(sig) &&
                 (sig->keyid[0]!=kid[0] || sig->keyid[1]!=kid[1]))
                { 
-                 /* Failure here means the sig did not verify, is was
-                    not issued by a revocation key, or a revocation
-                    key loop was broken. */
+                 int rc=check_revocation_keys(pk,sig);
+                 if(rc==0)
+                   {
+                     *r_revoked=2;
+                     sig_to_revoke_info(sig,rinfo);
+                     /* don't continue checking since we can't be any
+                        more revoked than this */
+                     break;
+                   }
+                 else if(rc==G10ERR_NO_PUBKEY)
+                   pk->maybe_revoked=1;
 
-                 if(check_revocation_keys(pk,sig)==0)
-                   *r_revoked=1;
+                 /* A failure here means the sig did not verify, was
+                    not issued by a revocation key, or a revocation
+                    key loop was broken.  If a revocation key isn't
+                    findable, however, the key might be revoked and
+                    we don't know it. */
 
-                 /* In the future handle subkey and cert revocations?
-                     PGP doesn't, but it's in 2440. */
+                 /* TODO: In the future handle subkey and cert
+                     revocations?  PGP doesn't, but it's in 2440. */
                }
            }
        }
@@ -1383,15 +1762,15 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
     /* second pass: look at the self-signature of all user IDs */
     signode = uidnode = NULL;
     sigdate = 0; /* helper to find the latest signature in one user ID */
-    uiddate = 0; /* and over of all user IDs */
     for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) {
        if ( k->pkt->pkttype == PKT_USER_ID ) {
             if ( uidnode && signode ) 
+             {
                 fixup_uidnode ( uidnode, signode, keytimestamp );
+               pk->is_valid=1;
+             }
             uidnode = k;
             signode = NULL;
-            if ( sigdate > uiddate )
-                uiddate = sigdate;
             sigdate = 0;
        }
         else if ( k->pkt->pkttype == PKT_SIGNATURE && uidnode ) {
@@ -1400,7 +1779,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                 if ( check_key_signature( keyblock, k, NULL ) )
                     ; /* signature did not verify */
                 else if ( (IS_UID_SIG (sig) || IS_UID_REV (sig))
-                          && sig->timestamp >= sigdate ) {
+                          && sig->timestamp >= sigdate )
+                 {
                     /* Note: we allow to invalidate cert revocations
                      * by a newer signature.  An attacker can't use this
                      * because a key should be revoced with a key revocation.
@@ -1409,20 +1789,13 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                      * the same email address may become valid again (hired,
                      * fired, hired again).
                      */
-                   if(sig->flags.expired) {
-                     /* Expired uids don't get to be primary unless
-                         they are the only uid there is. */
-                     uidnode->pkt->pkt.user_id->is_primary=0;
-                     uidnode->pkt->pkt.user_id->is_expired=1;
-                     uidnode->pkt->pkt.user_id->expiredate=sig->expiredate;
-                   }
-                    else {
-                        sigdate = sig->timestamp;
-                        signode = k;
-                       if( sig->version > sigversion )
-                         sigversion = sig->version;
-                    }
-                }
+
+                   sigdate = sig->timestamp;
+                   signode = k;
+                   signode->pkt->pkt.signature->flags.chosen_selfsig=0;
+                   if( sig->version > sigversion )
+                     sigversion = sig->version;
+                 }
             }
         }
     }
@@ -1436,10 +1809,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
     if(!pk->is_valid && opt.allow_non_selfsigned_uid)
       {
        if(opt.verbose)
-         log_info(_("Invalid key %08lX made valid by "
-                    "--allow-non-selfsigned-uid\n"),
-                  (ulong)keyid_from_pk(pk,NULL));
-
+         log_info(_("Invalid key %s made valid by"
+                    " --allow-non-selfsigned-uid\n"),keystr_from_pk(pk));
        pk->is_valid = 1;
       }
 
@@ -1461,11 +1832,18 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                  {
                    PKT_public_key *ultimate_pk;
 
-                   ultimate_pk=m_alloc_clear(sizeof(*ultimate_pk));
-
-                   if(get_pubkey(ultimate_pk,sig->keyid)==0 &&
-                      check_key_signature(keyblock,k,NULL)==0 &&
-                      get_ownertrust(ultimate_pk)==TRUST_ULTIMATE)
+                   ultimate_pk=xmalloc_clear(sizeof(*ultimate_pk));
+
+                    /* We don't want to use the full get_pubkey to
+                       avoid infinite recursion in certain cases.
+                       There is no reason to check that an ultimately
+                       trusted key is still valid - if it has been
+                       revoked or the user should also renmove the
+                       ultimate trust flag.  */
+                   if(get_pubkey_fast(ultimate_pk,sig->keyid)==0
+                      && check_key_signature2(keyblock,k,ultimate_pk,
+                                              NULL,NULL,NULL,NULL)==0
+                      && get_ownertrust(ultimate_pk)==TRUST_ULTIMATE)
                      {
                        free_public_key(ultimate_pk);
                        pk->is_valid=1;
@@ -1478,9 +1856,15 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
          }
       }
 
-    /* Record the highest selfsigversion so we know if this is a v3
-       key through and through, or a v3 key with a v4 selfsig, which
-       means we can trust the preferences (if any). */
+    /* Record the highest selfsig version so we know if this is a v3
+       key through and through, or a v3 key with a v4 selfsig
+       somewhere.  This is useful in a few places to know if the key
+       must be treated as PGP2-style or OpenPGP-style.  Note that a
+       selfsig revocation with a higher version number will also raise
+       this value.  This is okay since such a revocation must be
+       issued by the user (i.e. it cannot be issued by someone else to
+       modify the key behavior.) */
+
     pk->selfsigversion=sigversion;
 
     /* Now that we had a look at all user IDs we can now get some information
@@ -1509,7 +1893,9 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
         if ( x ) /* mask it down to the actual allowed usage */
             key_usage &= x; 
     }
-    pk->pubkey_usage = key_usage;
+
+    /* Whatever happens, it's a primary key, so it can certify. */
+    pk->pubkey_usage = key_usage|PUBKEY_USAGE_CERT;
 
     if ( !key_expire_seen ) {
         /* find the latest valid user ID with a key expiration set 
@@ -1546,14 +1932,38 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
         if ( k->pkt->pkttype == PKT_USER_ID &&
             !k->pkt->pkt.user_id->attrib_data) {
             PKT_user_id *uid = k->pkt->pkt.user_id;
-            if ( uid->is_primary && uid->created > uiddate ) {
-                uiddate = uid->created;
-                uidnode = k;
-            }
-            if ( !uid->is_primary && uid->created > uiddate2 ) {
-                uiddate2 = uid->created;
-                uidnode2 = k;
-            }
+            if (uid->is_primary)
+             {
+               if(uid->created > uiddate)
+                 {
+                   uiddate = uid->created;
+                   uidnode = k;
+                 }
+               else if(uid->created==uiddate && uidnode)
+                 {
+                   /* The dates are equal, so we need to do a
+                      different (and arbitrary) comparison.  This
+                      should rarely, if ever, happen.  It's good to
+                      try and guarantee that two different GnuPG
+                      users with two different keyrings at least pick
+                      the same primary. */
+                   if(cmp_user_ids(uid,uidnode->pkt->pkt.user_id)>0)
+                     uidnode=k;
+                 }
+             }
+           else
+             {
+               if(uid->created > uiddate2)
+                 {
+                   uiddate2 = uid->created;
+                   uidnode2 = k;
+                 }
+               else if(uid->created==uiddate2 && uidnode2)
+                 {
+                   if(cmp_user_ids(uid,uidnode2->pkt->pkt.user_id)>0)
+                     uidnode2=k;
+                 }
+             }
         }
     }
     if ( uidnode ) {
@@ -1568,23 +1978,43 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
         }
     }
     else if( uidnode2 ) {
-        /* none is flagged primary - use the latest user ID we have */
+        /* none is flagged primary - use the latest user ID we have,
+          and disambiguate with the arbitrary packet comparison. */
         uidnode2->pkt->pkt.user_id->is_primary = 1;
     }
     else
       {
-       /* None of our uids were self-signed, so pick the first one to
-          be the primary.  This is the best we can do here since
-          there are no self sigs to date the uids. */
+       /* None of our uids were self-signed, so pick the one that
+          sorts first to be the primary.  This is the best we can do
+          here since there are no self sigs to date the uids. */
+
+       uidnode = NULL;
 
        for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY;
            k = k->next )
          {
-           if(k->pkt->pkttype==PKT_USER_ID &&
-              !k->pkt->pkt.user_id->attrib_data)
+           if(k->pkt->pkttype==PKT_USER_ID
+              && !k->pkt->pkt.user_id->attrib_data)
              {
-               k->pkt->pkt.user_id->is_primary=1;
-               break;
+               if(!uidnode)
+                 {
+                   uidnode=k;
+                   uidnode->pkt->pkt.user_id->is_primary=1;
+                   continue;
+                 }
+               else
+                 {
+                   if(cmp_user_ids(k->pkt->pkt.user_id,
+                                   uidnode->pkt->pkt.user_id)>0)
+                     {
+                       uidnode->pkt->pkt.user_id->is_primary=0;
+                       uidnode=k;
+                       uidnode->pkt->pkt.user_id->is_primary=1;
+                     }
+                   else
+                     k->pkt->pkt.user_id->is_primary=0; /* just to be
+                                                           safe */
+                 }
              }
          }
       }
@@ -1605,7 +2035,6 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
     u32 keytimestamp = 0;
     u32 key_expire = 0;
     const byte *p;
-    size_t n;
 
     if ( subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY )
         BUG ();
@@ -1640,45 +2069,47 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
                      problem is in the distribution.  Plus, PGP (7)
                      does this the same way.  */
                     subpk->is_revoked = 1;
+                   sig_to_revoke_info(sig,&subpk->revoked);
                     /* although we could stop now, we continue to 
                      * figure out other information like the old expiration
                      * time */
                 }
-                else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate ) {
+                else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate )
+                 {
                    if(sig->flags.expired)
-                        ; /* signature has expired - ignore it */
-                    else {
+                     ; /* signature has expired - ignore it */
+                    else
+                     {
                         sigdate = sig->timestamp;
                         signode = k;
-                    }
-                }
+                       signode->pkt->pkt.signature->flags.chosen_selfsig=0;
+                     }
+                 }
             }
         }
     }
 
-    if ( !signode ) {
-        return;  /* no valid key binding */
-    }
+    /* no valid key binding */
+    if ( !signode )
+      return;
 
-    subpk->is_valid = 1;
     sig = signode->pkt->pkt.signature;
-        
-    p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n );
-    if ( p && n ) {
-        /* first octet of the keyflags */   
-        if ( (*p & 3) )
-            key_usage |= PUBKEY_USAGE_SIG;
-        if ( (*p & 12) )    
-            key_usage |= PUBKEY_USAGE_ENC;
-    }
-    if ( !key_usage ) { /* no key flags at all: get it from the algo */
+    sig->flags.chosen_selfsig=1; /* so we know which selfsig we chose later */
+
+    key_usage=parse_key_usage(sig);
+    if ( !key_usage )
+      {
+       /* no key flags at all: get it from the algo */
         key_usage = openpgp_pk_algo_usage ( subpk->pubkey_algo );
-    }
-    else { /* check that the usage matches the usage as given by the algo */
+      }
+    else
+      {
+       /* check that the usage matches the usage as given by the algo */
         int x = openpgp_pk_algo_usage ( subpk->pubkey_algo );
         if ( x ) /* mask it down to the actual allowed usage */
-            key_usage &= x; 
-    }
+         key_usage &= x; 
+      }
+
     subpk->pubkey_usage = key_usage;
     
     p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
@@ -1688,8 +2119,56 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
         key_expire = 0;
     subpk->has_expired = key_expire >= curtime? 0 : key_expire;
     subpk->expiredate = key_expire;
-}
 
+    /* algo doesn't exist */
+    if(check_pubkey_algo(subpk->pubkey_algo))
+      return;
+
+    subpk->is_valid = 1;
+
+    /* Find the first 0x19 embedded signature on our self-sig. */
+    if(subpk->backsig==0)
+      {
+       int seq=0;
+       size_t n;
+
+       /* 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;
+
+       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;
+         }
+
+       if(p)
+         {
+           PKT_signature *backsig=xmalloc_clear(sizeof(PKT_signature));
+           IOBUF backsig_buf=iobuf_temp_with_content(p,n);
+
+           if(parse_signature(backsig_buf,PKT_SIGNATURE,n,backsig)==0)
+             {
+               if(check_backsig(mainpk,subpk,backsig)==0)
+                 subpk->backsig=2;
+               else
+                 subpk->backsig=1;
+             }
+
+           iobuf_close(backsig_buf);
+           free_seckey_enc(backsig);
+         }
+      }
+}
 
 
 /* 
@@ -1709,6 +2188,7 @@ merge_selfsigs( KBNODE keyblock )
 {
     KBNODE k;
     int revoked;
+    struct revoke_info rinfo;
     PKT_public_key *main_pk;
     prefitem_t *prefs;
     int mdc_feature;
@@ -1725,22 +2205,7 @@ merge_selfsigs( KBNODE keyblock )
         BUG ();
     }
 
-    merge_selfsigs_main ( keyblock, &revoked );
-    main_pk = keyblock->pkt->pkt.public_key;
-    if ( revoked ) {
-        /* if the primary key has been revoked we better set the revoke
-         * flag on that key and all subkeys */
-        for(k=keyblock; k; k = k->next ) {
-            if ( k->pkt->pkttype == PKT_PUBLIC_KEY
-                || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
-                PKT_public_key *pk = k->pkt->pkt.public_key;
-                pk->is_revoked = 1;
-                pk->main_keyid[0] = main_pk->main_keyid[0];
-                pk->main_keyid[1] = main_pk->main_keyid[1];
-            }
-       }
-        return;
-    }
+    merge_selfsigs_main ( keyblock, &revoked, &rinfo );
 
     /* now merge in the data from each of the subkeys */
     for(k=keyblock; k; k = k->next ) {
@@ -1749,12 +2214,28 @@ merge_selfsigs( KBNODE keyblock )
         }
     }
 
-    /* If the main key is not valid, then the subkeys aren't either,
-       even if they have binding sigs. */
-    if(!main_pk->is_valid)
-      for(k=keyblock; k; k=k->next)
-       if(k->pkt->pkttype==PKT_PUBLIC_SUBKEY)
-         k->pkt->pkt.public_key->is_valid=0;
+    main_pk = keyblock->pkt->pkt.public_key;
+    if ( revoked || main_pk->has_expired || !main_pk->is_valid ) {
+        /* if the primary key is revoked, expired, or invalid we
+         * better set the appropriate flags on that key and all
+         * subkeys */
+        for(k=keyblock; k; k = k->next ) {
+            if ( k->pkt->pkttype == PKT_PUBLIC_KEY
+                || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+                PKT_public_key *pk = k->pkt->pkt.public_key;
+               if(!main_pk->is_valid)
+                 pk->is_valid = 0;
+               if(revoked && !pk->is_revoked)
+                 {
+                   pk->is_revoked = revoked;
+                   memcpy(&pk->revoked,&rinfo,sizeof(rinfo));
+                 }
+                if(main_pk->has_expired)
+                 pk->has_expired = main_pk->has_expired;
+            }
+       }
+       return;
+    }
 
     /* set the preference list of all keys to those of the primary real
      * user ID.  Note: we use these preferences when we don't know by
@@ -1772,7 +2253,7 @@ merge_selfsigs( KBNODE keyblock )
            && !k->pkt->pkt.user_id->attrib_data
             && k->pkt->pkt.user_id->is_primary) {
             prefs = k->pkt->pkt.user_id->prefs;
-            mdc_feature = k->pkt->pkt.user_id->mdc_feature;
+            mdc_feature = k->pkt->pkt.user_id->flags.mdc;
             break;
         }
     }    
@@ -1781,7 +2262,7 @@ merge_selfsigs( KBNODE keyblock )
              || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
             PKT_public_key *pk = k->pkt->pkt.public_key;
             if (pk->prefs)
-                m_free (pk->prefs);
+                xfree (pk->prefs);
             pk->prefs = copy_prefs (prefs);
             pk->mdc_feature = mdc_feature;
         }
@@ -1869,7 +2350,8 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock )
                             /* The secret parts are not available so
                                we can't use that key for signing etc.
                                Fix the pubkey usage */
-                            pk->pubkey_usage &= ~PUBKEY_USAGE_SIG;
+                            pk->pubkey_usage &= ~(PUBKEY_USAGE_SIG
+                                                  |PUBKEY_USAGE_AUTH);
                         }
                         /* transfer flag bits 0 and 1 to the pubblock */
                         pub->flag |= (sec->flag &3);
@@ -1880,14 +2362,15 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock )
             if ( !sec ) {
                 KBNODE next, ll;
 
-                log_info ( "no secret subkey "
-                           "for public subkey %08lX - ignoring\n",  
-                           (ulong)keyid_from_pk (pk,NULL) );
+                if (opt.verbose)
+                  log_info (_("no secret subkey"
+                             " for public subkey %s - ignoring\n"),  
+                           keystr_from_pk (pk));
                 /* we have to remove the subkey in this case */
                 assert ( last );
                 /* find the next subkey */
                 for (next=pub->next,ll=pub;
-                     next && pub->pkt->pkttype != PKT_PUBLIC_SUBKEY;
+                     next && next->pkt->pkttype != PKT_PUBLIC_SUBKEY;
                      ll = next, next = next->next ) 
                     ;
                 /* make new link */
@@ -1945,13 +2428,14 @@ finish_lookup (GETKEY_CTX ctx)
     KBNODE k;
     KBNODE foundk = NULL;
     PKT_user_id *foundu = NULL;
-  #define USAGE_MASK  (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC)
+#define USAGE_MASK  (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT)
     unsigned int req_usage = ( ctx->req_usage & USAGE_MASK );
     /* Request the primary if we're certifying another key, and also
        if signing data while --pgp6 or --pgp7 is on since pgp 6 and 7
-       do not understand signatures made by a signing subkey. */
+       do not understand signatures made by a signing subkey.  PGP 8
+       does. */
     int req_prim = (ctx->req_usage & PUBKEY_USAGE_CERT) ||
-      ((opt.pgp6 || opt.pgp7) && (ctx->req_usage & PUBKEY_USAGE_SIG));
+      ((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG));
     u32 latest_date;
     KBNODE latest_key;
     u32 curtime = make_timestamp ();
@@ -2106,12 +2590,14 @@ finish_lookup (GETKEY_CTX ctx)
         
     ctx->found_key = latest_key;
 
-    if (latest_key != keyblock && opt.verbose) {
-        log_info(_("using secondary key %08lX "
-                   "instead of primary key %08lX\n"),
-                 (ulong)keyid_from_pk( latest_key->pkt->pkt.public_key, NULL),
-                 (ulong)keyid_from_pk( keyblock->pkt->pkt.public_key, NULL) );
-    }
+    if (latest_key != keyblock && opt.verbose)
+      {
+       char *tempkeystr=
+         xstrdup(keystr_from_pk(latest_key->pkt->pkt.public_key));
+        log_info(_("using subkey %s instead of primary key %s\n"),
+                 tempkeystr, keystr_from_pk(keyblock->pkt->pkt.public_key));
+       xfree(tempkeystr);
+      }
 
     cache_user_id( keyblock );
     
@@ -2152,12 +2638,13 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode )
 
             keyid_from_sk (k->pkt->pkt.secret_key, aki);
             k = get_pubkeyblock (aki);
-            if( !k ) {
+            if( !k )
+             {
                 if (!opt.quiet)
-                    log_info(_("key %08lX: secret key without public key "
-                               "- skipped\n"),  (ulong)aki[1] );
+                 log_info(_("key %s: secret key without public key"
+                            " - skipped\n"), keystr(aki));
                 goto skip;
-            }
+             }
             secblock = ctx->keyblock;
             ctx->keyblock = k;
 
@@ -2225,6 +2712,9 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode )
  *                   b) to get the ultimately trusted keys.
  *        The a) usage might have some problems.
  *
+ * set with_subkeys true to include subkeys
+ * set with_spm true to include secret-parts-missing keys
+ *
  * Enumerate all primary secret keys.  Caller must use these procedure:
  *  1) create a void pointer and initialize it to NULL
  *  2) pass this void pointer by reference to this function
@@ -2235,7 +2725,8 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode )
  *     so that can free it's context.
  */
 int
-enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys )
+enum_secret_keys( void **context, PKT_secret_key *sk,
+                 int with_subkeys, int with_spm )
 {
     int rc=0;
     struct {
@@ -2248,7 +2739,7 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys )
 
 
     if( !c ) { /* make a new context */
-       c = m_alloc_clear( sizeof *c );
+       c = xmalloc_clear( sizeof *c );
        *context = c;
        c->hd = keydb_new (1);
         c->first = 1;
@@ -2259,7 +2750,7 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys )
     if( !sk ) { /* free the context */
         keydb_release (c->hd);
         release_kbnode (c->keyblock);
-       m_free( c );
+       xfree( c );
        *context = NULL;
        return 0;
     }
@@ -2270,9 +2761,11 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys )
     do {
         /* get the next secret key from the current keyblock */
         for (; c->node; c->node = c->node->next) {
-            if (c->node->pkt->pkttype == PKT_SECRET_KEY
+            if ((c->node->pkt->pkttype == PKT_SECRET_KEY
                 || (with_subkeys
-                    && c->node->pkt->pkttype == PKT_SECRET_SUBKEY) ) {
+                    && c->node->pkt->pkttype == PKT_SECRET_SUBKEY) )
+               && !(c->node->pkt->pkt.secret_key->protect.s2k.mode==1001
+                    && !with_spm)) {
                 copy_secret_key (sk, c->node->pkt->pkt.secret_key );
                 c->node = c->node->next;
                 return 0; /* found */
@@ -2304,43 +2797,44 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys )
 
 /****************
  * Return a string with a printable representation of the user_id.
- * this string must be freed by m_free.
+ * this string must be freed by xfree.
  */
 char*
 get_user_id_string( u32 *keyid )
 {
-    user_id_db_t r;
-    char *p;
-    int pass=0;
-    /* try it two times; second pass reads from key resources */
-    do {
-       for(r=user_id_db; r; r = r->next ) {
-            keyid_list_t a;
-            for (a=r->keyids; a; a= a->next ) {
-                if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) {
-                    p = m_alloc( r->len + 10 );
-                    sprintf(p, "%08lX %.*s",
-                            (ulong)keyid[1], r->len, r->name );
-                    return p;
-                }
-            }
+  user_id_db_t r;
+  char *p;
+  int pass=0;
+  /* try it two times; second pass reads from key resources */
+  do
+    {
+      for(r=user_id_db; r; r = r->next )
+       {
+         keyid_list_t a;
+         for (a=r->keyids; a; a= a->next )
+           {
+             if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] )
+               {
+                 p = xmalloc( keystrlen() + 1 + r->len + 1 );
+                 sprintf(p, "%s %.*s", keystr(keyid), r->len, r->name );
+                 return p;
+               }
+           }
         }
     } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
-    p = m_alloc( 15 );
-    sprintf(p, "%08lX [?]", (ulong)keyid[1] );
-    return p;
+  p = xmalloc( keystrlen() + 5 );
+  sprintf(p, "%s [?]", keystr(keyid));
+  return p;
 }
 
 
 char*
-get_user_id_string_printable ( u32 *keyid )
+get_user_id_string_native ( u32 *keyid )
 {
-    char *p = get_user_id_string( keyid );
-    char *p2 = utf8_to_native( p, strlen(p), 0 );
-    m_free(p);
-    p = make_printable_string (p2, strlen (p2), 0);
-    m_free (p2);
-    return p;
+  char *p = get_user_id_string( keyid );
+  char *p2 = utf8_to_native( p, strlen(p), 0 );
+  xfree(p);
+  return p2;
 }
 
 
@@ -2356,7 +2850,7 @@ get_long_user_id_string( u32 *keyid )
             keyid_list_t a;
             for (a=r->keyids; a; a= a->next ) {
                 if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) {
-                    p = m_alloc( r->len + 20 );
+                    p = xmalloc( r->len + 20 );
                     sprintf(p, "%08lX%08lX %.*s",
                             (ulong)keyid[0], (ulong)keyid[1],
                             r->len, r->name );
@@ -2365,7 +2859,7 @@ get_long_user_id_string( u32 *keyid )
             }
         }
     } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
-    p = m_alloc( 25 );
+    p = xmalloc( 25 );
     sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] );
     return p;
 }
@@ -2383,7 +2877,7 @@ get_user_id( u32 *keyid, size_t *rn )
             keyid_list_t a;
             for (a=r->keyids; a; a= a->next ) {
                 if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) {
-                    p = m_alloc( r->len );
+                    p = xmalloc( r->len );
                     memcpy(p, r->name, r->len );
                     *rn = r->len;
                     return p;
@@ -2391,21 +2885,19 @@ get_user_id( u32 *keyid, size_t *rn )
             }
         }
     } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
-    p = m_strdup( _("[User id not found]") );
+    p = xstrdup( _("[User ID not found]") );
     *rn = strlen(p);
     return p;
 }
 
 char*
-get_user_id_printable( u32 *keyid )
+get_user_id_native( u32 *keyid )
 {
-    size_t rn;
-    char *p = get_user_id( keyid, &rn );
-    char *p2 = utf8_to_native( p, rn, 0 );
-    m_free(p);
-    p = make_printable_string (p2, strlen (p2), 0);
-    m_free (p2);
-    return p;
+  size_t rn;
+  char *p = get_user_id( keyid, &rn );
+  char *p2 = utf8_to_native( p, rn, 0 );
+  xfree(p);
+  return p2;
 }
 
 KEYDB_HANDLE
@@ -2413,3 +2905,85 @@ get_ctx_handle(GETKEY_CTX ctx)
 {
   return ctx->kr_handle;
 }
+
+static void
+free_akl(struct akl *akl)
+{
+  if(akl->spec)
+    free_keyserver_spec(akl->spec);
+
+  xfree(akl);
+}
+
+void
+release_akl(void)
+{
+  while(opt.auto_key_locate)
+    {
+      struct akl *akl2=opt.auto_key_locate;
+      opt.auto_key_locate=opt.auto_key_locate->next;
+      free_akl(akl2);
+    }
+}
+
+int
+parse_auto_key_locate(char *options)
+{
+  char *tok;
+
+  while((tok=optsep(&options)))
+    {
+      struct akl *akl,*last;
+      int dupe=0;
+
+      if(tok[0]=='\0')
+       continue;
+
+      akl=xmalloc_clear(sizeof(*akl));
+
+      if(ascii_strcasecmp(tok,"ldap")==0)
+       akl->type=AKL_LDAP;
+      else if(ascii_strcasecmp(tok,"keyserver")==0)
+       akl->type=AKL_KEYSERVER;
+#ifdef USE_DNS_CERT
+      else if(ascii_strcasecmp(tok,"cert")==0)
+       akl->type=AKL_CERT;
+#endif
+#ifdef USE_DNS_PKA
+      else if(ascii_strcasecmp(tok,"pka")==0)
+       akl->type=AKL_PKA;
+#endif
+      else if((akl->spec=parse_keyserver_uri(tok,1,NULL,0)))
+       akl->type=AKL_SPEC;
+      else
+       {
+         free_akl(akl);
+         return 0;
+       }
+
+      /* We must maintain the order the user gave us */
+      for(last=opt.auto_key_locate;last && last->next;last=last->next)
+       {
+         /* Check for duplicates */
+         if(last && last->type==akl->type
+            && (akl->type!=AKL_SPEC
+                || (akl->type==AKL_SPEC
+                    && strcmp(last->spec->uri,akl->spec->uri)==0)))
+           {
+             dupe=1;
+             free_akl(akl);
+             break;
+           }
+       }
+
+      if(!dupe)
+       {
+         if(last)
+           last->next=akl;
+         else
+           opt.auto_key_locate=akl;
+       }
+    }
+
+  return 1;
+}