We have reached a state where we are able to import certs and
[gnupg.git] / sm / keydb.c
1 /* keydb.c - key database dispatcher
2  * Copyright (C) 2001 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 <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30
31 #include "gpgsm.h"
32 #include "../kbx/keybox.h"
33 #include "keydb.h" 
34 #include "i18n.h"
35
36 #define DIRSEP_C '/'
37
38 static int active_handles;
39
40 typedef enum {
41     KEYDB_RESOURCE_TYPE_NONE = 0,
42     KEYDB_RESOURCE_TYPE_KEYBOX
43 } KeydbResourceType;
44 #define MAX_KEYDB_RESOURCES 20
45
46 struct resource_item {
47   KeydbResourceType type;
48   union {
49     KEYBOX_HANDLE kr;
50   } u;
51   void *token;
52   int secret;
53 };
54
55 static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
56 static int used_resources;
57
58 struct keydb_handle {
59   int locked;
60   int found;
61   int current;
62   int used; /* items in active */
63   struct resource_item active[MAX_KEYDB_RESOURCES];
64 };
65
66
67 static int lock_all (KEYDB_HANDLE hd);
68 static void unlock_all (KEYDB_HANDLE hd);
69
70
71 /*
72  * Register a resource (which currently may only be a keybox file).
73  * The first keybox which is added by this function is
74  * created if it does not exist.
75  * Note: this function may be called before secure memory is
76  * available.
77  */
78 int
79 keydb_add_resource (const char *url, int force, int secret)
80 {
81     static int any_secret, any_public;
82     const char *resname = url;
83     char *filename = NULL;
84     int rc = 0; 
85     KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
86 /*      const char *created_fname = NULL; */
87
88     /* Do we have an URL?
89      *  gnupg-ring:filename  := this is a plain keybox
90      *  filename := See what is is, but create as plain keybox.
91      */
92     if (strlen (resname) > 11) {
93         if (!strncmp( resname, "gnupg-kbx:", 10) ) {
94             rt = KEYDB_RESOURCE_TYPE_KEYBOX;
95             resname += 11;
96         }
97       #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
98         else if (strchr (resname, ':')) {
99             log_error ("invalid key resource URL `%s'\n", url );
100             rc = GPGSM_General_Error;
101             goto leave;
102         }
103       #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
104     }
105
106     if (*resname != DIRSEP_C ) { /* do tilde expansion etc */
107         if (strchr(resname, DIRSEP_C) )
108             filename = make_filename (resname, NULL);
109         else
110             filename = make_filename (opt.homedir, resname, NULL);
111     }
112     else
113         filename = xstrdup (resname);
114
115     if (!force)
116         force = secret? !any_secret : !any_public;
117
118     /* see whether we can determine the filetype */
119     if (rt == KEYDB_RESOURCE_TYPE_NONE) {
120         FILE *fp2 = fopen( filename, "rb" );
121
122         if (fp2) {
123             u32 magic;
124
125             /* FIXME: check for the keybox magic */
126             if (fread( &magic, 4, 1, fp2) == 1 ) 
127               {
128                 if (magic == 0x13579ace || magic == 0xce9a5713)
129                   ; /* GDBM magic - no more support */
130                 else
131                   rt = KEYDB_RESOURCE_TYPE_KEYBOX;
132               }
133             else /* maybe empty: assume ring */
134               rt = KEYDB_RESOURCE_TYPE_KEYBOX;
135             fclose (fp2);
136         }
137         else /* no file yet: create ring */
138           rt = KEYDB_RESOURCE_TYPE_KEYBOX;
139     }
140
141     switch (rt) {
142       case KEYDB_RESOURCE_TYPE_NONE:
143         log_error ("unknown type of key resource `%s'\n", url );
144         rc = GPGSM_General_Error;
145         goto leave;
146
147       case KEYDB_RESOURCE_TYPE_KEYBOX:
148 #if 0
149         fp = fopen (filename);
150         if (!iobuf && !force) {
151             rc = G10ERR_OPEN_FILE;
152             goto leave;
153         }
154
155         if (!fp) {
156             char *last_slash_in_filename;
157
158             last_slash_in_filename = strrchr (filename, DIRSEP_C);
159             *last_slash_in_filename = 0;
160
161             if (access(filename, F_OK)) {
162                 /* on the first time we try to create the default
163                    homedir and in this case the process will be
164                    terminated, so that on the next invocation it can
165                    read the options file in on startup */
166                 try_make_homedir (filename);
167                 rc = G10ERR_OPEN_FILE;
168                 *last_slash_in_filename = DIRSEP_C;
169                 goto leave;
170             }
171
172             *last_slash_in_filename = DIRSEP_C;
173
174             iobuf = iobuf_create (filename);
175             if (!iobuf) {
176                 log_error ( _("error creating keybox `%s': %s\n"),
177                             filename, strerror(errno));
178                 rc = G10ERR_OPEN_FILE;
179                 goto leave;
180             }
181             else {
182               #ifndef HAVE_DOSISH_SYSTEM
183                 if (secret && !opt.preserve_permissionws) {
184                     if (chmod (filename, S_IRUSR | S_IWUSR) ) {
185                         log_error (_("changing permission of "
186                                      " `%s' failed: %s\n"),
187                                    filename, strerror(errno) );
188                         rc = G10ERR_WRITE_FILE;
189                         goto leave;
190                     }
191                 }
192               #endif
193                 if (!opt.quiet)
194                     log_info (_("keybox `%s' created\n"), filename);
195                 created_fname = filename;
196             }
197         }
198         iobuf_close (iobuf);
199         iobuf = NULL;
200         if (created_fname) /* must invalidate that ugly cache */
201             iobuf_ioctl (NULL, 2, 0, (char*)created_fname);
202 #endif     
203         {
204           void *token = keybox_register_file (filename, secret);
205           if (!token)
206             ; /* already registered - ignore it */
207           else if (used_resources >= MAX_KEYDB_RESOURCES)
208               rc = GPGSM_Resource_Limit;
209           else 
210             {
211               all_resources[used_resources].type = rt;
212               all_resources[used_resources].u.kr = NULL; /* Not used here */
213               all_resources[used_resources].token = token;
214               all_resources[used_resources].secret = secret;
215               used_resources++;
216             }
217         }
218         break;
219
220       default:
221         log_error ("resource type of `%s' not supported\n", url);
222         rc = GPGSM_General_Error;
223         goto leave;
224     }
225
226     /* fixme: check directory permissions and print a warning */
227
228   leave:
229     if (rc)
230       log_error ("keyblock resource `%s': %s\n", filename, gpgsm_strerror(rc));
231     else if (secret)
232         any_secret = 1;
233     else
234         any_public = 1;
235     xfree (filename);
236     return rc;
237 }
238
239
240
241
242 KEYDB_HANDLE
243 keydb_new (int secret)
244 {
245   KEYDB_HANDLE hd;
246   int i, j;
247   
248   hd = xcalloc (1, sizeof *hd);
249   hd->found = -1;
250   
251   assert (used_resources <= MAX_KEYDB_RESOURCES);
252   for (i=j=0; i < used_resources; i++)
253     {
254       if (!all_resources[i].secret != !secret)
255         continue;
256       switch (all_resources[i].type)
257         {
258         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
259           break;
260         case KEYDB_RESOURCE_TYPE_KEYBOX:
261           hd->active[j].type   = all_resources[i].type;
262           hd->active[j].token  = all_resources[i].token;
263           hd->active[j].secret = all_resources[i].secret;
264           hd->active[j].u.kr = keybox_new (all_resources[i].token, secret);
265           if (!hd->active[j].u.kr) {
266             xfree (hd);
267             return NULL; /* fixme: release all previously allocated handles*/
268           }
269           j++;
270           break;
271         }
272     }
273   hd->used = j;
274   
275   active_handles++;
276   return hd;
277 }
278
279 void 
280 keydb_release (KEYDB_HANDLE hd)
281 {
282   int i;
283   
284   if (!hd)
285     return;
286   assert (active_handles > 0);
287   active_handles--;
288
289   unlock_all (hd);
290   for (i=0; i < hd->used; i++)
291     {
292       switch (hd->active[i].type) 
293         {
294         case KEYDB_RESOURCE_TYPE_NONE:
295           break;
296         case KEYDB_RESOURCE_TYPE_KEYBOX:
297           keybox_release (hd->active[i].u.kr);
298           break;
299         }
300     }
301
302     xfree (hd);
303 }
304
305
306 /* Return the name of the current resource.  This is function first
307    looks for the last found found, then for the current search
308    position, and last returns the first available resource.  The
309    returned string is only valid as long as the handle exists.  This
310    function does only return NULL if no handle is specified, in all
311    other error cases an empty string is returned.  */
312 const char *
313 keydb_get_resource_name (KEYDB_HANDLE hd)
314 {
315   int idx;
316   const char *s = NULL;
317   
318   if (!hd) 
319     return NULL;
320
321   if ( hd->found >= 0 && hd->found < hd->used) 
322     idx = hd->found;
323   else if ( hd->current >= 0 && hd->current < hd->used) 
324     idx = hd->current;
325   else
326     idx = 0;
327
328   switch (hd->active[idx].type) 
329     {
330     case KEYDB_RESOURCE_TYPE_NONE:
331       s = NULL; 
332       break;
333     case KEYDB_RESOURCE_TYPE_KEYBOX:
334       s = keybox_get_resource_name (hd->active[idx].u.kr);
335       break;
336     }
337   
338   return s? s: "";
339 }
340
341
342 \f
343 static int 
344 lock_all (KEYDB_HANDLE hd)
345 {
346   int i, rc = 0;
347
348   for (i=0; !rc && i < hd->used; i++) 
349     {
350       switch (hd->active[i].type) 
351         {
352         case KEYDB_RESOURCE_TYPE_NONE:
353           break;
354         case KEYDB_RESOURCE_TYPE_KEYBOX:
355           /* FIXME  rc = keybox_lock (hd->active[i].u.kr, 1);*/
356           break;
357         }
358     }
359
360     if (rc) 
361       {
362         /* revert the already set locks */
363         for (i--; i >= 0; i--) 
364           {
365             switch (hd->active[i].type) 
366               {
367               case KEYDB_RESOURCE_TYPE_NONE:
368                 break;
369               case KEYDB_RESOURCE_TYPE_KEYBOX:
370                 /* Fixme: keybox_lock (hd->active[i].u.kr, 0);*/
371                 break;
372               }
373           }
374       }
375     else
376       hd->locked = 1;
377
378     return rc;
379 }
380
381 static void
382 unlock_all (KEYDB_HANDLE hd)
383 {
384   int i;
385   
386   if (!hd->locked)
387     return;
388
389   for (i=hd->used-1; i >= 0; i--) 
390     {
391       switch (hd->active[i].type) 
392         {
393         case KEYDB_RESOURCE_TYPE_NONE:
394           break;
395         case KEYDB_RESOURCE_TYPE_KEYBOX:
396           /* fixme: keybox_lock (hd->active[i].u.kr, 0);*/
397           break;
398         }
399     }
400   hd->locked = 0;
401 }
402
403 \f
404 #if 0
405 /*
406  * Return the last found keybox.  Caller must free it.
407  * The returned keyblock has the kbode flag bit 0 set for the node with
408  * the public key used to locate the keyblock or flag bit 1 set for 
409  * the user ID node.
410  */
411 int
412 keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
413 {
414     int rc = 0;
415
416     if (!hd)
417         return G10ERR_INV_ARG;
418
419     if ( hd->found < 0 || hd->found >= hd->used) 
420         return -1; /* nothing found */
421
422     switch (hd->active[hd->found].type) {
423       case KEYDB_RESOURCE_TYPE_NONE:
424         rc = G10ERR_GENERAL; /* oops */
425         break;
426       case KEYDB_RESOURCE_TYPE_KEYBOX:
427         rc = keybox_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
428         break;
429     }
430
431     return rc;
432 }
433
434 /* 
435  * update the current keyblock with KB
436  */
437 int
438 keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
439 {
440     int rc = 0;
441
442     if (!hd)
443         return G10ERR_INV_ARG;
444
445     if ( hd->found < 0 || hd->found >= hd->used) 
446         return -1; /* nothing found */
447
448     if( opt.dry_run )
449         return 0;
450
451     rc = lock_all (hd);
452     if (rc)
453         return rc;
454
455     switch (hd->active[hd->found].type) {
456       case KEYDB_RESOURCE_TYPE_NONE:
457         rc = G10ERR_GENERAL; /* oops */
458         break;
459       case KEYDB_RESOURCE_TYPE_KEYBOX:
460         rc = keybox_update_keyblock (hd->active[hd->found].u.kr, kb);
461         break;
462     }
463
464     unlock_all (hd);
465     return rc;
466 }
467
468
469 /* 
470  * Insert a new KB into one of the resources. 
471  */
472 int
473 keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
474 {
475     int rc = -1;
476     int idx;
477
478     if (!hd) 
479         return G10ERR_INV_ARG;
480
481     if( opt.dry_run )
482         return 0;
483
484     if ( hd->found >= 0 && hd->found < hd->used) 
485         idx = hd->found;
486     else if ( hd->current >= 0 && hd->current < hd->used) 
487         idx = hd->current;
488     else
489         return G10ERR_GENERAL;
490
491     rc = lock_all (hd);
492     if (rc)
493         return rc;
494
495     switch (hd->active[idx].type) {
496       case KEYDB_RESOURCE_TYPE_NONE:
497         rc = G10ERR_GENERAL; /* oops */
498         break;
499       case KEYDB_RESOURCE_TYPE_KEYBOX:
500         rc = keybox_insert_keyblock (hd->active[idx].u.kr, kb);
501         break;
502     }
503
504     unlock_all (hd);
505     return rc;
506 }
507
508 #endif /*disabled code*/
509
510
511 \f
512 /*
513   Return the last found keybox.  Caller must free it.  The returned
514   keyblock has the kbode flag bit 0 set for the node with the public
515   key used to locate the keyblock or flag bit 1 set for the user ID
516   node.  */
517 int
518 keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
519 {
520   int rc = 0;
521
522   if (!hd)
523     return GPGSM_Invalid_Value;
524   
525   if ( hd->found < 0 || hd->found >= hd->used) 
526     return -1; /* nothing found */
527   
528   switch (hd->active[hd->found].type) 
529     {
530     case KEYDB_RESOURCE_TYPE_NONE:
531       rc = GPGSM_General_Error; /* oops */
532       break;
533     case KEYDB_RESOURCE_TYPE_KEYBOX:
534       rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert);
535       break;
536     }
537   
538   return rc;
539 }
540
541 /* 
542  * Insert a new Certificate into one of the resources. 
543  */
544 int
545 keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
546 {
547   int rc = -1;
548   int idx;
549   char digest[20];
550   
551   if (!hd) 
552     return GPGSM_Invalid_Value;
553
554   if (opt.dry_run)
555     return 0;
556   
557   if ( hd->found >= 0 && hd->found < hd->used) 
558     idx = hd->found;
559   else if ( hd->current >= 0 && hd->current < hd->used) 
560     idx = hd->current;
561   else
562     return GPGSM_General_Error;
563
564   rc = lock_all (hd);
565   if (rc)
566     return rc;
567
568   gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
569
570   switch (hd->active[idx].type) 
571     {
572     case KEYDB_RESOURCE_TYPE_NONE:
573       rc = GPGSM_General_Error;
574       break;
575     case KEYDB_RESOURCE_TYPE_KEYBOX:
576       rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest);
577       break;
578     }
579   
580   unlock_all (hd);
581   return rc;
582 }
583
584
585
586 /* update the current keyblock with KB */
587 int
588 keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
589 {
590   int rc = 0;
591   char digest[20];
592   
593   if (!hd)
594     return GPGSM_Invalid_Value;
595
596   if ( hd->found < 0 || hd->found >= hd->used) 
597     return -1; /* nothing found */
598
599   if (opt.dry_run)
600     return 0;
601
602   rc = lock_all (hd);
603   if (rc)
604     return rc;
605
606   gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
607
608   switch (hd->active[hd->found].type) 
609     {
610     case KEYDB_RESOURCE_TYPE_NONE:
611       rc = GPGSM_General_Error; /* oops */
612       break;
613     case KEYDB_RESOURCE_TYPE_KEYBOX:
614       rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest);
615       break;
616     }
617
618   unlock_all (hd);
619   return rc;
620 }
621
622
623 /* 
624  * The current keyblock or cert will be deleted.
625  */
626 int
627 keydb_delete (KEYDB_HANDLE hd)
628 {
629   int rc = -1;
630   
631   if (!hd)
632     return GPGSM_Invalid_Value;
633
634   if ( hd->found < 0 || hd->found >= hd->used) 
635     return -1; /* nothing found */
636
637   if( opt.dry_run )
638     return 0;
639
640   rc = lock_all (hd);
641   if (rc)
642     return rc;
643
644   switch (hd->active[hd->found].type)
645     {
646     case KEYDB_RESOURCE_TYPE_NONE:
647       rc = GPGSM_General_Error;
648       break;
649     case KEYDB_RESOURCE_TYPE_KEYBOX:
650       rc = keybox_delete (hd->active[hd->found].u.kr);
651       break;
652     }
653
654   unlock_all (hd);
655   return rc;
656 }
657
658
659 \f
660 /*
661  * Locate the default writable key resource, so that the next
662  * operation (which is only relevant for inserts) will be done on this
663  * resource.  
664  */
665 int
666 keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
667 {
668   int rc;
669   
670   if (!hd)
671     return GPGSM_Invalid_Value;
672   
673   rc = keydb_search_reset (hd); /* this does reset hd->current */
674   if (rc)
675     return rc;
676   
677   for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) 
678     {
679       switch (hd->active[hd->current].type) 
680         {
681         case KEYDB_RESOURCE_TYPE_NONE:
682           BUG();
683           break;
684         case KEYDB_RESOURCE_TYPE_KEYBOX:
685           if (keybox_is_writable (hd->active[hd->current].token))
686             return 0; /* found (hd->current is set to it) */
687           break;
688         }
689     }
690   
691   return -1;
692 }
693
694 /*
695  * Rebuild the caches of all key resources.
696  */
697 void
698 keydb_rebuild_caches (void)
699 {
700   int i;
701   
702   for (i=0; i < used_resources; i++)
703     {
704       if (all_resources[i].secret)
705         continue;
706       switch (all_resources[i].type)
707         {
708         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
709           break;
710         case KEYDB_RESOURCE_TYPE_KEYBOX:
711 /*            rc = keybox_rebuild_cache (all_resources[i].token); */
712 /*            if (rc) */
713 /*              log_error (_("failed to rebuild keybox cache: %s\n"), */
714 /*                         g10_errstr (rc)); */
715           break;
716         }
717     }
718 }
719
720
721
722 /* 
723  * Start the next search on this handle right at the beginning
724  */
725 int 
726 keydb_search_reset (KEYDB_HANDLE hd)
727 {
728   int i, rc = 0;
729   
730   if (!hd)
731     return GPGSM_Invalid_Value;
732
733   hd->current = 0; 
734   hd->found = -1;
735   /* and reset all resources */
736   for (i=0; !rc && i < hd->used; i++) 
737     {
738       switch (hd->active[i].type) 
739         {
740         case KEYDB_RESOURCE_TYPE_NONE:
741           break;
742         case KEYDB_RESOURCE_TYPE_KEYBOX:
743           rc = keybox_search_reset (hd->active[i].u.kr);
744           break;
745         }
746     }
747   return rc; /* fixme: we need to map error codes or share them with
748                 all modules*/
749 }
750
751 /* 
752  * Search through all keydb resources, starting at the current position,
753  * for a keyblock which contains one of the keys described in the DESC array.
754  */
755 int 
756 keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
757 {
758   int rc = -1;
759   
760   if (!hd)
761     return GPGSM_Invalid_Value;
762
763   while (rc == -1 && hd->current >= 0 && hd->current < hd->used) 
764     {
765       switch (hd->active[hd->current].type) 
766         {
767         case KEYDB_RESOURCE_TYPE_NONE:
768           BUG(); /* we should never see it here */
769           break;
770         case KEYDB_RESOURCE_TYPE_KEYBOX:
771           rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc);
772           break;
773         }
774       if (rc == -1) /* EOF -> switch to next resource */
775         hd->current++; 
776       else if (!rc)
777         hd->found = hd->current;
778     }
779   
780   return rc; 
781 }
782
783
784 int
785 keydb_search_first (KEYDB_HANDLE hd)
786 {
787   KEYDB_SEARCH_DESC desc;
788   
789   memset (&desc, 0, sizeof desc);
790   desc.mode = KEYDB_SEARCH_MODE_FIRST;
791   return keydb_search (hd, &desc, 1);
792 }
793
794 int
795 keydb_search_next (KEYDB_HANDLE hd)
796 {
797   KEYDB_SEARCH_DESC desc;
798   
799   memset (&desc, 0, sizeof desc);
800   desc.mode = KEYDB_SEARCH_MODE_NEXT;
801   return keydb_search (hd, &desc, 1);
802 }
803
804 int
805 keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
806 {
807   KEYDB_SEARCH_DESC desc;
808   
809   memset (&desc, 0, sizeof desc);
810   desc.mode = KEYDB_SEARCH_MODE_LONG_KID;
811 /*    desc.u.kid[0] = kid[0]; */
812 /*    desc.u.kid[1] = kid[1]; */
813   return keydb_search (hd, &desc, 1);
814 }
815
816 int
817 keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
818 {
819   KEYDB_SEARCH_DESC desc;
820   
821   memset (&desc, 0, sizeof desc);
822   desc.mode = KEYDB_SEARCH_MODE_FPR;
823   memcpy (desc.u.fpr, fpr, 20);
824   return keydb_search (hd, &desc, 1);
825 }
826
827 int
828 keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
829 {
830   KEYDB_SEARCH_DESC desc;
831   int rc;
832   
833   memset (&desc, 0, sizeof desc);
834   desc.mode = KEYDB_SEARCH_MODE_ISSUER;
835   desc.u.name = issuer;
836   rc = keydb_search (hd, &desc, 1);
837   return rc;
838 }
839
840 int
841 keydb_search_issuer_sn (KEYDB_HANDLE hd,
842                         const char *issuer, const unsigned char *serial)
843 {
844   KEYDB_SEARCH_DESC desc;
845   int rc;
846   
847   memset (&desc, 0, sizeof desc);
848   desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN;
849   desc.sn = serial;
850   desc.u.name = issuer;
851   rc = keydb_search (hd, &desc, 1);
852   return rc;
853 }
854
855
856