g10: clean up of headers for card.
[gnupg.git] / kbx / keybox-search.c
index a45f617..1edb4ae 100644 (file)
@@ -1,5 +1,6 @@
 /* keybox-search.c - Search operations
- * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2012,
+ *               2013 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <assert.h>
 #include <errno.h>
 
-#include "../jnlib/stringhelp.h" /* ascii_xxxx() */
-
 #include "keybox-defs.h"
 #include <gcrypt.h>
-
+#include "host2net.h"
+#include "mbox-util.h"
 
 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
@@ -41,53 +41,45 @@ struct sn_array_s {
 };
 
 
-
-static inline ulong
-get32 (const byte *buffer)
-{
-  ulong a;
-  a =  *buffer << 24;
-  a |= buffer[1] << 16;
-  a |= buffer[2] << 8;
-  a |= buffer[3];
-  return a;
-}
-
-static inline ulong
-get16 (const byte *buffer)
-{
-  ulong a;
-  a =  *buffer << 8;
-  a |= buffer[1];
-  return a;
-}
-
+#define get32(a) buf32_to_ulong ((a))
+#define get16(a) buf16_to_ulong ((a))
 
 
-static inline int
-blob_get_type (KEYBOXBLOB blob)
+static inline unsigned int
+blob_get_blob_flags (KEYBOXBLOB blob)
 {
   const unsigned char *buffer;
   size_t length;
 
   buffer = _keybox_get_blob_image (blob, &length);
-  if (length < 32)
-    return -1; /* blob too short */
+  if (length < 8)
+    return 0; /* oops */
 
-  return buffer[4];
+  return get16 (buffer + 6);
 }
 
-static inline unsigned int
-blob_get_blob_flags (KEYBOXBLOB blob)
+
+/* Return the first keyid from the blob.  Returns true if
+   available.  */
+static int
+blob_get_first_keyid (KEYBOXBLOB blob, u32 *kid)
 {
   const unsigned char *buffer;
-  size_t length;
+  size_t length, nkeys, keyinfolen;
 
   buffer = _keybox_get_blob_image (blob, &length);
-  if (length < 8)
-    return 0; /* oops */
+  if (length < 48)
+    return 0; /* blob too short */
 
-  return get16 (buffer + 6);
+  nkeys = get16 (buffer + 16);
+  keyinfolen = get16 (buffer + 18);
+  if (!nkeys || keyinfolen < 28)
+    return 0; /* invalid blob */
+
+  kid[0] = get32 (buffer + 32);
+  kid[1] = get32 (buffer + 36);
+
+  return 1;
 }
 
 
@@ -102,7 +94,7 @@ _keybox_get_flag_location (const unsigned char *buffer, size_t length,
   size_t nkeys, keyinfolen;
   size_t nuids, uidinfolen;
   size_t nserial;
-  size_t nsigs, siginfolen;
+  size_t nsigs, siginfolen, siginfooff;
 
   switch (what)
     {
@@ -112,10 +104,11 @@ _keybox_get_flag_location (const unsigned char *buffer, size_t length,
       *flag_off = 6;
       *flag_size = 2;
       break;
-    
+
     case KEYBOX_FLAG_OWNERTRUST:
     case KEYBOX_FLAG_VALIDITY:
     case KEYBOX_FLAG_CREATED_AT:
+    case KEYBOX_FLAG_SIG_INFO:
       if (length < 20)
         return GPG_ERR_INV_OBJ;
       /* Key info. */
@@ -127,7 +120,7 @@ _keybox_get_flag_location (const unsigned char *buffer, size_t length,
       if (pos+2 > length)
         return GPG_ERR_INV_OBJ; /* Out of bounds. */
       /* Serial number. */
-      nserial = get16 (buffer+pos); 
+      nserial = get16 (buffer+pos);
       pos += 2 + nserial;
       if (pos+4 > length)
         return GPG_ERR_INV_OBJ; /* Out of bounds. */
@@ -135,15 +128,16 @@ _keybox_get_flag_location (const unsigned char *buffer, size_t length,
       nuids = get16 (buffer + pos); pos += 2;
       uidinfolen = get16 (buffer + pos); pos += 2;
       if (uidinfolen < 12 )
-        return GPG_ERR_INV_OBJ; 
+        return GPG_ERR_INV_OBJ;
       pos += uidinfolen*nuids;
       if (pos+4 > length)
         return GPG_ERR_INV_OBJ ; /* Out of bounds. */
       /* Signature info. */
+      siginfooff = pos;
       nsigs = get16 (buffer + pos); pos += 2;
       siginfolen = get16 (buffer + pos); pos += 2;
       if (siginfolen < 4 )
-        return GPG_ERR_INV_OBJ; 
+        return GPG_ERR_INV_OBJ;
       pos += siginfolen*nsigs;
       if (pos+1+1+2+4+4+4+4 > length)
         return GPG_ERR_INV_OBJ ; /* Out of bounds. */
@@ -158,6 +152,10 @@ _keybox_get_flag_location (const unsigned char *buffer, size_t length,
           *flag_size = 4;
           *flag_off += 1+2+4+4+4;
           break;
+        case KEYBOX_FLAG_SIG_INFO:
+          *flag_size = siginfolen * nsigs;
+          *flag_off = siginfooff;
+          break;
         default:
           break;
         }
@@ -171,7 +169,7 @@ _keybox_get_flag_location (const unsigned char *buffer, size_t length,
 
 
 
-/* Return one of the flags WHAT in VALUE from teh blob BUFFER of
+/* Return one of the flags WHAT in VALUE from the blob BUFFER of
    LENGTH bytes.  Return 0 on success or an raw error code. */
 static gpg_err_code_t
 get_flag_from_image (const unsigned char *buffer, size_t length,
@@ -190,7 +188,7 @@ get_flag_from_image (const unsigned char *buffer, size_t length,
       case 4: *value = get32 (buffer + pos); break;
       default: ec = GPG_ERR_BUG; break;
       }
-  
+
   return ec;
 }
 
@@ -218,7 +216,7 @@ blob_cmp_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen)
     return 0; /* out of bounds */
 
   /*serial*/
-  nserial = get16 (buffer+pos); 
+  nserial = get16 (buffer+pos);
   off = pos + 2;
   if (off+nserial > length)
     return 0; /* out of bounds */
@@ -227,6 +225,9 @@ blob_cmp_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen)
 }
 
 
+/* Returns 0 if not found or the number of the key which was found.
+   For X.509 this is always 1, for OpenPGP this is 1 for the primary
+   key and 2 and more for the subkeys.  */
 static int
 blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr)
 {
@@ -253,7 +254,7 @@ blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr)
     {
       off = pos + idx*keyinfolen;
       if (!memcmp (buffer + off, fpr, 20))
-        return 1; /* found */
+        return idx+1; /* found */
     }
   return 0; /* not found */
 }
@@ -285,7 +286,7 @@ blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr,
     {
       off = pos + idx*keyinfolen;
       if (!memcmp (buffer + off + fproff, fpr, fprlen))
-        return 1; /* found */
+        return idx+1; /* found */
     }
   return 0; /* not found */
 }
@@ -293,7 +294,7 @@ blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr,
 
 static int
 blob_cmp_name (KEYBOXBLOB blob, int idx,
-               const char *name, size_t namelen, int substr)
+               const char *name, size_t namelen, int substr, int x509)
 {
   const unsigned char *buffer;
   size_t length;
@@ -316,7 +317,7 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
     return 0; /* out of bounds */
 
   /*serial*/
-  nserial = get16 (buffer+pos); 
+  nserial = get16 (buffer+pos);
   pos += 2 + nserial;
   if (pos+4 > length)
     return 0; /* out of bounds */
@@ -330,10 +331,9 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
     return 0; /* out of bounds */
 
   if (idx < 0)
-    { /* compare all names starting with that (negated) index */
-      idx = -idx;
-      
-      for ( ;idx < nuids; idx++)
+    { /* Compare all names.  Note that for X.509 we start with index 1
+         so to skip the issuer at index 0.  */
+      for (idx = !!x509; idx < nuids; idx++)
         {
           size_t mypos = pos;
 
@@ -347,15 +347,14 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
           if (substr)
             {
               if (ascii_memcasemem (buffer+off, len, name, namelen))
-                return 1; /* found */
+                return idx+1; /* found */
             }
           else
             {
               if (len == namelen && !memcmp (buffer+off, name, len))
-                return 1; /* found */
+                return idx+1; /* found */
             }
         }
-      return 0; /* not found */
     }
   else
     {
@@ -371,20 +370,25 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
 
       if (substr)
         {
-          return !!ascii_memcasemem (buffer+off, len, name, namelen);
+          if (ascii_memcasemem (buffer+off, len, name, namelen))
+            return idx+1; /* found */
         }
       else
         {
-          return len == namelen && !memcmp (buffer+off, name, len);
+          if (len == namelen && !memcmp (buffer+off, name, len))
+            return idx+1; /* found */
         }
     }
+  return 0; /* not found */
 }
 
 
-/* compare all email addresses of the subject.  With SUBSTR given as
-   True a substring search is done in the mail address */
+/* Compare all email addresses of the subject.  With SUBSTR given as
+   True a substring search is done in the mail address.  The X509 flag
+   indicated whether the search is done on an X.509 blob.  */
 static int
-blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
+blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr,
+               int x509)
 {
   const unsigned char *buffer;
   size_t length;
@@ -409,7 +413,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
     return 0; /* out of bounds */
 
   /*serial*/
-  nserial = get16 (buffer+pos); 
+  nserial = get16 (buffer+pos);
   pos += 2 + nserial;
   if (pos+4 > length)
     return 0; /* out of bounds */
@@ -425,30 +429,68 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
   if (namelen < 1)
     return 0;
 
-  for (idx=1 ;idx < nuids; idx++)
+  /* Note that for X.509 we start at index 1 because index 0 is used
+     for the issuer name.  */
+  for (idx=!!x509 ;idx < nuids; idx++)
     {
       size_t mypos = pos;
-      
+      size_t mylen;
+
       mypos += idx*uidinfolen;
       off = get32 (buffer+mypos);
       len = get32 (buffer+mypos+4);
       if (off+len > length)
-        return 0; /* error: better stop here out of bounds */
-      if (len < 2 || buffer[off] != '<')
-        continue; /* empty name or trailing 0 not stored */
-      len--; /* one back */
-      if ( len < 3 || buffer[off+len] != '>')
-        continue; /* not a proper email address */
-      len--; 
+        return 0; /* error: better stop here - out of bounds */
+      if (x509)
+        {
+          if (len < 2 || buffer[off] != '<')
+            continue; /* empty name or trailing 0 not stored */
+          len--; /* one back */
+          if ( len < 3 || buffer[off+len] != '>')
+            continue; /* not a proper email address */
+          off++;
+          len--;
+        }
+      else /* OpenPGP.  */
+        {
+          /* We need to forward to the mailbox part.  */
+          mypos = off;
+          mylen = len;
+          for ( ; len && buffer[off] != '<'; len--, off++)
+            ;
+          if (len < 2 || buffer[off] != '<')
+            {
+              /* Mailbox not explicitly given or too short.  Restore
+                 OFF and LEN and check whether the entire string
+                 resembles a mailbox without the angle brackets.  */
+              off = mypos;
+              len = mylen;
+              if (!is_valid_mailbox_mem (buffer+off, len))
+                continue; /* Not a mail address. */
+            }
+          else /* Seems to be standard user id with mail address.  */
+            {
+              off++; /* Point to first char of the mail address.  */
+              len--;
+              /* Search closing '>'.  */
+              for (mypos=off; len && buffer[mypos] != '>'; len--, mypos++)
+                ;
+              if (!len || buffer[mypos] != '>' || off == mypos)
+                continue; /* Not a proper mail address.  */
+              len = mypos - off;
+            }
+
+        }
+
       if (substr)
         {
-          if (ascii_memcasemem (buffer+off+1, len, name, namelen))
-            return 1; /* found */
+          if (ascii_memcasemem (buffer+off, len, name, namelen))
+            return idx+1; /* found */
         }
       else
         {
-          if (len == namelen && !ascii_memcasecmp (buffer+off+1, name, len))
-            return 1; /* found */
+          if (len == namelen && !ascii_memcasecmp (buffer+off, name, len))
+            return idx+1; /* found */
         }
     }
   return 0; /* not found */
@@ -457,8 +499,8 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
 
 #ifdef KEYBOX_WITH_X509
 /* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
-   We don't have the keygrips as meta data, thus wen need to parse the
-   certificate. Fixme: We might wat to return proper error codes
+   We don't have the keygrips as meta data, thus we need to parse the
+   certificate. Fixme: We might want to return proper error codes
    instead of failing a search for invalid certificates etc.  */
 static int
 blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
@@ -474,7 +516,7 @@ blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
   unsigned char array[20];
   unsigned char *rcp;
   size_t n;
-  
+
   buffer = _keybox_get_blob_image (blob, &length);
   if (length < 40)
     return 0; /* Too short. */
@@ -527,18 +569,32 @@ blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
 
 \f
 /*
-  The has_foo functions are used as helpers for search 
+  The has_foo functions are used as helpers for search
 */
 static inline int
-has_short_kid (KEYBOXBLOB blob, const unsigned char *kid)
+has_short_kid (KEYBOXBLOB blob, u32 lkid)
 {
-  return blob_cmp_fpr_part (blob, kid+4, 16, 4);
+  unsigned char buf[4];
+  buf[0] = lkid >> 24;
+  buf[1] = lkid >> 16;
+  buf[2] = lkid >> 8;
+  buf[3] = lkid;
+  return blob_cmp_fpr_part (blob, buf, 16, 4);
 }
 
 static inline int
-has_long_kid (KEYBOXBLOB blob, const unsigned char *kid)
+has_long_kid (KEYBOXBLOB blob, u32 mkid, u32 lkid)
 {
-  return blob_cmp_fpr_part (blob, kid, 12, 8);
+  unsigned char buf[8];
+  buf[0] = mkid >> 24;
+  buf[1] = mkid >> 16;
+  buf[2] = mkid >> 8;
+  buf[3] = mkid;
+  buf[4] = lkid >> 24;
+  buf[5] = lkid >> 16;
+  buf[6] = lkid >> 8;
+  buf[7] = lkid;
+  return blob_cmp_fpr_part (blob, buf, 12, 8);
 }
 
 static inline int
@@ -551,8 +607,11 @@ static inline int
 has_keygrip (KEYBOXBLOB blob, const unsigned char *grip)
 {
 #ifdef KEYBOX_WITH_X509
-  if (blob_get_type (blob) == BLOBTYPE_X509)
+  if (blob_get_type (blob) == KEYBOX_BLOBTYPE_X509)
     return blob_x509_has_grip (blob, grip);
+#else
+  (void)blob;
+  (void)grip;
 #endif
   return 0;
 }
@@ -565,11 +624,11 @@ has_issuer (KEYBOXBLOB blob, const char *name)
 
   return_val_if_fail (name, 0);
 
-  if (blob_get_type (blob) != BLOBTYPE_X509)
+  if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509)
     return 0;
 
   namelen = strlen (name);
-  return blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0);
+  return blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0, 1);
 }
 
 static inline int
@@ -581,13 +640,13 @@ has_issuer_sn (KEYBOXBLOB blob, const char *name,
   return_val_if_fail (name, 0);
   return_val_if_fail (sn, 0);
 
-  if (blob_get_type (blob) != BLOBTYPE_X509)
+  if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509)
     return 0;
 
   namelen = strlen (name);
-  
+
   return (blob_cmp_sn (blob, sn, snlen)
-          && blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0));
+          && blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0, 1));
 }
 
 static inline int
@@ -595,7 +654,7 @@ has_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen)
 {
   return_val_if_fail (sn, 0);
 
-  if (blob_get_type (blob) != BLOBTYPE_X509)
+  if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509)
     return 0;
   return blob_cmp_sn (blob, sn, snlen);
 }
@@ -607,26 +666,29 @@ has_subject (KEYBOXBLOB blob, const char *name)
 
   return_val_if_fail (name, 0);
 
-  if (blob_get_type (blob) != BLOBTYPE_X509)
+  if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509)
     return 0;
 
   namelen = strlen (name);
-  return blob_cmp_name (blob, 1 /* subject */, name, namelen, 0);
+  return blob_cmp_name (blob, 1 /* subject */, name, namelen, 0, 1);
 }
 
+
 static inline int
-has_subject_or_alt (KEYBOXBLOB blob, const char *name, int substr)
+has_username (KEYBOXBLOB blob, const char *name, int substr)
 {
   size_t namelen;
+  int btype;
 
   return_val_if_fail (name, 0);
 
-  if (blob_get_type (blob) != BLOBTYPE_X509)
+  btype = blob_get_type (blob);
+  if (btype != KEYBOX_BLOBTYPE_PGP && btype != KEYBOX_BLOBTYPE_X509)
     return 0;
 
   namelen = strlen (name);
-  return blob_cmp_name (blob, -1 /* all subject names*/, name,
-                        namelen, substr);
+  return blob_cmp_name (blob, -1 /* all subject/user names */, name,
+                        namelen, substr, (btype == KEYBOX_BLOBTYPE_X509));
 }
 
 
@@ -634,16 +696,22 @@ static inline int
 has_mail (KEYBOXBLOB blob, const char *name, int substr)
 {
   size_t namelen;
+  int btype;
 
   return_val_if_fail (name, 0);
 
-  if (blob_get_type (blob) != BLOBTYPE_X509)
+  btype = blob_get_type (blob);
+  if (btype != KEYBOX_BLOBTYPE_PGP && btype != KEYBOX_BLOBTYPE_X509)
     return 0;
 
+  if (btype == KEYBOX_BLOBTYPE_PGP && *name == '<')
+    name++; /* Hack to remove the leading '<' for gpg.  */
+
   namelen = strlen (name);
   if (namelen && name[namelen-1] == '>')
     namelen--;
-  return blob_cmp_mail (blob, name, namelen, substr);
+  return blob_cmp_mail (blob, name, namelen, substr,
+                        (btype == KEYBOX_BLOBTYPE_X509));
 }
 
 
@@ -664,7 +732,7 @@ release_sn_array (struct sn_array_s *array, size_t size)
 
 */
 
-int 
+int
 keybox_search_reset (KEYBOX_HANDLE hd)
 {
   if (!hd)
@@ -683,20 +751,26 @@ keybox_search_reset (KEYBOX_HANDLE hd)
     }
   hd->error = 0;
   hd->eof = 0;
-  return 0;   
+  return 0;
 }
 
 
 /* Note: When in ephemeral mode the search function does visit all
-   blobs but in standard mode, blobs flagged as ephemeral are ignored.  */
-int 
-keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
+   blobs but in standard mode, blobs flagged as ephemeral are ignored.
+   If WANT_BLOBTYPE is not 0 only blobs of this type are considered.
+   The value at R_SKIPPED is updated by the number of skipped long
+   records (counts PGP and X.509). */
+int
+keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
+               keybox_blobtype_t want_blobtype,
+               size_t *r_descindex, unsigned long *r_skipped)
 {
   int rc;
   size_t n;
   int need_words, any_skip;
   KEYBOXBLOB blob = NULL;
   struct sn_array_s *sn_array = NULL;
+  int pk_no, uid_no;
 
   if (!hd)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -708,18 +782,18 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
       hd->found.blob = NULL;
     }
 
-  if (hd->error)  
+  if (hd->error)
     return hd->error; /* still in error state */
-  if (hd->eof)  
+  if (hd->eof)
     return -1; /* still EOF */
 
   /* figure out what information we need */
   need_words = any_skip = 0;
-  for (n=0; n < ndesc; n++) 
+  for (n=0; n < ndesc; n++)
     {
-      switch (desc[n].mode) 
+      switch (desc[n].mode)
         {
-        case KEYDB_SEARCH_MODE_WORDS: 
+        case KEYDB_SEARCH_MODE_WORDS:
           need_words = 1;
           break;
         case KEYDB_SEARCH_MODE_FIRST:
@@ -729,38 +803,40 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
         default:
           break;
        }
-      if (desc[n].skipfnc) 
+      if (desc[n].skipfnc)
         any_skip = 1;
       if (desc[n].snlen == -1 && !sn_array)
         {
           sn_array = xtrycalloc (ndesc, sizeof *sn_array);
           if (!sn_array)
-            return (hd->error = gpg_error (gpg_err_code_from_errno (errno)));
+            return (hd->error = gpg_error_from_syserror ());
         }
     }
 
+  (void)need_words;  /* Not yet implemented.  */
+
   if (!hd->fp)
     {
       hd->fp = fopen (hd->kb->fname, "rb");
       if (!hd->fp)
         {
-          hd->error = gpg_error (gpg_err_code_from_errno (errno));
+          hd->error = gpg_error_from_syserror ();
           xfree (sn_array);
           return hd->error;
         }
     }
 
-  /* kludge: we need to convert an SN given as hexstring to it's
-     binary representation - in some cases we are not able to store it
-     in the search descriptor, because due to its usage it is not
-     possible to free allocated memory */
+  /* Kludge: We need to convert an SN given as hexstring to its binary
+     representation - in some cases we are not able to store it in the
+     search descriptor, because due to the way we use it, it is not
+     possible to free allocated memory. */
   if (sn_array)
     {
       const unsigned char *s;
       int i, odd;
       size_t snlen;
 
-      for (n=0; n < ndesc; n++) 
+      for (n=0; n < ndesc; n++)
         {
           if (!desc[n].sn)
             ;
@@ -776,7 +852,7 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
               sn_array[n].sn = xtrymalloc (snlen);
               if (!sn_array[n].sn)
                 {
-                  hd->error = gpg_error (gpg_err_code_from_errno (errno));
+                  hd->error = gpg_error_from_syserror ();
                   release_sn_array (sn_array, n);
                   return hd->error;
                 }
@@ -800,7 +876,7 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
               sn_array[n].sn = xtrymalloc (snlen);
               if (!sn_array[n].sn)
                 {
-                  hd->error = gpg_error (gpg_err_code_from_errno (errno));
+                  hd->error = gpg_error_from_syserror ();
                   release_sn_array (sn_array, n);
                   return hd->error;
                 }
@@ -811,49 +887,64 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
     }
 
 
+  pk_no = uid_no = 0;
   for (;;)
     {
       unsigned int blobflags;
+      int blobtype;
 
       _keybox_release_blob (blob); blob = NULL;
       rc = _keybox_read_blob (&blob, hd->fp);
+      if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE
+          && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX)
+        {
+          ++*r_skipped;
+          continue; /* Skip too large records.  */
+        }
+
       if (rc)
         break;
 
-      if (blob_get_type (blob) == BLOBTYPE_HEADER)
+      blobtype = blob_get_type (blob);
+      if (blobtype == KEYBOX_BLOBTYPE_HEADER)
+        continue;
+      if (want_blobtype && blobtype != want_blobtype)
         continue;
-
 
       blobflags = blob_get_blob_flags (blob);
       if (!hd->ephemeral && (blobflags & 2))
         continue; /* Not in ephemeral mode but blob is flagged ephemeral.  */
 
-      for (n=0; n < ndesc; n++) 
+      for (n=0; n < ndesc; n++)
         {
           switch (desc[n].mode)
             {
-            case KEYDB_SEARCH_MODE_NONE: 
+            case KEYDB_SEARCH_MODE_NONE:
               never_reached ();
               break;
-            case KEYDB_SEARCH_MODE_EXACT: 
-              if (has_subject_or_alt (blob, desc[n].u.name, 0))
+            case KEYDB_SEARCH_MODE_EXACT:
+              uid_no = has_username (blob, desc[n].u.name, 0);
+              if (uid_no)
                 goto found;
               break;
             case KEYDB_SEARCH_MODE_MAIL:
-              if (has_mail (blob, desc[n].u.name, 0))
+              uid_no = has_mail (blob, desc[n].u.name, 0);
+              if (uid_no)
                 goto found;
               break;
             case KEYDB_SEARCH_MODE_MAILSUB:
-              if (has_mail (blob, desc[n].u.name, 1))
+              uid_no = has_mail (blob, desc[n].u.name, 1);
+              if (uid_no)
                 goto found;
               break;
             case KEYDB_SEARCH_MODE_SUBSTR:
-              if (has_subject_or_alt (blob, desc[n].u.name, 1))
+              uid_no =  has_username (blob, desc[n].u.name, 1);
+              if (uid_no)
                 goto found;
               break;
             case KEYDB_SEARCH_MODE_MAILEND:
-            case KEYDB_SEARCH_MODE_WORDS: 
-              never_reached (); /* not yet implemented */
+            case KEYDB_SEARCH_MODE_WORDS:
+              /* not yet implemented */
               break;
             case KEYDB_SEARCH_MODE_ISSUER:
               if (has_issuer (blob, desc[n].u.name))
@@ -874,56 +965,68 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
               if (has_subject (blob, desc[n].u.name))
                 goto found;
               break;
-            case KEYDB_SEARCH_MODE_SHORT_KID: 
-              if (has_short_kid (blob, desc[n].u.kid))
+            case KEYDB_SEARCH_MODE_SHORT_KID:
+              pk_no = has_short_kid (blob, desc[n].u.kid[1]);
+              if (pk_no)
                 goto found;
               break;
             case KEYDB_SEARCH_MODE_LONG_KID:
-              if (has_long_kid (blob, desc[n].u.kid))
+              pk_no = has_long_kid (blob, desc[n].u.kid[0], desc[n].u.kid[1]);
+              if (pk_no)
                 goto found;
               break;
             case KEYDB_SEARCH_MODE_FPR:
             case KEYDB_SEARCH_MODE_FPR20:
-              if (has_fingerprint (blob, desc[n].u.fpr))
+              pk_no = has_fingerprint (blob, desc[n].u.fpr);
+              if (pk_no)
                 goto found;
               break;
             case KEYDB_SEARCH_MODE_KEYGRIP:
               if (has_keygrip (blob, desc[n].u.grip))
                 goto found;
               break;
-            case KEYDB_SEARCH_MODE_FIRST: 
+            case KEYDB_SEARCH_MODE_FIRST:
               goto found;
               break;
-            case KEYDB_SEARCH_MODE_NEXT: 
+            case KEYDB_SEARCH_MODE_NEXT:
               goto found;
               break;
-            default: 
+            default:
               rc = gpg_error (GPG_ERR_INV_VALUE);
               goto found;
             }
        }
       continue;
-    found:  
-      for (n=any_skip?0:ndesc; n < ndesc; n++) 
+    found:
+      /* Record which DESC we matched on.  Note this value is only
+        meaningful if this function returns with no errors. */
+      if(r_descindex)
+       *r_descindex = n;
+      for (n=any_skip?0:ndesc; n < ndesc; n++)
         {
-/*            if (desc[n].skipfnc */
-/*                && desc[n].skipfnc (desc[n].skipfncvalue, aki)) */
-/*              break; */
+          u32 kid[2];
+
+          if (desc[n].skipfnc
+              && blob_get_first_keyid (blob, kid)
+             && desc[n].skipfnc (desc[n].skipfncvalue, kid, uid_no))
+               break;
         }
       if (n == ndesc)
         break; /* got it */
     }
-  
+
   if (!rc)
     {
       hd->found.blob = blob;
+      hd->found.pk_no = pk_no;
+      hd->found.uid_no = uid_no;
     }
   else if (rc == -1)
     {
       _keybox_release_blob (blob);
       hd->eof = 1;
     }
-  else 
+  else
     {
       _keybox_release_blob (blob);
       hd->error = rc;
@@ -942,6 +1045,65 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
    Functions to return a certificate or a keyblock.  To be used after
    a successful search operation.
 */
+
+
+/* Return the last found keyblock.  Returns 0 on success and stores a
+   new iobuf at R_IOBUF and a signature status vector at R_SIGSTATUS
+   in that case.  R_UID_NO and R_PK_NO are used to retun the number of
+   the key or user id which was matched the search criteria; if not
+   known they are set to 0. */
+gpg_error_t
+keybox_get_keyblock (KEYBOX_HANDLE hd, iobuf_t *r_iobuf,
+                     int *r_pk_no, int *r_uid_no, u32 **r_sigstatus)
+{
+  gpg_error_t err;
+  const unsigned char *buffer, *p;
+  size_t length;
+  size_t image_off, image_len;
+  size_t siginfo_off, siginfo_len;
+  u32 *sigstatus, n, n_sigs, sigilen;
+
+  *r_iobuf = NULL;
+  *r_sigstatus = NULL;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!hd->found.blob)
+    return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+  if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP)
+    return gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
+
+  buffer = _keybox_get_blob_image (hd->found.blob, &length);
+  if (length < 40)
+    return gpg_error (GPG_ERR_TOO_SHORT);
+  image_off = get32 (buffer+8);
+  image_len = get32 (buffer+12);
+  if (image_off+image_len > length)
+    return gpg_error (GPG_ERR_TOO_SHORT);
+
+  err = _keybox_get_flag_location (buffer, length, KEYBOX_FLAG_SIG_INFO,
+                                   &siginfo_off, &siginfo_len);
+  if (err)
+    return err;
+  n_sigs  = get16 (buffer + siginfo_off);
+  sigilen = get16 (buffer + siginfo_off + 2);
+  p = buffer + siginfo_off + 4;
+  sigstatus = xtrymalloc ((1+n_sigs) * sizeof *sigstatus);
+  if (!sigstatus)
+    return gpg_error_from_syserror ();
+  sigstatus[0] = n_sigs;
+  for (n=1; n <= n_sigs; n++, p += sigilen)
+    sigstatus[n] = get32 (p);
+
+  *r_pk_no  = hd->found.pk_no;
+  *r_uid_no = hd->found.uid_no;
+  *r_sigstatus = sigstatus;
+  *r_iobuf = iobuf_temp_with_content (buffer+image_off, image_len);
+  return 0;
+}
+
+
 #ifdef KEYBOX_WITH_X509
 /*
   Return the last found cert.  Caller must free it.
@@ -961,7 +1123,7 @@ keybox_get_cert (KEYBOX_HANDLE hd, ksba_cert_t *r_cert)
   if (!hd->found.blob)
     return gpg_error (GPG_ERR_NOTHING_FOUND);
 
-  if (blob_get_type (hd->found.blob) != BLOBTYPE_X509)
+  if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_X509)
     return gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
 
   buffer = _keybox_get_blob_image (hd->found.blob, &length);
@@ -1015,6 +1177,8 @@ keybox_get_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int *value)
   size_t length;
   gpg_err_code_t ec;
 
+  (void)idx; /* Not yet used.  */
+
   if (!hd)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!hd->found.blob)
@@ -1025,3 +1189,39 @@ keybox_get_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int *value)
   return ec? gpg_error (ec):0;
 }
 
+off_t
+keybox_offset (KEYBOX_HANDLE hd)
+{
+  if (!hd->fp)
+    return 0;
+  return ftello (hd->fp);
+}
+
+gpg_error_t
+keybox_seek (KEYBOX_HANDLE hd, off_t offset)
+{
+  int err;
+
+  if (hd->error)
+    return hd->error; /* still in error state */
+
+  if (! hd->fp)
+    {
+      if (offset == 0)
+        /* No need to open the file.  An unopened file is effectively at
+           offset 0.  */
+        return 0;
+
+      hd->fp = fopen (hd->kb->fname, "rb");
+      if (!hd->fp)
+        {
+          hd->error = gpg_error_from_syserror ();
+          return hd->error;
+        }
+    }
+
+  err = fseeko (hd->fp, offset, SEEK_SET);
+  hd->error = gpg_error_from_errno (err);
+
+  return hd->error;
+}