scd: Use pipe to kick the loop on NetBSD.
[gnupg.git] / kbx / keybox-search.c
index bf47042..a5fc7fa 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <assert.h>
 #include <errno.h>
 
-#include "../common/stringhelp.h" /* ascii_xxxx() */
-
 #include "keybox-defs.h"
 #include <gcrypt.h>
-
+#include "../common/host2net.h"
+#include "../common/mbox-util.h"
 
 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
@@ -42,27 +41,8 @@ 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 unsigned int
@@ -79,6 +59,30 @@ 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, nkeys, keyinfolen;
+
+  buffer = _keybox_get_blob_image (blob, &length);
+  if (length < 48)
+    return 0; /* blob too short */
+
+  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;
+}
+
+
 /* Return information on the flag WHAT within the blob BUFFER,LENGTH.
    Return the offset and the length (in bytes) of the flag in
    FLAGOFF,FLAG_SIZE. */
@@ -165,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,
@@ -380,8 +384,8 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
 
 
 /* Compare all email addresses of the subject.  With SUBSTR given as
-   True a substring search is done in the mail address.  If X509
-   states whether thr search is done on an X.509 blob.  */
+   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,
                int x509)
@@ -425,37 +429,67 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr,
   if (namelen < 1)
     return 0;
 
-  /* Note that for X.509 we start at index 1 becuase index 0 is used
+  /* 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 (!x509)
+        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.  */
         {
-          /* For OpenPGP we need to forward to the mailbox part.  */
-          for ( ;len && buffer[off] != '<'; len--, off++)
+          /* 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 (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--;
+
       if (substr)
         {
-          if (ascii_memcasemem (buffer+off+1, len, name, namelen))
+          if (ascii_memcasemem (buffer+off, len, name, namelen))
             return idx+1; /* found */
         }
       else
         {
-          if (len == namelen && !ascii_memcasecmp (buffer+off+1, name, len))
+          if (len == namelen && !ascii_memcasecmp (buffer+off, name, len))
             return idx+1; /* found */
         }
     }
@@ -573,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;
 }
@@ -587,7 +624,7 @@ 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);
@@ -603,7 +640,7 @@ 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);
@@ -617,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);
 }
@@ -629,7 +666,7 @@ 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);
@@ -646,12 +683,12 @@ has_username (KEYBOXBLOB blob, const char *name, int substr)
   return_val_if_fail (name, 0);
 
   btype = blob_get_type (blob);
-  if (btype != BLOBTYPE_PGP && btype != BLOBTYPE_X509)
+  if (btype != KEYBOX_BLOBTYPE_PGP && btype != KEYBOX_BLOBTYPE_X509)
     return 0;
 
   namelen = strlen (name);
   return blob_cmp_name (blob, -1 /* all subject/user names */, name,
-                        namelen, substr, (btype == BLOBTYPE_X509));
+                        namelen, substr, (btype == KEYBOX_BLOBTYPE_X509));
 }
 
 
@@ -664,16 +701,17 @@ has_mail (KEYBOXBLOB blob, const char *name, int substr)
   return_val_if_fail (name, 0);
 
   btype = blob_get_type (blob);
-  if (btype != BLOBTYPE_PGP && btype != BLOBTYPE_X509)
+  if (btype != KEYBOX_BLOBTYPE_PGP && btype != KEYBOX_BLOBTYPE_X509)
     return 0;
 
-  if (btype == BLOBTYPE_PGP && *name == '<')
+  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, (btype == BLOBTYPE_X509));
+  return blob_cmp_mail (blob, name, namelen, substr,
+                        (btype == KEYBOX_BLOBTYPE_X509));
 }
 
 
@@ -687,6 +725,23 @@ release_sn_array (struct sn_array_s *array, size_t size)
   xfree (array);
 }
 
+
+/* Helper to open the file.  */
+static gpg_error_t
+open_file (KEYBOX_HANDLE hd)
+{
+
+  hd->fp = fopen (hd->kb->fname, "rb");
+  if (!hd->fp)
+    {
+      hd->error = gpg_error_from_syserror ();
+      return hd->error;
+    }
+
+  return 0;
+}
+
+
 \f
 /*
 
@@ -694,7 +749,7 @@ release_sn_array (struct sn_array_s *array, size_t size)
 
 */
 
-int
+gpg_error_t
 keybox_search_reset (KEYBOX_HANDLE hd)
 {
   if (!hd)
@@ -708,8 +763,13 @@ keybox_search_reset (KEYBOX_HANDLE hd)
 
   if (hd->fp)
     {
-      fclose (hd->fp);
-      hd->fp = NULL;
+      if (fseeko (hd->fp, 0, SEEK_SET))
+        {
+          /* Ooops.  Seek did not work.  Close so that the search will
+           * open the file again.  */
+          fclose (hd->fp);
+          hd->fp = NULL;
+        }
     }
   hd->error = 0;
   hd->eof = 0;
@@ -719,13 +779,15 @@ keybox_search_reset (KEYBOX_HANDLE hd)
 
 /* Note: When in ephemeral mode the search function does visit all
    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
+gpg_error_t
 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;
+  gpg_error_t rc;
   size_t n;
   int need_words, any_skip;
   KEYBOXBLOB blob = NULL;
@@ -777,12 +839,11 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
 
   if (!hd->fp)
     {
-      hd->fp = fopen (hd->kb->fname, "rb");
-      if (!hd->fp)
+      rc = open_file (hd);
+      if (rc)
         {
-          hd->error = gpg_error_from_syserror ();
           xfree (sn_array);
-          return hd->error;
+          return rc;
         }
     }
 
@@ -851,9 +912,10 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
   for (;;)
     {
       unsigned int blobflags;
+      int blobtype;
 
       _keybox_release_blob (blob); blob = NULL;
-      rc = _keybox_read_blob (&blob, hd->fp);
+      rc = _keybox_read_blob (&blob, hd->fp, NULL);
       if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE
           && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX)
         {
@@ -864,9 +926,11 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
       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))
@@ -901,7 +965,7 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
               break;
             case KEYDB_SEARCH_MODE_MAILEND:
             case KEYDB_SEARCH_MODE_WORDS:
-              never_reached (); /* not yet implemented */
+              /* not yet implemented */
               break;
             case KEYDB_SEARCH_MODE_ISSUER:
               if (has_issuer (blob, desc[n].u.name))
@@ -961,9 +1025,12 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
        *r_descindex = n;
       for (n=any_skip?0:ndesc; n < ndesc; n++)
         {
-/*            if (desc[n].skipfnc */
-/*                && desc[n].skipfnc (desc[n].skipfncvalue, aki, NULL)) */
-/*              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 */
@@ -975,7 +1042,7 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
       hd->found.pk_no = pk_no;
       hd->found.uid_no = uid_no;
     }
-  else if (rc == -1)
+  else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
     {
       _keybox_release_blob (blob);
       hd->eof = 1;
@@ -1002,30 +1069,27 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc,
 
 
 /* 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. */
+ * new iobuf at R_IOBUF.  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)
+                     int *r_pk_no, int *r_uid_no)
 {
   gpg_error_t err;
-  const unsigned char *buffer, *p;
+  const unsigned char *buffer;
   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) != BLOBTYPE_PGP)
+  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);
@@ -1040,19 +1104,9 @@ keybox_get_keyblock (KEYBOX_HANDLE hd, iobuf_t *r_iobuf,
                                    &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;
 }
@@ -1077,7 +1131,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);
@@ -1142,3 +1196,39 @@ keybox_get_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int *value)
   ec = get_flag_from_image (buffer, length, what, 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)
+{
+  gpg_error_t err;
+
+  if (hd->error)
+    return hd->error; /* still in error state */
+
+  if (! hd->fp)
+    {
+      if (!offset)
+        {
+          /* No need to open the file.  An unopened file is effectively at
+             offset 0.  */
+          return 0;
+        }
+
+      err = open_file (hd);
+      if (err)
+        return err;
+    }
+
+  err = fseeko (hd->fp, offset, SEEK_SET);
+  hd->error = gpg_error_from_errno (err);
+
+  return hd->error;
+}