Updated from latest NewPG project
[gnupg.git] / kbx / keybox-search.c
1 /* keybox-search.c - Search operations
2  *      Copyright (C) 2001, 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #include "../jnlib/stringhelp.h" /* ascii_xxxx() */
28 #include "keybox-defs.h"
29
30 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
31                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
32 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
33
34 struct sn_array_s {
35     int snlen;
36     unsigned char *sn;
37 };
38
39
40
41 static ulong
42 get32 (const byte *buffer)
43 {
44   ulong a;
45   a =  *buffer << 24;
46   a |= buffer[1] << 16;
47   a |= buffer[2] << 8;
48   a |= buffer[3];
49   return a;
50 }
51
52 static ulong
53 get16 (const byte *buffer)
54 {
55   ulong a;
56   a =  *buffer << 8;
57   a |= buffer[1];
58   return a;
59 }
60
61
62
63 static int
64 blob_get_type (KEYBOXBLOB blob)
65 {
66   const unsigned char *buffer;
67   size_t length;
68
69   buffer = _keybox_get_blob_image (blob, &length);
70   if (length < 40)
71     return -1; /* blob too short */
72
73   return buffer[4];
74 }
75
76 static unsigned int
77 blob_get_blob_flags (KEYBOXBLOB blob)
78 {
79   const unsigned char *buffer;
80   size_t length;
81
82   buffer = _keybox_get_blob_image (blob, &length);
83   if (length < 8)
84     return 0; /* oops */
85
86   return get16 (buffer + 6);
87 }
88
89
90 static int
91 blob_cmp_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen)
92 {
93   const unsigned char *buffer;
94   size_t length;
95   size_t pos, off;
96   size_t nkeys, keyinfolen;
97   size_t nserial;
98
99   buffer = _keybox_get_blob_image (blob, &length);
100   if (length < 40)
101     return 0; /* blob too short */
102
103   /*keys*/
104   nkeys = get16 (buffer + 16);
105   keyinfolen = get16 (buffer + 18 );
106   if (keyinfolen < 28)
107     return 0; /* invalid blob */
108   pos = 20 + keyinfolen*nkeys;
109   if (pos+2 > length)
110     return 0; /* out of bounds */
111
112   /*serial*/
113   nserial = get16 (buffer+pos); 
114   off = pos + 2;
115   if (off+nserial > length)
116     return 0; /* out of bounds */
117
118   return nserial == snlen && !memcmp (buffer+off, sn, snlen);
119 }
120
121
122 static int
123 blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr)
124 {
125   const unsigned char *buffer;
126   size_t length;
127   size_t pos, off;
128   size_t nkeys, keyinfolen;
129   int idx;
130
131   buffer = _keybox_get_blob_image (blob, &length);
132   if (length < 40)
133     return 0; /* blob too short */
134
135   /*keys*/
136   nkeys = get16 (buffer + 16);
137   keyinfolen = get16 (buffer + 18 );
138   if (keyinfolen < 28)
139     return 0; /* invalid blob */
140   pos = 20;
141   if (pos + keyinfolen*nkeys > length)
142     return 0; /* out of bounds */
143
144   for (idx=0; idx < nkeys; idx++)
145     {
146       off = pos + idx*keyinfolen;
147       if (!memcmp (buffer + off, fpr, 20))
148         return 1; /* found */
149     }
150   return 0; /* not found */
151 }
152
153 static int
154 blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr,
155                    int fproff, int fprlen)
156 {
157   const unsigned char *buffer;
158   size_t length;
159   size_t pos, off;
160   size_t nkeys, keyinfolen;
161   int idx;
162
163   buffer = _keybox_get_blob_image (blob, &length);
164   if (length < 40)
165     return 0; /* blob too short */
166
167   /*keys*/
168   nkeys = get16 (buffer + 16);
169   keyinfolen = get16 (buffer + 18 );
170   if (keyinfolen < 28)
171     return 0; /* invalid blob */
172   pos = 20;
173   if (pos + keyinfolen*nkeys > length)
174     return 0; /* out of bounds */
175
176   for (idx=0; idx < nkeys; idx++)
177     {
178       off = pos + idx*keyinfolen;
179       if (!memcmp (buffer + off + fproff, fpr, fprlen))
180         return 1; /* found */
181     }
182   return 0; /* not found */
183 }
184
185
186 static int
187 blob_cmp_name (KEYBOXBLOB blob, int idx,
188                const char *name, size_t namelen, int substr)
189 {
190   const unsigned char *buffer;
191   size_t length;
192   size_t pos, off, len;
193   size_t nkeys, keyinfolen;
194   size_t nuids, uidinfolen;
195   size_t nserial;
196
197   buffer = _keybox_get_blob_image (blob, &length);
198   if (length < 40)
199     return 0; /* blob too short */
200
201   /*keys*/
202   nkeys = get16 (buffer + 16);
203   keyinfolen = get16 (buffer + 18 );
204   if (keyinfolen < 28)
205     return 0; /* invalid blob */
206   pos = 20 + keyinfolen*nkeys;
207   if (pos+2 > length)
208     return 0; /* out of bounds */
209
210   /*serial*/
211   nserial = get16 (buffer+pos); 
212   pos += 2 + nserial;
213   if (pos+4 > length)
214     return 0; /* out of bounds */
215
216   /* user ids*/
217   nuids = get16 (buffer + pos);  pos += 2;
218   uidinfolen = get16 (buffer + pos);  pos += 2;
219   if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */)
220     return 0; /* invalid blob */
221   if (pos + uidinfolen*nuids > length)
222     return 0; /* out of bounds */
223
224   if (idx < 0)
225     { /* compare all names starting with that (negated) index */
226       idx = -idx;
227       
228       for ( ;idx < nuids; idx++)
229         {
230           size_t mypos = pos;
231
232           mypos += idx*uidinfolen;
233           off = get32 (buffer+mypos);
234           len = get32 (buffer+mypos+4);
235           if (off+len > length)
236             return 0; /* error: better stop here out of bounds */
237           if (len < 1)
238             continue; /* empty name */
239           if (substr)
240             {
241               if (ascii_memcasemem (buffer+off, len, name, namelen))
242                 return 1; /* found */
243             }
244           else
245             {
246               if (len == namelen && !memcmp (buffer+off, name, len))
247                 return 1; /* found */
248             }
249         }
250       return 0; /* not found */
251     }
252   else
253     {
254       if (idx > nuids)
255         return 0; /* no user ID with that idx */
256       pos += idx*uidinfolen;
257       off = get32 (buffer+pos);
258       len = get32 (buffer+pos+4);
259       if (off+len > length)
260         return 0; /* out of bounds */
261       if (len < 1)
262         return 0; /* empty name */
263
264       if (substr)
265         {
266           return !!ascii_memcasemem (buffer+off, len, name, namelen);
267         }
268       else
269         {
270           return len == namelen && !memcmp (buffer+off, name, len);
271         }
272     }
273 }
274
275
276 /* compare all email addresses of the subject.  With SUBSTR given as
277    True a substring search is done in the mail address */
278 static int
279 blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
280 {
281   const unsigned char *buffer;
282   size_t length;
283   size_t pos, off, len;
284   size_t nkeys, keyinfolen;
285   size_t nuids, uidinfolen;
286   size_t nserial;
287   int idx;
288
289   /* fixme: this code is common to blob_cmp_mail */
290   buffer = _keybox_get_blob_image (blob, &length);
291   if (length < 40)
292     return 0; /* blob too short */
293
294   /*keys*/
295   nkeys = get16 (buffer + 16);
296   keyinfolen = get16 (buffer + 18 );
297   if (keyinfolen < 28)
298     return 0; /* invalid blob */
299   pos = 20 + keyinfolen*nkeys;
300   if (pos+2 > length)
301     return 0; /* out of bounds */
302
303   /*serial*/
304   nserial = get16 (buffer+pos); 
305   pos += 2 + nserial;
306   if (pos+4 > length)
307     return 0; /* out of bounds */
308
309   /* user ids*/
310   nuids = get16 (buffer + pos);  pos += 2;
311   uidinfolen = get16 (buffer + pos);  pos += 2;
312   if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */)
313     return 0; /* invalid blob */
314   if (pos + uidinfolen*nuids > length)
315     return 0; /* out of bounds */
316
317   if (namelen < 1)
318     return 0;
319
320   for (idx=1 ;idx < nuids; idx++)
321     {
322       size_t mypos = pos;
323       
324       mypos += idx*uidinfolen;
325       off = get32 (buffer+mypos);
326       len = get32 (buffer+mypos+4);
327       if (off+len > length)
328         return 0; /* error: better stop here out of bounds */
329       if (len < 2 || buffer[off] != '<')
330         continue; /* empty name or trailing 0 not stored */
331       len--; /* one back */
332       if ( len < 3 || buffer[off+len] != '>')
333         continue; /* not a proper email address */
334       len--; 
335       if (substr)
336         {
337           if (ascii_memcasemem (buffer+off+1, len, name, namelen))
338             return 1; /* found */
339         }
340       else
341         {
342           if (len == namelen && !ascii_memcasecmp (buffer+off+1, name, len))
343             return 1; /* found */
344         }
345     }
346   return 0; /* not found */
347 }
348
349
350
351 \f
352 /*
353   The has_foo functions are used as helpers for search 
354 */
355 static int
356 has_short_kid (KEYBOXBLOB blob, const unsigned char *kid)
357 {
358   return blob_cmp_fpr_part (blob, kid+4, 16, 4);
359 }
360
361 static int
362 has_long_kid (KEYBOXBLOB blob, const unsigned char *kid)
363 {
364   return blob_cmp_fpr_part (blob, kid, 12, 8);
365 }
366
367 static int
368 has_fingerprint (KEYBOXBLOB blob, const unsigned char *fpr)
369 {
370   return blob_cmp_fpr (blob, fpr);
371 }
372
373
374 static int
375 has_issuer (KEYBOXBLOB blob, const char *name)
376 {
377   size_t namelen;
378
379   return_val_if_fail (name, 0);
380
381   if (blob_get_type (blob) != BLOBTYPE_X509)
382     return 0;
383
384   namelen = strlen (name);
385   return blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0);
386 }
387
388 static int
389 has_issuer_sn (KEYBOXBLOB blob, const char *name,
390                const unsigned char *sn, int snlen)
391 {
392   size_t namelen;
393
394   return_val_if_fail (name, 0);
395   return_val_if_fail (sn, 0);
396
397   if (blob_get_type (blob) != BLOBTYPE_X509)
398     return 0;
399
400   namelen = strlen (name);
401   
402   return (blob_cmp_sn (blob, sn, snlen)
403           && blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0));
404 }
405
406 static int
407 has_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen)
408 {
409   return_val_if_fail (sn, 0);
410
411   if (blob_get_type (blob) != BLOBTYPE_X509)
412     return 0;
413   return blob_cmp_sn (blob, sn, snlen);
414 }
415
416 static int
417 has_subject (KEYBOXBLOB blob, const char *name)
418 {
419   size_t namelen;
420
421   return_val_if_fail (name, 0);
422
423   if (blob_get_type (blob) != BLOBTYPE_X509)
424     return 0;
425
426   namelen = strlen (name);
427   return blob_cmp_name (blob, 1 /* subject */, name, namelen, 0);
428 }
429
430 static int
431 has_subject_or_alt (KEYBOXBLOB blob, const char *name, int substr)
432 {
433   size_t namelen;
434
435   return_val_if_fail (name, 0);
436
437   if (blob_get_type (blob) != BLOBTYPE_X509)
438     return 0;
439
440   namelen = strlen (name);
441   return blob_cmp_name (blob, -1 /* all subject names*/, name,
442                         namelen, substr);
443 }
444
445
446 static int
447 has_mail (KEYBOXBLOB blob, const char *name, int substr)
448 {
449   size_t namelen;
450
451   return_val_if_fail (name, 0);
452
453   if (blob_get_type (blob) != BLOBTYPE_X509)
454     return 0;
455
456   namelen = strlen (name);
457   if (namelen && name[namelen-1] == '>')
458     namelen--;
459   return blob_cmp_mail (blob, name, namelen, substr);
460 }
461
462
463 static void
464 release_sn_array (struct sn_array_s *array, size_t size)
465 {
466   size_t n;
467
468   for (n=0; n < size; n++)
469     xfree (array[n].sn);
470   xfree (array);
471 }
472
473 \f
474 /*
475
476   The search API
477
478 */
479
480 int 
481 keybox_search_reset (KEYBOX_HANDLE hd)
482 {
483   if (!hd)
484     return KEYBOX_Invalid_Value;
485
486   if (hd->found.blob)
487     {
488       _keybox_release_blob (hd->found.blob);
489       hd->found.blob = NULL;
490     }
491
492   if (hd->fp)
493     {
494       fclose (hd->fp);
495       hd->fp = NULL;
496     }
497   hd->error = 0;
498   hd->eof = 0;
499   return 0;   
500 }
501
502
503 /* Note: When in ephemeral mode the search function does visit all
504    blobs but in standard mode, blobs flagged as ephemeral are ignored.  */
505 int 
506 keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
507 {
508   int rc;
509   size_t n;
510   int need_words, any_skip;
511   KEYBOXBLOB blob = NULL;
512   struct sn_array_s *sn_array = NULL;
513
514   if (!hd)
515     return KEYBOX_Invalid_Value;
516
517   /* clear last found result */
518   if (hd->found.blob)
519     {
520       _keybox_release_blob (hd->found.blob);
521       hd->found.blob = NULL;
522     }
523
524   if (hd->error)  
525     return hd->error; /* still in error state */
526   if (hd->eof)  
527     return -1; /* still EOF */
528
529   /* figure out what information we need */
530   need_words = any_skip = 0;
531   for (n=0; n < ndesc; n++) 
532     {
533       switch (desc[n].mode) 
534         {
535         case KEYDB_SEARCH_MODE_WORDS: 
536           need_words = 1;
537           break;
538         case KEYDB_SEARCH_MODE_FIRST:
539           /* always restart the search in this mode */
540           keybox_search_reset (hd);
541           break;
542         default:
543           break;
544         }
545       if (desc[n].skipfnc) 
546         any_skip = 1;
547       if (desc[n].snlen == -1 && !sn_array)
548         {
549           sn_array = xtrycalloc (ndesc, sizeof *sn_array);
550           if (!sn_array)
551             return (hd->error = KEYBOX_Out_Of_Core);
552         }
553     }
554
555   if (!hd->fp)
556     {
557       hd->fp = fopen (hd->kb->fname, "rb");
558       if (!hd->fp)
559         {
560           xfree (sn_array);
561           return (hd->error = KEYBOX_File_Open_Error);
562         }
563     }
564
565   /* kludge: we need to convert an SN given as hexstring to it's
566      binary representation - in some cases we are not able to store it
567      in the search descriptor, because due to its usgae it is not
568      possible to free allocated memory */
569   if (sn_array)
570     {
571       const unsigned char *s;
572       int i, odd;
573       size_t snlen;
574
575       for (n=0; n < ndesc; n++) 
576         {
577           if (!desc[n].sn)
578             ;
579           else if (desc[n].snlen == -1)
580             {
581               unsigned char *sn;
582
583               s = desc[n].sn;
584               for (i=0; *s && *s != '/'; s++, i++)
585                 ;
586               odd = (i & 1);
587               snlen = (i+1)/2;
588               sn_array[n].sn = xtrymalloc (snlen);
589               if (!sn_array[n].sn)
590                 {
591                   release_sn_array (sn_array, n);
592                   return (hd->error = KEYBOX_Out_Of_Core);
593                 }
594               sn_array[n].snlen = snlen;
595               sn = sn_array[n].sn;
596               s = desc[n].sn;
597               if (odd)
598                 {
599                   *sn++ = xtoi_1 (s);
600                   s++;
601                 }
602               for (; *s && *s != '/';  s += 2)
603                 *sn++ = xtoi_2 (s);
604             }
605           else
606             {
607               const unsigned char *sn;
608
609               sn = desc[n].sn;
610               snlen = desc[n].snlen;
611               sn_array[n].sn = xtrymalloc (snlen);
612               if (!sn_array[n].sn)
613                 {
614                   release_sn_array (sn_array, n);
615                   return (hd->error = KEYBOX_Out_Of_Core);
616                 }
617               sn_array[n].snlen = snlen;
618               memcpy (sn_array[n].sn, sn, snlen);
619             }
620         }
621     }
622
623
624   for (;;)
625     {
626       unsigned int blobflags;
627
628       _keybox_release_blob (blob); blob = NULL;
629       rc = _keybox_read_blob (&blob, hd->fp);
630       if (rc)
631         break;
632
633       blobflags = blob_get_blob_flags (blob);
634       if (!hd->ephemeral && (blobflags & 2))
635         continue; /* not in ephemeral mode but blob is flagged ephemeral */
636
637       for (n=0; n < ndesc; n++) 
638         {
639           switch (desc[n].mode)
640             {
641             case KEYDB_SEARCH_MODE_NONE: 
642               never_reached ();
643               break;
644             case KEYDB_SEARCH_MODE_EXACT: 
645               if (has_subject_or_alt (blob, desc[n].u.name, 0))
646                 goto found;
647               break;
648             case KEYDB_SEARCH_MODE_MAIL:
649               if (has_mail (blob, desc[n].u.name, 0))
650                 goto found;
651               break;
652             case KEYDB_SEARCH_MODE_MAILSUB:
653               if (has_mail (blob, desc[n].u.name, 1))
654                 goto found;
655               break;
656             case KEYDB_SEARCH_MODE_SUBSTR:
657               if (has_subject_or_alt (blob, desc[n].u.name, 1))
658                 goto found;
659               break;
660             case KEYDB_SEARCH_MODE_MAILEND:
661             case KEYDB_SEARCH_MODE_WORDS: 
662               never_reached (); /* not yet implemented */
663               break;
664             case KEYDB_SEARCH_MODE_ISSUER:
665               if (has_issuer (blob, desc[n].u.name))
666                 goto found;
667               break;
668             case KEYDB_SEARCH_MODE_ISSUER_SN:
669               if (has_issuer_sn (blob, desc[n].u.name,
670                                  sn_array? sn_array[n].sn : desc[n].sn,
671                                  sn_array? sn_array[n].snlen : desc[n].snlen))
672                 goto found;
673               break;
674             case KEYDB_SEARCH_MODE_SN:
675               if (has_sn (blob, sn_array? sn_array[n].sn : desc[n].sn,
676                                 sn_array? sn_array[n].snlen : desc[n].snlen))
677                 goto found;
678               break;
679             case KEYDB_SEARCH_MODE_SUBJECT:
680               if (has_subject (blob, desc[n].u.name))
681                 goto found;
682               break;
683             case KEYDB_SEARCH_MODE_SHORT_KID: 
684               if (has_short_kid (blob, desc[n].u.kid))
685                 goto found;
686               break;
687             case KEYDB_SEARCH_MODE_LONG_KID:
688               if (has_long_kid (blob, desc[n].u.kid))
689                 goto found;
690               break;
691             case KEYDB_SEARCH_MODE_FPR:
692             case KEYDB_SEARCH_MODE_FPR20:
693               if (has_fingerprint (blob, desc[n].u.fpr))
694                 goto found;
695               break;
696             case KEYDB_SEARCH_MODE_FIRST: 
697               goto found;
698               break;
699             case KEYDB_SEARCH_MODE_NEXT: 
700               goto found;
701               break;
702             default: 
703               rc = KEYBOX_Invalid_Value;
704               goto found;
705             }
706         }
707       continue;
708     found:  
709       for (n=any_skip?0:ndesc; n < ndesc; n++) 
710         {
711 /*            if (desc[n].skipfnc */
712 /*                && desc[n].skipfnc (desc[n].skipfncvalue, aki)) */
713 /*              break; */
714         }
715       if (n == ndesc)
716         break; /* got it */
717     }
718   
719   if (!rc)
720     {
721       hd->found.blob = blob;
722     }
723   else if (rc == -1)
724     {
725       _keybox_release_blob (blob);
726       hd->eof = 1;
727     }
728   else 
729     {
730       _keybox_release_blob (blob);
731       hd->error = rc;
732     }
733
734   if (sn_array)
735     release_sn_array (sn_array, ndesc);
736
737   return rc;
738 }
739
740
741
742 \f
743 /*
744    Functions to return a certificate or a keyblock.  To be used after
745    a successful search operation.
746 */
747 #ifdef KEYBOX_WITH_X509
748 /*
749   Return the last found cert.  Caller must free it.
750  */
751 int
752 keybox_get_cert (KEYBOX_HANDLE hd, KsbaCert *r_cert)
753 {
754   const unsigned char *buffer;
755   size_t length;
756   size_t cert_off, cert_len;
757   KsbaReader reader = NULL;
758   KsbaCert cert = NULL;
759   int rc;
760
761   if (!hd)
762     return KEYBOX_Invalid_Value;
763   if (!hd->found.blob)
764     return KEYBOX_Nothing_Found;
765
766   if (blob_get_type (hd->found.blob) != BLOBTYPE_X509)
767     return KEYBOX_Wrong_Blob_Type;
768
769   buffer = _keybox_get_blob_image (hd->found.blob, &length);
770   if (length < 40)
771     return KEYBOX_Blob_Too_Short;
772   cert_off = get32 (buffer+8);
773   cert_len = get32 (buffer+12);
774   if (cert_off+cert_len > length)
775     return KEYBOX_Blob_Too_Short;
776
777   reader = ksba_reader_new ();
778   if (!reader)
779     return KEYBOX_Out_Of_Core;
780   rc = ksba_reader_set_mem (reader, buffer+cert_off, cert_len);
781   if (rc)
782     {
783       ksba_reader_release (reader);
784       /* fixme: need to map the error codes */
785       return KEYBOX_General_Error;
786     }
787
788   cert = ksba_cert_new ();
789   if (!cert)
790     {
791       ksba_reader_release (reader);
792       return KEYBOX_Out_Of_Core;
793     }
794
795   rc = ksba_cert_read_der (cert, reader);
796   if (rc)
797     {
798       ksba_cert_release (cert);
799       ksba_reader_release (reader);
800       /* fixme: need to map the error codes */
801       return KEYBOX_General_Error;
802     }
803
804   *r_cert = cert;
805   ksba_reader_release (reader);
806   return 0;
807 }
808
809 #endif /*KEYBOX_WITH_X509*/