Backport bug fix. Fixes bug#1240.
[gnupg.git] / g10 / getkey.c
index c1d21f1..54843cf 100644 (file)
@@ -1,6 +1,6 @@
 /* getkey.c -  Get a key from the database
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- *               2006, 2007 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.
  *
@@ -47,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;
@@ -759,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)
@@ -912,64 +915,122 @@ key_byname( GETKEY_CTX *retctx, strlist_t 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 --auto-key-locate
-   has been enabled, we try to import the key via the online mechanisms
-   defined by --auto-key-locate.  */
+   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_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)
     {
-      struct akl *akl;
-
-      for(akl=opt.auto_key_locate;akl;akl=akl->next)
+      for (akl=opt.auto_key_locate; akl; akl=akl->next)
        {
-         unsigned char *fpr=NULL;
+         unsigned char *fpr = NULL;
          size_t fpr_len;
-
-         switch(akl->type)
+          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++;
              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:
+              mechanism = "PKA";
              glo_ctrl.in_auto_key_retrieve++;
              rc=keyserver_import_pka(name,&fpr,&fpr_len);
              glo_ctrl.in_auto_key_retrieve--;
-
-             if(rc==0)
-               log_info(_("automatically retrieved `%s' via %s\n"),
-                        name,"PKA");
              break;
 
            case AKL_LDAP:
+              mechanism = "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:
@@ -979,32 +1040,31 @@ 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++;
                  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);
                }
+              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++;
                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
@@ -1012,9 +1072,8 @@ get_pubkey_byname (PKT_public_key *pk,
             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)
+         if (!rc && fpr)
            {
-             int i;
              char fpr_string[MAX_FINGERPRINT_LEN*2+1];
 
              assert(fpr_len<=MAX_FINGERPRINT_LEN);
@@ -1022,28 +1081,64 @@ get_pubkey_byname (PKT_public_key *pk,
              free_strlist(namelist);
              namelist=NULL;
 
-             for(i=0;i<fpr_len;i++)
-               sprintf(fpr_string+2*i,"%02X",fpr[i]);
-
+              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 );
-
-             xfree(fpr);
            }
-
-         rc = key_byname( NULL, namelist, pk, NULL, 0,
-                          include_unusable, ret_keyblock, ret_kdbhd);
-         if(rc!=G10ERR_NO_PUBKEY)
-           break;
+          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_t names, KBNODE *ret_keyblock )
@@ -1069,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 );
     }
@@ -2042,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 )
@@ -2148,48 +2264,74 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
 
     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);
+               }
+           }
 
-       if(p)
-         {
-           PKT_signature *backsig=xmalloc_clear(sizeof(PKT_signature));
-           IOBUF backsig_buf=iobuf_temp_with_content(p,n);
-           int save_mode=set_packet_list_mode(0);
+       seq=0;
 
-           if(parse_signature(backsig_buf,PKT_SIGNATURE,n,backsig)==0)
-             {
-               if(check_backsig(mainpk,subpk,backsig)==0)
-                 subpk->backsig=2;
-               else
-                 subpk->backsig=1;
-             }
+       /* It is safe to have this in the unhashed area since the 0x19
+          is located on the selfsig for convenience, not security. */
 
-           set_packet_list_mode(save_mode);
+       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(backsig)
+         {
+           /* At ths point, backsig contains the most recent 0x19 sig.
+              Let's see if it is good. */
+
+           /* 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);
          }
       }
@@ -2498,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 */
@@ -2554,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;
             }
@@ -2640,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;
@@ -2951,6 +3088,7 @@ release_akl(void)
     }
 }
 
+/* Returns false on error. */
 int
 parse_auto_key_locate(char *options)
 {
@@ -2966,7 +3104,11 @@ parse_auto_key_locate(char *options)
 
       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;