* keylist.c (list_cert_colon): Fixed listing of crt record; the
[gnupg.git] / sm / keydb.c
index ef839ed..4f7bbb5 100644 (file)
@@ -78,134 +78,130 @@ static void unlock_all (KEYDB_HANDLE hd);
 int
 keydb_add_resource (const char *url, int force, int secret)
 {
-    static int any_secret, any_public;
-    const char *resname = url;
-    char *filename = NULL;
-    int rc = 0; 
-    KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
-/*      const char *created_fname = NULL; */
-
-    /* Do we have an URL?
-     * gnupg-ring:filename  := this is a plain keybox
-     * filename := See what is is, but create as plain keybox.
-     */
-    if (strlen (resname) > 11) {
-       if (!strncmp( resname, "gnupg-kbx:", 10) ) {
-           rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-           resname += 11;
+  static int any_secret, any_public;
+  const char *resname = url;
+  char *filename = NULL;
+  int rc = 0; 
+  FILE *fp;
+  KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
+  const char *created_fname = NULL;
+
+  /* Do we have an URL?
+     gnupg-kbx:filename := this is a plain keybox
+     filename := See what is is, but create as plain keybox.
+  */
+  if (strlen (resname) > 10) 
+    {
+      if (!strncmp (resname, "gnupg-kbx:", 10) )
+        {
+          rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+          resname += 10;
        }
-      #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
-       else if (strchr (resname, ':')) {
-           log_error ("invalid key resource URL `%s'\n", url );
-           rc = GPGSM_General_Error;
-           goto leave;
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
+      else if (strchr (resname, ':'))
+        {
+          log_error ("invalid key resource URL `%s'\n", url );
+          rc = GNUPG_General_Error;
+          goto leave;
        }
-      #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
+#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
     }
 
-    if (*resname != DIRSEP_C ) { /* do tilde expansion etc */
-       if (strchr(resname, DIRSEP_C) )
-           filename = make_filename (resname, NULL);
-       else
-           filename = make_filename (opt.homedir, resname, NULL);
+  if (*resname != DIRSEP_C )
+    { /* do tilde expansion etc */
+      if (strchr(resname, DIRSEP_C) )
+        filename = make_filename (resname, NULL);
+      else
+        filename = make_filename (opt.homedir, resname, NULL);
     }
-    else
-       filename = xstrdup (resname);
-
-    if (!force)
-       force = secret? !any_secret : !any_public;
-
-    /* see whether we can determine the filetype */
-    if (rt == KEYDB_RESOURCE_TYPE_NONE) {
-       FILE *fp2 = fopen( filename, "rb" );
-
-       if (fp2) {
-           u32 magic;
-
-            /* FIXME: check for the keybox magic */
-           if (fread( &magic, 4, 1, fp2) == 1 ) 
-              {
-               if (magic == 0x13579ace || magic == 0xce9a5713)
-                  ; /* GDBM magic - no more support */
-               else
-                  rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-              }
-           else /* maybe empty: assume ring */
+  else
+    filename = xstrdup (resname);
+  
+  if (!force)
+    force = secret? !any_secret : !any_public;
+  
+  /* see whether we can determine the filetype */
+  if (rt == KEYDB_RESOURCE_TYPE_NONE)
+    {
+      FILE *fp2 = fopen( filename, "rb" );
+      
+      if (fp2) {
+        u32 magic;
+        
+        /* FIXME: check for the keybox magic */
+        if (fread( &magic, 4, 1, fp2) == 1 ) 
+          {
+            if (magic == 0x13579ace || magic == 0xce9a5713)
+              ; /* GDBM magic - no more support */
+            else
               rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-           fclose (fp2);
-       }
-       else /* no file yet: create ring */
+          }
+        else /* maybe empty: assume ring */
           rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+        fclose (fp2);
+      }
+      else /* no file yet: create ring */
+        rt = KEYDB_RESOURCE_TYPE_KEYBOX;
     }
-
-    switch (rt) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-       log_error ("unknown type of key resource `%s'\n", url );
-       rc = GPGSM_General_Error;
-       goto leave;
-
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-#if 0
-       fp = fopen (filename);
-       if (!iobuf && !force) {
-           rc = G10ERR_OPEN_FILE;
-           goto leave;
-       }
-
-       if (!fp) {
-           char *last_slash_in_filename;
-
-           last_slash_in_filename = strrchr (filename, DIRSEP_C);
-           *last_slash_in_filename = 0;
-
-           if (access(filename, F_OK)) {
-               /* on the first time we try to create the default
+    
+  switch (rt)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      log_error ("unknown type of key resource `%s'\n", url );
+      rc = GNUPG_General_Error;
+      goto leave;
+      
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      fp = fopen (filename, "rb");
+      if (!fp && !force)
+        {
+          rc = GNUPG_File_Open_Error;
+          goto leave;
+        }
+      
+      if (!fp)
+        { /* no file */
+#if 0 /* no autocreate of the homedirectory yet */
+          {
+            char *last_slash_in_filename;
+            
+            last_slash_in_filename = strrchr (filename, DIRSEP_C);
+            *last_slash_in_filename = 0;
+            if (access (filename, F_OK))
+              { /* on the first time we try to create the default
                    homedir and in this case the process will be
-                   terminated, so that on the next invocation it can
+                   terminated, so that on the next invocation can
                    read the options file in on startup */
-               try_make_homedir (filename);
-               rc = G10ERR_OPEN_FILE;
-               *last_slash_in_filename = DIRSEP_C;
-               goto leave;
+                try_make_homedir (filename);
+                rc = GNUPG_File_Open_Error;
+                *last_slash_in_filename = DIRSEP_C;
+                goto leave;
+              }
+            *last_slash_in_filename = DIRSEP_C;
+          }
+#endif
+          fp = fopen (filename, "w");
+          if (!fp)
+            {
+              log_error (_("error creating keybox `%s': %s\n"),
+                         filename, strerror(errno));
+              rc = GNUPG_File_Create_Error;
+              goto leave;
            }
 
-           *last_slash_in_filename = DIRSEP_C;
-
-           iobuf = iobuf_create (filename);
-           if (!iobuf) {
-               log_error ( _("error creating keybox `%s': %s\n"),
-                            filename, strerror(errno));
-               rc = G10ERR_OPEN_FILE;
-               goto leave;
-           }
-           else {
-             #ifndef HAVE_DOSISH_SYSTEM
-               if (secret && !opt.preserve_permissionws) {
-                   if (chmod (filename, S_IRUSR | S_IWUSR) ) {
-                       log_error (_("changing permission of "
-                                     " `%s' failed: %s\n"),
-                                   filename, strerror(errno) );
-                       rc = G10ERR_WRITE_FILE;
-                       goto leave;
-                   }
-               }
-             #endif
-               if (!opt.quiet)
-                    log_info (_("keybox `%s' created\n"), filename);
-                created_fname = filename;
-           }
+          if (!opt.quiet)
+            log_info (_("keybox `%s' created\n"), filename);
+          created_fname = filename;
        }
-       iobuf_close (iobuf);
-       iobuf = NULL;
-        if (created_fname) /* must invalidate that ugly cache */
-            iobuf_ioctl (NULL, 2, 0, (char*)created_fname);
-#endif     
+       fclose (fp);
+       fp = NULL;
+        /* now regsiter the file */
         {
           void *token = keybox_register_file (filename, secret);
           if (!token)
             ; /* already registered - ignore it */
           else if (used_resources >= MAX_KEYDB_RESOURCES)
-              rc = GPGSM_Resource_Limit;
+            rc = GNUPG_Resource_Limit;
           else 
             {
               all_resources[used_resources].type = rt;
@@ -216,29 +212,26 @@ keydb_add_resource (const char *url, int force, int secret)
             }
         }
        break;
-
-      default:
-       log_error ("resource type of `%s' not supported\n", url);
-       rc = GPGSM_General_Error;
-       goto leave;
+    default:
+      log_error ("resource type of `%s' not supported\n", url);
+      rc = GNUPG_Not_Supported;
+      goto leave;
     }
 
-    /* fixme: check directory permissions and print a warning */
+  /* fixme: check directory permissions and print a warning */
 
 leave:
-    if (rc)
-      log_error ("keyblock resource `%s': %s\n", filename, gpgsm_strerror(rc));
-    else if (secret)
-       any_secret = 1;
-    else
-       any_public = 1;
-    xfree (filename);
-    return rc;
+ leave:
+  if (rc)
+    log_error ("keyblock resource `%s': %s\n", filename, gnupg_strerror(rc));
+  else if (secret)
+    any_secret = 1;
+  else
+    any_public = 1;
+  xfree (filename);
+  return rc;
 }
 
 
-
-
 KEYDB_HANDLE
 keydb_new (int secret)
 {
@@ -520,7 +513,7 @@ keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
   int rc = 0;
 
   if (!hd)
-    return GPGSM_Invalid_Value;
+    return GNUPG_Invalid_Value;
   
   if ( hd->found < 0 || hd->found >= hd->used) 
     return -1; /* nothing found */
@@ -528,7 +521,7 @@ keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
   switch (hd->active[hd->found].type) 
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GPGSM_General_Error; /* oops */
+      rc = GNUPG_General_Error; /* oops */
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert);
@@ -549,7 +542,7 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
   char digest[20];
   
   if (!hd) 
-    return GPGSM_Invalid_Value;
+    return GNUPG_Invalid_Value;
 
   if (opt.dry_run)
     return 0;
@@ -559,7 +552,7 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
   else if ( hd->current >= 0 && hd->current < hd->used) 
     idx = hd->current;
   else
-    return GPGSM_General_Error;
+    return GNUPG_General_Error;
 
   rc = lock_all (hd);
   if (rc)
@@ -570,7 +563,7 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
   switch (hd->active[idx].type) 
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GPGSM_General_Error;
+      rc = GNUPG_General_Error;
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest);
@@ -591,7 +584,7 @@ keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
   char digest[20];
   
   if (!hd)
-    return GPGSM_Invalid_Value;
+    return GNUPG_Invalid_Value;
 
   if ( hd->found < 0 || hd->found >= hd->used) 
     return -1; /* nothing found */
@@ -608,7 +601,7 @@ keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
   switch (hd->active[hd->found].type) 
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GPGSM_General_Error; /* oops */
+      rc = GNUPG_General_Error; /* oops */
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest);
@@ -629,7 +622,7 @@ keydb_delete (KEYDB_HANDLE hd)
   int rc = -1;
   
   if (!hd)
-    return GPGSM_Invalid_Value;
+    return GNUPG_Invalid_Value;
 
   if ( hd->found < 0 || hd->found >= hd->used) 
     return -1; /* nothing found */
@@ -644,7 +637,7 @@ keydb_delete (KEYDB_HANDLE hd)
   switch (hd->active[hd->found].type)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GPGSM_General_Error;
+      rc = GNUPG_General_Error;
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_delete (hd->active[hd->found].u.kr);
@@ -668,7 +661,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
   int rc;
   
   if (!hd)
-    return GPGSM_Invalid_Value;
+    return GNUPG_Invalid_Value;
   
   rc = keydb_search_reset (hd); /* this does reset hd->current */
   if (rc)
@@ -728,7 +721,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
   int i, rc = 0;
   
   if (!hd)
-    return GPGSM_Invalid_Value;
+    return GNUPG_Invalid_Value;
 
   hd->current = 0; 
   hd->found = -1;
@@ -758,7 +751,7 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
   int rc = -1;
   
   if (!hd)
-    return GPGSM_Invalid_Value;
+    return GNUPG_Invalid_Value;
 
   while (rc == -1 && hd->current >= 0 && hd->current < hd->used) 
     {
@@ -839,18 +832,368 @@ keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
 
 int
 keydb_search_issuer_sn (KEYDB_HANDLE hd,
-                        const char *issuer, const unsigned char *serial)
+                        const char *issuer, KsbaConstSexp serial)
 {
   KEYDB_SEARCH_DESC desc;
   int rc;
+  const unsigned char *s;
   
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN;
-  desc.sn = serial;
+  s = serial;
+  if (*s !='(')
+    return GNUPG_Invalid_Value;
+  s++;
+  for (desc.snlen = 0; digitp (s); s++)
+    desc.snlen = 10*desc.snlen + atoi_1 (s);
+  if (*s !=':')
+    return GNUPG_Invalid_Value;
+  desc.sn = s+1;
   desc.u.name = issuer;
   rc = keydb_search (hd, &desc, 1);
   return rc;
 }
 
+int
+keydb_search_subject (KEYDB_HANDLE hd, const char *name)
+{
+  KEYDB_SEARCH_DESC desc;
+  int rc;
+  
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_SUBJECT;
+  desc.u.name = name;
+  rc = keydb_search (hd, &desc, 1);
+  return rc;
+}
+
+
+static int
+hextobyte (const unsigned char *s)
+{
+  int c;
+
+  if( *s >= '0' && *s <= '9' )
+    c = 16 * (*s - '0');
+  else if ( *s >= 'A' && *s <= 'F' )
+    c = 16 * (10 + *s - 'A');
+  else if ( *s >= 'a' && *s <= 'f' )
+    c = 16 * (10 + *s - 'a');
+  else
+    return -1;
+  s++;
+  if ( *s >= '0' && *s <= '9' )
+    c += *s - '0';
+  else if ( *s >= 'A' && *s <= 'F' )
+    c += 10 + *s - 'A';
+  else if ( *s >= 'a' && *s <= 'f' )
+    c += 10 + *s - 'a';
+  else
+    return -1;
+  return c;
+}
+
+
+static int
+classify_user_id (const char *name, 
+                  KEYDB_SEARCH_DESC *desc,
+                  int *force_exact )
+{
+  const char *s;
+  int hexprefix = 0;
+  int hexlength;
+  int mode = 0;   
+    
+  /* clear the structure so that the mode field is set to zero unless
+   * we set it to the correct value right at the end of this function */
+  memset (desc, 0, sizeof *desc);
+  *force_exact = 0;
+  /* skip leading spaces.  Fixme: what about trailing white space? */
+  for(s = name; *s && spacep (s); s++ )
+    ;
+
+  switch (*s) 
+    {
+    case 0:  /* empty string is an error */
+      return 0;
+
+    case '.': /* an email address, compare from end */
+      mode = KEYDB_SEARCH_MODE_MAILEND;
+      s++;
+      desc->u.name = s;
+      break;
+
+    case '<': /* an email address */
+      mode = KEYDB_SEARCH_MODE_MAIL;
+      s++;
+      desc->u.name = s;
+      break;
+
+    case '@':  /* part of an email address */
+      mode = KEYDB_SEARCH_MODE_MAILSUB;
+      s++;
+      desc->u.name = s;
+      break;
+
+    case '=':  /* exact compare */
+      mode = KEYDB_SEARCH_MODE_EXACT;
+      s++;
+      desc->u.name = s;
+      break;
+
+    case '*':  /* case insensitive substring search */
+      mode = KEYDB_SEARCH_MODE_SUBSTR;
+      s++;
+      desc->u.name = s;
+      break;
+
+    case '+':  /* compare individual words */
+      mode = KEYDB_SEARCH_MODE_WORDS;
+      s++;
+      desc->u.name = s;
+      break;
+
+    case '/': /* subject's DN */
+      s++;
+      if (!*s || spacep (s))
+        return 0; /* no DN or prefixed with a space */
+      desc->u.name = s;
+      mode = KEYDB_SEARCH_MODE_SUBJECT;
+      break;
+
+    case '#':
+      { 
+        const char *si;
+        
+        s++;
+        if ( *s == '/')
+          { /* "#/" indicates an issuer's DN */
+            s++;
+            if (!*s || spacep (s))
+              return 0; /* no DN or prefixed with a space */
+            desc->u.name = s;
+            mode = KEYDB_SEARCH_MODE_ISSUER;
+          }
+        else 
+          { /* serialnumber + optional issuer ID */
+            for (si=s; *si && *si != '/'; si++)
+              {
+                if (!strchr("01234567890abcdefABCDEF", *si))
+                  return 0; /* invalid digit in serial number*/
+              }
+            desc->sn = s;
+            desc->snlen = -1;
+            if (!*si)
+              mode = KEYDB_SEARCH_MODE_SN;
+            else
+              {
+                s = si+1;
+                if (!*s || spacep (s))
+                  return 0; /* no DN or prefixed with a space */
+                desc->u.name = s;
+                mode = KEYDB_SEARCH_MODE_ISSUER_SN;
+              }
+          }
+      }
+      break;
+
+    case ':': /*Unified fingerprint */
+      {  
+        const char *se, *si;
+        int i;
+        
+        se = strchr (++s,':');
+        if (!se)
+          return 0;
+        for (i=0,si=s; si < se; si++, i++ )
+          {
+            if (!strchr("01234567890abcdefABCDEF", *si))
+              return 0; /* invalid digit */
+          }
+        if (i != 32 && i != 40)
+          return 0; /* invalid length of fpr*/
+        for (i=0,si=s; si < se; i++, si +=2) 
+          desc->u.fpr[i] = hextobyte(si);
+        for (; i < 20; i++)
+          desc->u.fpr[i]= 0;
+        s = se + 1;
+        mode = KEYDB_SEARCH_MODE_FPR;
+      } 
+      break;
+           
+    default:
+      if (s[0] == '0' && s[1] == 'x')
+        {
+          hexprefix = 1;
+          s += 2;
+        }
+
+      hexlength = strspn(s, "0123456789abcdefABCDEF");
+      if (hexlength >= 8 && s[hexlength] =='!')
+        {
+          *force_exact = 1;
+          hexlength++; /* just for the following check */
+        }
+      
+      /* check if a hexadecimal number is terminated by EOS or blank */
+      if (hexlength && s[hexlength] && !spacep (s+hexlength)) 
+        {
+          if (hexprefix) /* a "0x" prefix without correct */
+            return 0;   /* termination is an error */
+          /* The first chars looked like a hex number, but really is
+             not */
+          hexlength = 0;  
+        }
+      
+      if (*force_exact)
+        hexlength--; /* remove the bang */
+
+      if (hexlength == 8
+          || (!hexprefix && hexlength == 9 && *s == '0'))
+        { /* short keyid */
+          unsigned long kid;
+          if (hexlength == 9)
+            s++;
+          kid = strtoul( s, NULL, 16 );
+          desc->u.kid[4] = kid >> 24; 
+          desc->u.kid[5] = kid >> 16; 
+          desc->u.kid[6] = kid >>  8; 
+          desc->u.kid[7] = kid; 
+          mode = KEYDB_SEARCH_MODE_SHORT_KID;
+        }
+      else if (hexlength == 16
+               || (!hexprefix && hexlength == 17 && *s == '0'))
+        { /* complete keyid */
+          unsigned long kid0, kid1;
+          char buf[9];
+          if (hexlength == 17)
+            s++;
+          mem2str(buf, s, 9 );
+          kid0 = strtoul (buf, NULL, 16);
+          kid1 = strtoul (s+8, NULL, 16);
+          desc->u.kid[0] = kid0 >> 24; 
+          desc->u.kid[1] = kid0 >> 16; 
+          desc->u.kid[2] = kid0 >>  8; 
+          desc->u.kid[3] = kid0; 
+          desc->u.kid[4] = kid1 >> 24; 
+          desc->u.kid[5] = kid1 >> 16; 
+          desc->u.kid[6] = kid1 >>  8; 
+          desc->u.kid[7] = kid1; 
+          mode = KEYDB_SEARCH_MODE_LONG_KID;
+        }
+      else if (hexlength == 32
+               || (!hexprefix && hexlength == 33 && *s == '0'))
+        { /* md5 fingerprint */
+          int i;
+          if (hexlength == 33)
+            s++;
+          memset(desc->u.fpr+16, 0, 4); 
+          for (i=0; i < 16; i++, s+=2) 
+            {
+              int c = hextobyte(s);
+              if (c == -1)
+                return 0;
+              desc->u.fpr[i] = c;
+            }
+          mode = KEYDB_SEARCH_MODE_FPR16;
+        }
+      else if (hexlength == 40
+               || (!hexprefix && hexlength == 41 && *s == '0'))
+        { /* sha1/rmd160 fingerprint */
+          int i;
+          if (hexlength == 41)
+            s++;
+          for (i=0; i < 20; i++, s+=2) 
+            {
+              int c = hextobyte(s);
+              if (c == -1)
+                return 0;
+              desc->u.fpr[i] = c;
+            }
+          mode = KEYDB_SEARCH_MODE_FPR20;
+        }
+      else if (!hexprefix)
+        { /* default is substring search */
+          *force_exact = 0;
+          desc->u.name = s;
+          mode = KEYDB_SEARCH_MODE_SUBSTR; 
+        }
+      else
+       { /* hex number with a prefix but a wrong length */
+          return 0;
+        }
+    }
+  
+  desc->mode = mode;
+  return mode;
+}
+
+
+int
+keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc)
+{
+  int dummy;
+  KEYDB_SEARCH_DESC dummy_desc;
+
+  if (!desc)
+    desc = &dummy_desc;
+
+  if (!classify_user_id (name, desc, &dummy))
+    return GNUPG_Invalid_Name;
+  return 0;
+}
+
+\f
+/* Store the certificate in the key Db but make sure that it does not
+   already exists.  We do this simply by comparing the fingerprint */
+int
+keydb_store_cert (KsbaCert cert)
+{
+  KEYDB_HANDLE kh;
+  int rc;
+  unsigned char fpr[20];
+
+  if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+    {
+      log_error (_("failed to get the fingerprint\n"));
+      return GNUPG_General_Error;
+    }
+
+  kh = keydb_new (0);
+  if (!kh)
+    {
+      log_error (_("failed to allocate keyDB handle\n"));
+      return GNUPG_Out_Of_Core;
+    }
+
+  rc = keydb_search_fpr (kh, fpr);
+  if (rc != -1)
+    {
+      keydb_release (kh);
+      if (!rc)
+        return 0; /* okay */
+      log_error (_("problem looking for existing certificate: %s\n"),
+                 gnupg_strerror (rc));
+      return rc;
+    }
+
+  rc = keydb_locate_writable (kh, 0);
+  if (rc)
+    {
+      log_error (_("error finding writable keyDB: %s\n"), gnupg_strerror (rc));
+      keydb_release (kh);
+      return rc;
+    }
+
+  rc = keydb_insert_cert (kh, cert);
+  if (rc)
+    {
+      log_error (_("error storing certificate: %s\n"), gnupg_strerror (rc));
+      keydb_release (kh);
+      return rc;
+    }
+  keydb_release (kh);               
+  return 0;
+}