diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index 1600861a8..77469a24c 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -1,1347 +1,1347 @@ /* keybox-search.c - Search operations * Copyright (C) 2001, 2002, 2003, 2004, 2012, * 2013 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . */ #include #include #include #include #include #include #include "keybox-defs.h" #include #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)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) struct sn_array_s { int snlen; unsigned char *sn; }; #define get32(a) buf32_to_ulong ((a)) #define get16(a) buf16_to_ulong ((a)) 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 < 8) return 0; /* oops */ return get16 (buffer + 6); } /* 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; int fpr32; buffer = _keybox_get_blob_image (blob, &length); if (length < 48) return 0; /* blob too short */ fpr32 = buffer[5] == 2; if (fpr32 && length < 56) return 0; /* blob to short */ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18); if (!nkeys || keyinfolen < (fpr32?56:28)) return 0; /* invalid blob */ if (fpr32 && (get16 (buffer + 20 + 32) & 0x80)) { /* 32 byte fingerprint. */ kid[0] = get32 (buffer + 20); kid[1] = get32 (buffer + 20 + 4); } else /* 20 byte fingerprint. */ { kid[0] = get32 (buffer + 20 + 12); kid[1] = get32 (buffer + 20 + 16); } 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. */ gpg_err_code_t _keybox_get_flag_location (const unsigned char *buffer, size_t length, int what, size_t *flag_off, size_t *flag_size) { size_t pos; size_t nkeys, keyinfolen; size_t nuids, uidinfolen; size_t nserial; size_t nsigs, siginfolen, siginfooff; switch (what) { case KEYBOX_FLAG_BLOB: if (length < 8) return GPG_ERR_INV_OBJ; *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. */ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return GPG_ERR_INV_OBJ; pos = 20 + keyinfolen*nkeys; if (pos+2 > length) return GPG_ERR_INV_OBJ; /* Out of bounds. */ /* Serial number. */ nserial = get16 (buffer+pos); pos += 2 + nserial; if (pos+4 > length) return GPG_ERR_INV_OBJ; /* Out of bounds. */ /* User IDs. */ nuids = get16 (buffer + pos); pos += 2; uidinfolen = get16 (buffer + pos); pos += 2; if (uidinfolen < 12 ) 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; pos += siginfolen*nsigs; if (pos+1+1+2+4+4+4+4 > length) return GPG_ERR_INV_OBJ ; /* Out of bounds. */ *flag_size = 1; *flag_off = pos; switch (what) { case KEYBOX_FLAG_VALIDITY: *flag_off += 1; break; case KEYBOX_FLAG_CREATED_AT: *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; } break; default: return GPG_ERR_INV_FLAG; } return 0; } /* 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, int what, unsigned int *value) { gpg_err_code_t ec; size_t pos, size; *value = 0; ec = _keybox_get_flag_location (buffer, length, what, &pos, &size); if (!ec) switch (size) { case 1: *value = buffer[pos]; break; case 2: *value = get16 (buffer + pos); break; case 4: *value = get32 (buffer + pos); break; default: ec = GPG_ERR_BUG; break; } return ec; } static int blob_cmp_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen) { const unsigned char *buffer; size_t length; size_t pos, off; size_t nkeys, keyinfolen; size_t nserial; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20 + keyinfolen*nkeys; if (pos+2 > length) return 0; /* out of bounds */ /*serial*/ nserial = get16 (buffer+pos); off = pos + 2; if (off+nserial > length) return 0; /* out of bounds */ return nserial == snlen && !memcmp (buffer+off, sn, 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, unsigned int fprlen) { const unsigned char *buffer; size_t length; size_t pos, off; size_t nkeys, keyinfolen; int idx, fpr32, storedfprlen; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ fpr32 = buffer[5] == 2; /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < (fpr32?56:28)) return 0; /* invalid blob */ pos = 20; if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length) return 0; /* out of bounds */ for (idx=0; idx < nkeys; idx++) { off = pos + idx*keyinfolen; if (fpr32) storedfprlen = (get16 (buffer + off + 32) & 0x80)? 32:20; else storedfprlen = 20; if (storedfprlen == fprlen && !memcmp (buffer + off, fpr, storedfprlen)) return idx+1; /* found */ } return 0; /* not found */ } /* Helper for has_short_kid and has_long_kid. */ static int blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr, int fproff, int fprlen) { const unsigned char *buffer; size_t length; size_t pos, off; size_t nkeys, keyinfolen; int idx, fpr32, storedfprlen; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ fpr32 = buffer[5] == 2; /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < (fpr32?56:28)) return 0; /* invalid blob */ pos = 20; if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length) return 0; /* out of bounds */ if (fpr32) fproff = 0; /* keyid are the high-order bits. */ for (idx=0; idx < nkeys; idx++) { off = pos + idx*keyinfolen; if (fpr32) storedfprlen = (get16 (buffer + off + 32) & 0x80)? 32:20; else storedfprlen = 20; if (storedfprlen == fproff + fprlen && !memcmp (buffer + off + fproff, fpr, fprlen)) return idx+1; /* found */ } return 0; /* not found */ } static int blob_cmp_name (KEYBOXBLOB blob, int idx, const char *name, size_t namelen, int substr, int x509) { const unsigned char *buffer; size_t length; size_t pos, off, len; size_t nkeys, keyinfolen; size_t nuids, uidinfolen; size_t nserial; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20 + keyinfolen*nkeys; if ((uint64_t)pos+2 > (uint64_t)length) return 0; /* out of bounds */ /*serial*/ nserial = get16 (buffer+pos); pos += 2 + nserial; if (pos+4 > length) return 0; /* out of bounds */ /* user ids*/ nuids = get16 (buffer + pos); pos += 2; uidinfolen = get16 (buffer + pos); pos += 2; if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */) return 0; /* invalid blob */ if (pos + uidinfolen*nuids > length) return 0; /* out of bounds */ if (idx < 0) { /* 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; mypos += idx*uidinfolen; off = get32 (buffer+mypos); len = get32 (buffer+mypos+4); if ((uint64_t)off+(uint64_t)len > (uint64_t)length) return 0; /* error: better stop here out of bounds */ if (len < 1) continue; /* empty name */ if (substr) { if (ascii_memcasemem (buffer+off, len, name, namelen)) return idx+1; /* found */ } else { if (len == namelen && !memcmp (buffer+off, name, len)) return idx+1; /* found */ } } } else { if (idx > nuids) return 0; /* no user ID with that idx */ pos += idx*uidinfolen; off = get32 (buffer+pos); len = get32 (buffer+pos+4); if (off+len > length) return 0; /* out of bounds */ if (len < 1) return 0; /* empty name */ if (substr) { if (ascii_memcasemem (buffer+off, len, name, namelen)) return idx+1; /* found */ } else { 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. 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) { const unsigned char *buffer; size_t length; size_t pos, off, len; size_t nkeys, keyinfolen; size_t nuids, uidinfolen; size_t nserial; int idx; /* fixme: this code is common to blob_cmp_mail */ buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20 + keyinfolen*nkeys; if (pos+2 > length) return 0; /* out of bounds */ /*serial*/ nserial = get16 (buffer+pos); pos += 2 + nserial; if (pos+4 > length) return 0; /* out of bounds */ /* user ids*/ nuids = get16 (buffer + pos); pos += 2; uidinfolen = get16 (buffer + pos); pos += 2; if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */) return 0; /* invalid blob */ if (pos + uidinfolen*nuids > length) return 0; /* out of bounds */ if (namelen < 1) return 0; /* 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 ((uint64_t)off+(uint64_t)len > (uint64_t)length) 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, len, name, namelen)) return idx+1; /* found */ } else { if (len == namelen && !ascii_memcasecmp (buffer+off, name, len)) return idx+1; /* found */ } } return 0; /* not found */ } /* Return true if the key in BLOB matches the 20 bytes keygrip GRIP. * 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_openpgp_has_grip (KEYBOXBLOB blob, const unsigned char *grip) { int rc = 0; const unsigned char *buffer; size_t length; size_t cert_off, cert_len; struct _keybox_openpgp_info info; struct _keybox_openpgp_key_info *k; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* Too short. */ cert_off = get32 (buffer+8); cert_len = get32 (buffer+12); if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length) return 0; /* Too short. */ if (_keybox_parse_openpgp (buffer + cert_off, cert_len, NULL, &info)) return 0; /* Parse error. */ if (!memcmp (info.primary.grip, grip, 20)) { rc = 1; goto leave; } if (info.nsubkeys) { k = &info.subkeys; do { if (!memcmp (k->grip, grip, 20)) { rc = 1; goto leave; } k = k->next; } while (k); } leave: _keybox_destroy_openpgp_info (&info); return rc; } #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 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) { int rc; const unsigned char *buffer; size_t length; size_t cert_off, cert_len; ksba_reader_t reader = NULL; ksba_cert_t cert = NULL; ksba_sexp_t p = NULL; gcry_sexp_t s_pkey; unsigned char array[20]; unsigned char *rcp; size_t n; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* Too short. */ cert_off = get32 (buffer+8); cert_len = get32 (buffer+12); if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length) return 0; /* Too short. */ rc = ksba_reader_new (&reader); if (rc) return 0; /* Problem with ksba. */ rc = ksba_reader_set_mem (reader, buffer+cert_off, cert_len); if (rc) goto failed; rc = ksba_cert_new (&cert); if (rc) goto failed; rc = ksba_cert_read_der (cert, reader); if (rc) goto failed; p = ksba_cert_get_public_key (cert); if (!p) goto failed; n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) goto failed; rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n); if (rc) { gcry_sexp_release (s_pkey); goto failed; } rcp = gcry_pk_get_keygrip (s_pkey, array); gcry_sexp_release (s_pkey); if (!rcp) goto failed; /* Can't calculate keygrip. */ xfree (p); ksba_cert_release (cert); ksba_reader_release (reader); return !memcmp (array, grip, 20); failed: xfree (p); ksba_cert_release (cert); ksba_reader_release (reader); return 0; } #endif /*KEYBOX_WITH_X509*/ /* The has_foo functions are used as helpers for search */ static inline int has_short_kid (KEYBOXBLOB blob, u32 lkid) { 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, u32 mkid, u32 lkid) { 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 has_fingerprint (KEYBOXBLOB blob, const unsigned char *fpr, unsigned int fprlen) { return blob_cmp_fpr (blob, fpr, fprlen); } static inline int has_keygrip (KEYBOXBLOB blob, const unsigned char *grip) { if (blob_get_type (blob) == KEYBOX_BLOBTYPE_PGP) return blob_openpgp_has_grip (blob, grip); #ifdef KEYBOX_WITH_X509 if (blob_get_type (blob) == KEYBOX_BLOBTYPE_X509) return blob_x509_has_grip (blob, grip); #endif return 0; } static inline int has_issuer (KEYBOXBLOB blob, const char *name) { size_t namelen; return_val_if_fail (name, 0); if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509) return 0; namelen = strlen (name); return blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0, 1); } static inline int has_issuer_sn (KEYBOXBLOB blob, const char *name, const unsigned char *sn, int snlen) { size_t namelen; return_val_if_fail (name, 0); return_val_if_fail (sn, 0); 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, 1)); } static inline int has_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen) { return_val_if_fail (sn, 0); if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509) return 0; return blob_cmp_sn (blob, sn, snlen); } static inline int has_subject (KEYBOXBLOB blob, const char *name) { size_t namelen; return_val_if_fail (name, 0); if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509) return 0; namelen = strlen (name); return blob_cmp_name (blob, 1 /* subject */, name, namelen, 0, 1); } static inline int has_username (KEYBOXBLOB blob, const char *name, int substr) { size_t namelen; int btype; return_val_if_fail (name, 0); 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/user names */, name, namelen, substr, (btype == KEYBOX_BLOBTYPE_X509)); } static inline int has_mail (KEYBOXBLOB blob, const char *name, int substr) { size_t namelen; int btype; return_val_if_fail (name, 0); 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, (btype == KEYBOX_BLOBTYPE_X509)); } static void release_sn_array (struct sn_array_s *array, size_t size) { size_t n; for (n=0; n < size; n++) xfree (array[n].sn); 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; } /* The search API */ gpg_error_t keybox_search_reset (KEYBOX_HANDLE hd) { if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (hd->found.blob) { _keybox_release_blob (hd->found.blob); hd->found.blob = NULL; } if (hd->fp) { 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; return 0; } /* 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). */ 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) { gpg_error_t rc; size_t n; int need_words, any_skip; KEYBOXBLOB blob = NULL; struct sn_array_s *sn_array = NULL; int pk_no, uid_no; off_t lastfoundoff; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); /* Clear last found result but reord the offset of the last found * blob which we may need later. */ if (hd->found.blob) { lastfoundoff = _keybox_get_blob_fileoffset (hd->found.blob); _keybox_release_blob (hd->found.blob); hd->found.blob = NULL; } else lastfoundoff = 0; if (hd->error) return hd->error; /* still in error state */ if (hd->eof) return -1; /* still EOF */ /* figure out what information we need */ need_words = any_skip = 0; for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_WORDS: need_words = 1; break; case KEYDB_SEARCH_MODE_FIRST: /* always restart the search in this mode */ keybox_search_reset (hd); lastfoundoff = 0; break; default: break; } 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_from_syserror ()); } } (void)need_words; /* Not yet implemented. */ if (!hd->fp) { rc = open_file (hd); if (rc) { xfree (sn_array); return rc; } /* log_debug ("%s: re-opened file\n", __func__); */ - if (ndesc && desc[0].mode == KEYDB_SEARCH_MODE_NEXT && lastfoundoff) + if (ndesc && desc[0].mode != KEYDB_SEARCH_MODE_FIRST && lastfoundoff) { - /* Search mode is next and the last search operation + /* Search mode is not first and the last search operation * returned a blob which also was not the first one. We now * need to skip over that blob and hope that the file has * not changed. */ if (fseeko (hd->fp, lastfoundoff, SEEK_SET)) { rc = gpg_error_from_syserror (); log_debug ("%s: seeking to last found offset failed: %s\n", __func__, gpg_strerror (rc)); xfree (sn_array); return gpg_error (GPG_ERR_NOTHING_FOUND); } /* log_debug ("%s: re-opened file and sought to last offset\n", */ /* __func__); */ rc = _keybox_read_blob (NULL, hd->fp, NULL); if (rc) { log_debug ("%s: skipping last found blob failed: %s\n", __func__, gpg_strerror (rc)); xfree (sn_array); return gpg_error (GPG_ERR_NOTHING_FOUND); } } } /* 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++) { if (!desc[n].sn) ; else if (desc[n].snlen == -1) { unsigned char *sn; s = desc[n].sn; for (i=0; *s && *s != '/'; s++, i++) ; odd = (i & 1); snlen = (i+1)/2; sn_array[n].sn = xtrymalloc (snlen); if (!sn_array[n].sn) { hd->error = gpg_error_from_syserror (); release_sn_array (sn_array, n); return hd->error; } sn_array[n].snlen = snlen; sn = sn_array[n].sn; s = desc[n].sn; if (odd) { *sn++ = xtoi_1 (s); s++; } for (; *s && *s != '/'; s += 2) *sn++ = xtoi_2 (s); } else { const unsigned char *sn; sn = desc[n].sn; snlen = desc[n].snlen; sn_array[n].sn = xtrymalloc (snlen); if (!sn_array[n].sn) { hd->error = gpg_error_from_syserror (); release_sn_array (sn_array, n); return hd->error; } sn_array[n].snlen = snlen; memcpy (sn_array[n].sn, sn, snlen); } } } pk_no = uid_no = 0; for (;;) { unsigned int blobflags; int blobtype; _keybox_release_blob (blob); blob = NULL; 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) { ++*r_skipped; continue; /* Skip too large records. */ } if (rc) break; 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++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_NONE: never_reached (); break; 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: uid_no = has_mail (blob, desc[n].u.name, 0); if (uid_no) goto found; break; case KEYDB_SEARCH_MODE_MAILSUB: uid_no = has_mail (blob, desc[n].u.name, 1); if (uid_no) goto found; break; case KEYDB_SEARCH_MODE_SUBSTR: 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: /* not yet implemented */ break; case KEYDB_SEARCH_MODE_ISSUER: if (has_issuer (blob, desc[n].u.name)) goto found; break; case KEYDB_SEARCH_MODE_ISSUER_SN: if (has_issuer_sn (blob, desc[n].u.name, sn_array? sn_array[n].sn : desc[n].sn, sn_array? sn_array[n].snlen : desc[n].snlen)) goto found; break; case KEYDB_SEARCH_MODE_SN: if (has_sn (blob, sn_array? sn_array[n].sn : desc[n].sn, sn_array? sn_array[n].snlen : desc[n].snlen)) goto found; break; case KEYDB_SEARCH_MODE_SUBJECT: if (has_subject (blob, desc[n].u.name)) goto found; break; 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: 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: pk_no = has_fingerprint (blob, desc[n].u.fpr, desc[n].fprlen); 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: goto found; break; case KEYDB_SEARCH_MODE_NEXT: goto found; break; default: rc = gpg_error (GPG_ERR_INV_VALUE); goto found; } } continue; 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++) { 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 || gpg_err_code (rc) == GPG_ERR_EOF) { _keybox_release_blob (blob); hd->eof = 1; } else { _keybox_release_blob (blob); hd->error = rc; } if (sn_array) release_sn_array (sn_array, ndesc); return rc; } /* 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. R_UID_NO and R_PK_NO are used to return 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) { gpg_error_t err; const unsigned char *buffer; size_t length; size_t image_off, image_len; size_t siginfo_off, siginfo_len; *r_iobuf = 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 ((uint64_t)image_off+(uint64_t)image_len > (uint64_t)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; *r_pk_no = hd->found.pk_no; *r_uid_no = hd->found.uid_no; *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. */ int keybox_get_cert (KEYBOX_HANDLE hd, ksba_cert_t *r_cert) { const unsigned char *buffer; size_t length; size_t cert_off, cert_len; ksba_reader_t reader = NULL; ksba_cert_t cert = NULL; int rc; 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_X509) 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); cert_off = get32 (buffer+8); cert_len = get32 (buffer+12); if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length) return gpg_error (GPG_ERR_TOO_SHORT); rc = ksba_reader_new (&reader); if (rc) return rc; rc = ksba_reader_set_mem (reader, buffer+cert_off, cert_len); if (rc) { ksba_reader_release (reader); /* fixme: need to map the error codes */ return gpg_error (GPG_ERR_GENERAL); } rc = ksba_cert_new (&cert); if (rc) { ksba_reader_release (reader); return rc; } rc = ksba_cert_read_der (cert, reader); if (rc) { ksba_cert_release (cert); ksba_reader_release (reader); /* fixme: need to map the error codes */ return gpg_error (GPG_ERR_GENERAL); } *r_cert = cert; ksba_reader_release (reader); return 0; } #endif /*KEYBOX_WITH_X509*/ /* Return the flags named WHAT at the address of VALUE. IDX is used only for certain flags and should be 0 if not required. */ int keybox_get_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int *value) { const unsigned char *buffer; 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) return gpg_error (GPG_ERR_NOTHING_FOUND); buffer = _keybox_get_blob_image (hd->found.blob, &length); 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; }