See ChangeLog: Wed Dec 23 13:34:22 CET 1998 Werner Koch
[gnupg.git] / g10 / ringedit.c
index 119cd67..d46094c 100644 (file)
@@ -1,14 +1,14 @@
 /* ringedit.c -  Function for key ring editing
  *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of GNUPG.
+ * This file is part of GnuPG.
  *
- * GNUPG is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
  * (at your option) any later version.
  *
- * GNUPG is distributed in the hope that it will be useful,
+ * 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.
@@ -35,8 +35,6 @@
  *
  *  - Delete a key block
  *
- * FIXME:  Keep track of all nodes, so that a change is propagated
- *        to all nodes. (or use shallow copies and ref-counting?)
  */
 
 
 #include "mpi.h"
 #include "iobuf.h"
 #include "keydb.h"
+#include "host2net.h"
 #include "options.h"
+#include "main.h"
 #include "i18n.h"
 
-#undef HAVE_LIBGDBM  /* <--- not ready */
 
 struct resource_table_struct {
     int used;
@@ -78,6 +77,7 @@ typedef struct resource_table_struct RESTBL;
 
 #define MAX_RESOURCES 10
 static RESTBL resource_table[MAX_RESOURCES];
+static const char *keyring_lock;
 
 static int search( PACKET *pkt, KBPOS *kbpos, int secret );
 
@@ -88,6 +88,14 @@ static int keyring_read( KBPOS *kbpos, KBNODE *ret_root );
 static int keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs );
 static int keyring_copy( KBPOS *kbpos, int mode, KBNODE root );
 
+#ifdef HAVE_LIBGDBM
+static int do_gdbm_store( KBPOS *kbpos, KBNODE root, int update );
+static int do_gdbm_locate( GDBM_FILE dbf, KBPOS *kbpos,
+                                         const byte *fpr, int fprlen );
+static int do_gdbm_locate_by_keyid( GDBM_FILE dbf, KBPOS *kbpos, u32 *keyid );
+static int do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root );
+static int do_gdbm_enum( KBPOS *kbpos, KBNODE *ret_root );
+#endif
 
 
 static RESTBL *
@@ -100,6 +108,23 @@ check_pos( KBPOS *kbpos )
     return resource_table + kbpos->resno;
 }
 
+#ifdef HAVE_LIBGDBM
+static void
+fatal_gdbm_error( const char *string )
+{
+    log_fatal("gdbm failed: %s\n", string);
+}
+
+#endif /* HAVE_LIBGDBM */
+
+static void
+cleanup( void )
+{
+    if( keyring_lock ) {
+       release_dotlock( keyring_lock );
+       keyring_lock = NULL;
+    }
+}
 
 /****************************************************************
  ****************** public functions ****************************
@@ -137,14 +162,20 @@ enum_keyblock_resources( int *sequence, int secret )
 int
 add_keyblock_resource( const char *url, int force, int secret )
 {
+    static int initialized = 0;
     static int any_secret, any_public;
     const char *resname = url;
-    IOBUF iobuf;
+    IOBUF iobuf = NULL;
     int i;
     char *filename = NULL;
     int rc = 0;
     enum resource_type rt = rt_UNKNOWN;
 
+    if( !initialized ) {
+       initialized = 1;
+       atexit( cleanup );
+    }
+
     /* Do we have an URL?
      * gnupg-gdbm:filename  := this is a GDBM resource
      * gnupg-ring:filename  := this is a plain keyring
@@ -188,32 +219,86 @@ add_keyblock_resource( const char *url, int force, int secret )
        goto leave;
     }
 
+    /* see whether we can determine the filetype */
+    if( rt == rt_UNKNOWN ) {
+       FILE *fp = fopen( filename, "rb" );
+
+       if( fp ) {
+           u32 magic;
+
+           if( fread( &magic, 4, 1, fp) == 1 ) {
+               if( magic == 0x13579ace )
+                   rt = rt_GDBM;
+               else if( magic == 0xce9a5713 )
+                   log_error("%s: endianess does not match\n", url );
+               else
+                   rt = rt_RING;
+           }
+           else /* maybe empty: assume ring */
+               rt = rt_RING;
+           fclose( fp );
+       }
+       else /* no file yet: create ring */
+           rt = rt_RING;
+    }
 
     switch( rt ) {
       case rt_UNKNOWN:
+       log_error("%s: unknown resource type\n", url );
+       rc = G10ERR_GENERAL;
+       goto leave;
+
       case rt_RING:
-       iobuf = iobuf_fopen( filename, "rb" );
+       iobuf = iobuf_open( filename );
        if( !iobuf && !force ) {
            rc = G10ERR_OPEN_FILE;
            goto leave;
        }
 
        if( !iobuf ) {
+           char *last_slash_in_filename;
+
+           last_slash_in_filename = strrchr(filename, '/');
+           *last_slash_in_filename = 0;
+
+           if( access(filename, F_OK) ) {
+               if( strlen(filename) >= 7
+                   && !strcmp(filename+strlen(filename)-7, "/.gnupg") ) {
+                 #if __MINGW32__
+                   if( mkdir(filename) )
+                 #else
+                   if( mkdir(filename, S_IRUSR|S_IWUSR|S_IXUSR) )
+                 #endif
+                   {
+                       log_error( _("%s: can't create directory: %s\n"),
+                                 filename, strerror(errno));
+                       rc = G10ERR_OPEN_FILE;
+                       goto leave;
+                   }
+                   else
+                       log_info( _("%s: directory created\n"), filename );
+                   copy_options_file( filename );
+               }
+               else
+               {
+                   rc = G10ERR_OPEN_FILE;
+                   goto leave;
+               }
+           }
+
+           *last_slash_in_filename = '/';
+
            iobuf = iobuf_create( filename );
            if( !iobuf ) {
-               log_error("%s: can't create: %s\n", filename, strerror(errno));
+               log_error(_("%s: can't create keyring: %s\n"),
+                                           filename, strerror(errno));
                rc = G10ERR_OPEN_FILE;
                goto leave;
            }
            else
-               log_info("%s: keyring created\n", filename );
+               log_info(_("%s: keyring created\n"), filename );
        }
-       /* fixme: see whether it is really a ring or if type is unknown,
-        * try to figure out of what type it is
-        */
-       rt = rt_RING; /* <--- FIXME */
-
-      #ifdef __MINGW32__
+      #if __MINGW32__ || 1
        /* must close it again */
        iobuf_close( iobuf );
        iobuf = NULL;
@@ -222,6 +307,17 @@ add_keyblock_resource( const char *url, int force, int secret )
 
     #ifdef HAVE_LIBGDBM
       case rt_GDBM:
+       resource_table[i].dbf = gdbm_open( filename, 0,
+                                          force? GDBM_WRCREAT : GDBM_WRITER,
+                                          S_IRUSR | S_IWUSR |
+                                          S_IRGRP | S_IWGRP | S_IROTH,
+                                          fatal_gdbm_error );
+       if( !resource_table[i].dbf ) {
+           log_error("%s: can't open gdbm file: %s\n",
+                           filename, gdbm_strerror(gdbm_errno));
+           rc = G10ERR_OPEN_FILE;
+           goto leave;
+       }
        break;
     #endif
 
@@ -310,14 +406,21 @@ search( PACKET *pkt, KBPOS *kbpos, int secret )
                                                 resource_table[i].fname );
                break;
             #ifdef HAVE_LIBGDBM
-             case rt_GDBM
-               rc = do_gdbm_search( pkt, kbpos, resource_table[i].dbf,
-                                                resource_table[i].fname );
+             case rt_GDBM: {
+                   PKT_public_key *req_pk = pkt->pkt.public_key;
+                   byte fpr[20];
+                   size_t fprlen;
+
+                   fingerprint_from_pk( req_pk, fpr, &fprlen );
+                   rc = do_gdbm_locate( resource_table[i].dbf,
+                                        kbpos, fpr, fprlen );
+               }
                break;
             #endif
              default: BUG();
            }
 
+           kbpos->rt = resource_table[i].rt;
            if( !rc ) {
                kbpos->resno = i;
                kbpos->fp = NULL;
@@ -345,7 +448,7 @@ find_keyblock_byname( KBPOS *kbpos, const char *username )
     PKT_public_key *pk = m_alloc_clear( sizeof *pk );
     int rc;
 
-    rc = get_pubkey_byname( pk, username );
+    rc = get_pubkey_byname( NULL, pk, username, NULL );
     if( rc ) {
        free_public_key(pk);
        return rc;
@@ -404,6 +507,98 @@ find_secret_keyblock_byname( KBPOS *kbpos, const char *username )
 }
 
 
+/****************
+ * Locate a keyblock in a database which is capable of direct access
+ * Put all information into KBPOS, which can be later be to access this
+ * key block.
+ * This function looks into all registered keyblock sources.
+ *
+ * Returns: 0 if found,
+ *         -1 if not found
+ *         G10ERR_UNSUPPORTED if no resource is able to handle this
+ *         or another errorcode.
+ */
+int
+locate_keyblock_by_fpr( KBPOS *kbpos, const byte *fpr, int fprlen, int secret )
+{
+    RESTBL *rentry;
+    int i, rc, any=0, last_rc=-1;
+
+
+    for(i=0, rentry = resource_table; i < MAX_RESOURCES; i++, rentry++ ) {
+       if( rentry->used && !rentry->secret == !secret ) {
+           kbpos->rt = rentry->rt;
+           switch( rentry->rt ) {
+            #ifdef HAVE_LIBGDBM
+             case rt_GDBM:
+               any = 1;
+               rc = do_gdbm_locate( rentry->dbf, kbpos, fpr, fprlen );
+               break;
+            #endif
+             default:
+               rc = G10ERR_UNSUPPORTED;
+               break;
+           }
+
+           if( !rc ) {
+               kbpos->resno = i;
+               kbpos->fp = NULL;
+               return 0;
+           }
+           else if( rc != -1 && rc != G10ERR_UNSUPPORTED ) {
+               log_error("error searching resource %d: %s\n",
+                                                 i, g10_errstr(rc));
+               last_rc = rc;
+           }
+       }
+    }
+
+    return (last_rc == -1 && !any)? G10ERR_UNSUPPORTED : last_rc;
+}
+
+
+int
+locate_keyblock_by_keyid( KBPOS *kbpos, u32 *keyid, int shortkid, int secret )
+{
+    RESTBL *rentry;
+    int i, rc, any=0, last_rc=-1;
+
+    if( shortkid )
+       return G10ERR_UNSUPPORTED;
+
+    for(i=0, rentry = resource_table; i < MAX_RESOURCES; i++, rentry++ ) {
+       if( rentry->used && !rentry->secret == !secret ) {
+           kbpos->rt = rentry->rt;
+           switch( rentry->rt ) {
+            #ifdef HAVE_LIBGDBM
+             case rt_GDBM:
+               any = 1;
+               rc = do_gdbm_locate_by_keyid( rentry->dbf, kbpos, keyid );
+               break;
+            #endif
+             default:
+               rc = G10ERR_UNSUPPORTED;
+               break;
+           }
+
+           if( !rc ) {
+               kbpos->resno = i;
+               kbpos->fp = NULL;
+               return 0;
+           }
+           else if( rc != -1 && rc != G10ERR_UNSUPPORTED ) {
+               log_error("error searching resource %d: %s\n",
+                                                 i, g10_errstr(rc));
+               last_rc = rc;
+           }
+       }
+    }
+
+    return (last_rc == -1 && !any)? G10ERR_UNSUPPORTED : last_rc;
+}
+
+
+
 
 /****************
  * Lock the keyblock; wait until it's available
@@ -492,9 +687,10 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
        kbpos->resno = i;
        rentry = check_pos( kbpos );
        kbpos->rt = resource_table[i].rt;
+       kbpos->valid = 0;
        switch( kbpos->rt ) {
          case rt_RING:
-           kbpos->fp = iobuf_fopen( rentry->fname, "rb" );
+           kbpos->fp = iobuf_open( rentry->fname );
            if( !kbpos->fp ) {
                log_error("can't open '%s'\n", rentry->fname );
                return G10ERR_OPEN_FILE;
@@ -502,7 +698,8 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
            break;
         #ifdef HAVE_LIBGDBM
          case rt_GDBM:
-           /* FIXME!!!! */
+           /* FIXME: make sure that there is only one enum at a time */
+           kbpos->offset = 0;
            break;
         #endif
          default: BUG();
@@ -521,7 +718,7 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
                break;
             #ifdef HAVE_LIBGDBM
              case rt_GDBM:
-               /* FIXME!!!! */
+               rc = do_gdbm_enum( kbpos, ret_root );
                break;
             #endif
              default: BUG();
@@ -548,12 +745,12 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
                kbpos->fp = NULL;
            }
            break;
-        #ifdef HAVE_LIBGDBM
          case rt_GDBM:
-           /* FIXME!!!! */
            break;
-        #endif
-         default: BUG();
+         default:
+           log_error("OOPS in close enum_keyblocks - ignored\n");
+           return rc;
+           break;
        }
        /* release pending packet */
        free_packet( kbpos->pkt );
@@ -583,7 +780,7 @@ insert_keyblock( KBPOS *kbpos, KBNODE root )
        break;
      #ifdef HAVE_LIBGDBM
       case rt_GDBM:
-       /* FIXME!!!! */
+       rc = do_gdbm_store( kbpos, root, 0 );
        break;
      #endif
       default: BUG();
@@ -612,7 +809,8 @@ delete_keyblock( KBPOS *kbpos )
        break;
      #ifdef HAVE_LIBGDBM
       case rt_GDBM:
-       /* FIXME!!!! */
+       log_debug("deleting gdbm keyblock is not yet implemented\n");
+       rc = 0;
        break;
      #endif
       default: BUG();
@@ -639,7 +837,7 @@ update_keyblock( KBPOS *kbpos, KBNODE root )
        break;
      #ifdef HAVE_LIBGDBM
       case rt_GDBM:
-       /* FIXME!!!! */
+       rc = do_gdbm_store( kbpos, root, 1 );
        break;
      #endif
       default: BUG();
@@ -651,6 +849,127 @@ update_keyblock( KBPOS *kbpos, KBNODE root )
 
 \f
 /****************************************************************
+ ********** Implemenation of a user ID database    **************
+ ****************************************************************/
+#if 0
+/****************
+ * Layout of the user ID db
+ *
+ * This user ID DB provides fast lookup of user ID, but the user ids are
+ * not in any specific order.
+ *
+ * A string "GnuPG user db", a \n.
+ * user ids of one key, delimited by \t,
+ * a # or ^ followed by a 20 byte fingerprint, followed by an \n
+ * The literal characters %, \n, \t, #, ^ must be replaced by a percent sign
+ * and their hex value.
+ *
+ * (We use Boyer/Moore pattern matching)
+ */
+
+/****************
+ * This compiles pattern to the distance table, the table will be allocate
+ * here and must be freed by using free().
+ * Returns: Ptr to new allocated Table
+ *         Caller must free the table.
+ */
+
+static size_t *
+compile_bm_table( const byte *pattern, size_t len )
+{
+    ushort *dist;
+    int i;
+
+    dist = m_alloc_clear( 256 * sizeof *dist );
+    for(i=0; i < 256; i++ )
+       dist[i] = len;
+    for(i=0; i < len-1; i++ )
+       dTbl[p[i]] = len-i-1;
+    return dist;
+}
+
+
+
+
+/****************
+ * Search BUF of BUFLEN for pattern P of length PATLEN.
+ * dist is the Boyer/Moore distance table of 256 Elements,
+ * case insensitive search is done if IGNCASE is true (In this case
+ * the distance table has to compiled from uppercase chacaters and
+ * PAT must also be uppercase.
+ * Returns: Prt to maching string in BUF, or NULL if not found.
+ */
+
+static const *
+do_bm_search( const byte *buf, size_t buflen,
+             const byte *pat, size_t patlen, size_t *dist, int igncase )
+{
+    int i, j, k;
+
+    if( igncase ) {
+       int c, c1;
+
+       for( i = --patlen; i < buflen; i += dist[c1] )
+           for( j=patlen, k=i, c1=c=toupper(buf[k]); c == pat[j];
+                                         j--, k--, c=toupper(buf[k]) ) {
+               if( !j )
+                   return buf+k;
+           }
+    }
+    else {
+       for( i = --patlen; i < buflen; i += dist[buf[i]] )
+           for( j=patlen, k=i; buf[k] == pat[j]; j--, k-- ) {
+               if( !j )
+                   return buf+k;
+           }
+    }
+    return NULL;
+}
+
+
+typedef struct {
+    size_t dist[256];
+} *SCAN_USER_HANDLE;
+
+static SCAN_USER_HANDLE
+scan_user_file_open( const byte *name )
+{
+    SCAN_USER_HANDLE hd;
+    size_t *dist;
+    int i;
+
+    hd = m_alloc_clear( sizeof *hd );
+    dist = hd->dist;
+    /* compile the distance table */
+    for(i=0; i < 256; i++ )
+       dist[i] = len;
+    for(i=0; i < len-1; i++ )
+       dTbl[p[i]] = len-i-1;
+    /* setup other things */
+
+    return hd;
+}
+
+static int
+scan_user_file_close( SCAN_USER_HANDLE hd )
+{
+    m_free( hd );
+}
+
+static int
+scan_user_file_read( SCAN_USER_HANDLE hd, byte *fpr )
+{
+    char record[1000];
+
+    /* read a record */
+
+
+}
+#endif
+
+
+\f
+/****************************************************************
  ********** Functions which operates on regular keyrings ********
  ****************************************************************/
 
@@ -701,8 +1020,9 @@ keyring_search( PACKET *req, KBPOS *kbpos, IOBUF iobuf, const char *fname )
     init_packet(&pkt);
     save_mode = set_packet_list_mode(0);
     kbpos->rt = rt_RING;
+    kbpos->valid = 0;
 
-  #if __MINGW32__
+  #if __MINGW32__  || 1
     assert(!iobuf);
     iobuf = iobuf_open( fname );
     if( !iobuf ) {
@@ -739,13 +1059,15 @@ keyring_search( PACKET *req, KBPOS *kbpos, IOBUF iobuf, const char *fname )
            BUG();
        free_packet(&pkt);
     }
-    if( !rc )
+    if( !rc ) {
        kbpos->offset = offset;
+       kbpos->valid = 1;
+    }
 
   leave:
     free_packet(&pkt);
     set_packet_list_mode(save_mode);
-  #if __MINGW32__
+  #if __MINGW32__ || 1
     iobuf_close(iobuf);
   #endif
     return rc;
@@ -765,12 +1087,14 @@ keyring_read( KBPOS *kbpos, KBNODE *ret_root )
     if( !(rentry=check_pos(kbpos)) )
        return G10ERR_GENERAL;
 
-    a = iobuf_fopen( rentry->fname, "rb" );
+    a = iobuf_open( rentry->fname );
     if( !a ) {
        log_error("can't open '%s'\n", rentry->fname );
        return G10ERR_OPEN_FILE;
     }
 
+    if( !kbpos->valid )
+       log_debug("kbpos not valid in keyring_read, want %d\n", (int)kbpos->offset );
     if( iobuf_seek( a, kbpos->offset ) ) {
        log_error("can't seek to %lu\n", kbpos->offset);
        iobuf_close(a);
@@ -794,6 +1118,12 @@ keyring_read( KBPOS *kbpos, KBNODE *ret_root )
        }
        /* make a linked list of all packets */
        switch( pkt->pkttype ) {
+         case PKT_COMPRESSED:
+           log_error("skipped compressed packet in keyring\n" );
+           free_packet(pkt);
+           init_packet(pkt);
+           break;
+
          case PKT_PUBLIC_KEY:
          case PKT_SECRET_KEY:
            if( in_cert )
@@ -811,6 +1141,7 @@ keyring_read( KBPOS *kbpos, KBNODE *ret_root )
        }
     }
   ready:
+    kbpos->valid = 0;
     if( rc == -1 && root )
        rc = 0;
 
@@ -856,6 +1187,12 @@ keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs )
        }
        /* make a linked list of all packets */
        switch( pkt->pkttype ) {
+         case PKT_COMPRESSED:
+           log_error("skipped compressed packet in keyring\n" );
+           free_packet(pkt);
+           init_packet(pkt);
+           break;
+
          case PKT_PUBLIC_KEY:
          case PKT_SECRET_KEY:
            if( root ) { /* store this packet */
@@ -873,8 +1210,6 @@ keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs )
             * a start packet; issue a warning if it is not a comment */
            if( !root && pkt->pkttype != PKT_COMMENT
                      && pkt->pkttype != PKT_OLD_COMMENT ) {
-               log_info("keyring_enum: skipped packet of type %d\n",
-                           pkt->pkttype );
                break;
            }
            if( !root || (skipsigs && ( pkt->pkttype == PKT_SIGNATURE
@@ -924,19 +1259,28 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
     if( kbpos->fp )
        BUG(); /* not allowed with such a handle */
 
+    if( !keyring_lock );
+       keyring_lock = make_dotlock( rentry->fname, -1 );
+    if( !keyring_lock )
+       log_fatal("can't lock '%s'\n", rentry->fname );
+
     /* open the source file */
-    fp = iobuf_fopen( rentry->fname, "rb" );
+    fp = iobuf_open( rentry->fname );
     if( mode == 1 && !fp && errno == ENOENT ) { /* no file yet */
        KBNODE kbctx, node;
 
        /* insert: create a new file */
        newfp = iobuf_create( rentry->fname );
        if( !newfp ) {
-           log_error("%s: can't create: %s\n", rentry->fname, strerror(errno));
+           log_error(_("%s: can't create: %s\n"), rentry->fname, strerror(errno));
+           if( !opt.lock_once ) {
+               release_dotlock( keyring_lock );
+               keyring_lock = NULL;
+           }
            return G10ERR_OPEN_FILE;
        }
        else
-           log_info("%s: keyring created\n", rentry->fname );
+           log_info(_("%s: keyring created\n"), rentry->fname );
 
        kbctx=NULL;
        while( (node = walk_kbnode( root, &kbctx, 0 )) ) {
@@ -944,16 +1288,28 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
                log_error("build_packet(%d) failed: %s\n",
                            node->pkt->pkttype, g10_errstr(rc) );
                iobuf_cancel(newfp);
+               if( !opt.lock_once ) {
+                   release_dotlock( keyring_lock );
+                   keyring_lock = NULL;
+               }
                return G10ERR_WRITE_FILE;
            }
        }
        if( iobuf_close(newfp) ) {
            log_error("%s: close failed: %s\n", rentry->fname, strerror(errno));
+           if( !opt.lock_once ) {
+               release_dotlock( keyring_lock );
+               keyring_lock = NULL;
+           }
            return G10ERR_CLOSE_FILE;
        }
        if( chmod( rentry->fname, S_IRUSR | S_IWUSR ) ) {
            log_error("%s: chmod failed: %s\n",
                                    rentry->fname, strerror(errno) );
+           if( !opt.lock_once ) {
+               release_dotlock( keyring_lock );
+               keyring_lock = NULL;
+           }
            return G10ERR_WRITE_FILE;
        }
        return 0;
@@ -1050,6 +1406,7 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
                goto leave;
            }
        }
+       kbpos->valid = 0;
     }
 
     if( mode == 2 || mode == 3 ) { /* delete or update */
@@ -1109,7 +1466,7 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
        rc = G10ERR_RENAME_FILE;
        if( rentry->secret ) {
            log_info(_(
-               "Warning: 2 files with confidential information exists.\n"));
+               "WARNING: 2 files with confidential information exists.\n"));
            log_info(_("%s is the unchanged one\n"), rentry->fname );
            log_info(_("%s is the new one\n"), tmpfname );
            log_info(_("Please fix this possible security flaw\n"));
@@ -1118,6 +1475,10 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
     }
 
   leave:
+    if( !opt.lock_once ) {
+       release_dotlock( keyring_lock );
+       keyring_lock = NULL;
+    }
     m_free(bakfname);
     m_free(tmpfname);
     return rc;
@@ -1129,58 +1490,182 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
  ********** Functions which operates on GDM files ***************
  ****************************************************************/
 
+#if MAX_FINGERPRINT_LEN > 20
+  #error A GDBM keyring assumes that fingerprints are less than 21
+#endif
+
 /****************
- * search one keybox, return 0 if found, -1 if not found or an errorcode.
+ * Insert the keyblock into the GDBM database
  */
+
 static int
-do_gdbm_search( PACKET *req, KBPOS *kbpos, GDBM_FILE dbf, const char *fname )
+do_gdbm_store( KBPOS *kbpos, KBNODE root, int update )
 {
-    int rc;
-    PACKET pkt;
-    int save_mode;
-    ulong offset;
-    int pkttype = req->pkttype;
-    PKT_public_key *req_pk = req->pkt.public_key;
-    PKT_secret_key *req_sk = req->pkt.secret_key;
-
-    init_packet(&pkt);
-    save_mode = set_packet_list_mode(0);
-
+    RESTBL *rentry;
+    PKT_public_key *pk;
+    KBNODE kbctx, node;
+    IOBUF fp = NULL;
+    byte fpr[20];
+    byte contbuf[21];
+    byte keybuf[21];
+    size_t fprlen;
+    datum key, content;
+    int i, rc;
 
-    while( !(rc=search_packet(iobuf, &pkt, pkttype, &offset)) ) {
-       if( pkt.pkttype == PKT_SECRET_KEY ) {
-           PKT_secret_key *sk = pkt.pkt.secret_key;
+    if( !(rentry = check_pos( kbpos )) )
+       return G10ERR_GENERAL;
 
-           if(   req_sk->timestamp == sk->timestamp
-              && req_sk->pubkey_algo == sk->pubkey_algo
-              && !cmp_seckey( req_sk, sk) )
-               break; /* found */
+    /* construct the fingerprint which is used as the primary key */
+    node = find_kbnode( root, PKT_PUBLIC_KEY );
+    if( !node )
+       log_bug("a gdbm database can't store secret keys\n");
+    pk = node->pkt->pkt.public_key;
+
+    fingerprint_from_pk( pk, fpr, &fprlen );
+    for(i=fprlen; i < DIM(fpr); i++ )
+       fpr[i] = 0;
+
+    /* build the keyblock */
+    kbctx=NULL;
+    fp = iobuf_temp();
+    iobuf_put( fp, 1 ); /* data is a keyblock */
+    while( (node = walk_kbnode( root, &kbctx, 0 )) ) {
+       if( (rc = build_packet( fp, node->pkt )) ) {
+           log_error("build_packet(%d) failed: %s\n",
+                       node->pkt->pkttype, g10_errstr(rc) );
+           rc = G10ERR_WRITE_FILE;
+           goto leave;
        }
-       else if( pkt.pkttype == PKT_PUBLIC_KEY ) {
-           PKT_public_key *pk = pkt.pkt.public_key;
+    }
+    /* store data and key */
+    *keybuf = 1;   /* key is a padded fingerprint */
+    memcpy(keybuf+1, fpr, 20 );
+    key.dptr  = keybuf;
+    key.dsize = 21;
+    content.dptr  = iobuf_get_temp_buffer( fp );
+    content.dsize = iobuf_get_temp_length( fp );
+    rc = gdbm_store( rentry->dbf, key, content,
+                                 update? GDBM_REPLACE : GDBM_INSERT );
+    if( rc == 1 && !update )
+       rc = gdbm_store( rentry->dbf, key, content, GDBM_REPLACE );
 
-           if(   req_pk->timestamp == pk->timestamp
-              && req_pk->pubkey_algo == pk->pubkey_algo
-              && !cmp_pubkey( req_pk, pk ) )
-               break; /* found */
+    if( rc ) {
+       log_error("%s: gdbm_store failed: %s\n", rentry->fname,
+                           rc == 1 ? "already stored"
+                                   : gdbm_strerror(gdbm_errno) );
+       rc = G10ERR_WRITE_FILE;
+       goto leave;
+    }
+    /* now store all keyids */
+    *contbuf = 2;  /* data is a list of fingerprints */
+    memcpy(contbuf+1, fpr, 20 );
+    content.dptr = contbuf;
+    content.dsize= 21;
+    kbctx=NULL;
+    while( (node = walk_kbnode( root, &kbctx, 0 )) ) {
+       if(    node->pkt->pkttype == PKT_PUBLIC_KEY
+           || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+           u32 aki[2];
+
+           keyid_from_pk( node->pkt->pkt.public_key, aki );
+           *keybuf = 2; /* key is a 8 byte keyid */
+           u32tobuf( keybuf+1  , aki[0] );
+           u32tobuf( keybuf+5, aki[1] );
+           key.dptr = keybuf;
+           key.dsize= 9;
+           /* fixme: must be more clever when a insert failed:
+            *        build a list of fingerprints in this case */
+           rc = gdbm_store( rentry->dbf, key, content,
+                                         update? GDBM_REPLACE : GDBM_INSERT );
+           if( rc ) {
+               log_info("%s: gdbm_store keyid failed: %s\n", rentry->fname,
+                                   rc == 1 ? "already stored"
+                                           : gdbm_strerror(gdbm_errno) );
+               rc = 0;
+           }
        }
-       else
-           BUG();
-       free_packet(&pkt);
     }
-    if( !rc )
-       kbpos->offset = offset;
 
   leave:
-    free_packet(&pkt);
-    set_packet_list_mode(save_mode);
-  #if __MINGW32__
-    iobuf_close(iobuf);
-  #endif
+    iobuf_close(fp); /* don't need a cancel because it is a temp iobuf */
     return rc;
 }
 
 
+
+/****************
+ * search one keybox, return 0 if found, -1 if not found or an errorcode.
+ */
+static int
+do_gdbm_locate( GDBM_FILE dbf, KBPOS *kbpos, const byte *fpr, int fprlen )
+{
+    byte *keybuf = kbpos->keybuf;
+    datum key;
+    int i;
+
+    *keybuf = 1;
+    for(i=0; i < fprlen; i++ )
+       keybuf[i+1] = fpr[i];
+    for(; i < 20; i++ )
+       keybuf[i+1] = 0;
+
+    /* fetch the data */
+    key.dptr  = keybuf;
+    key.dsize = 21;
+    if( !gdbm_exists( dbf, key ) )
+       return -1; /* not found */
+    return 0;
+}
+
+/****************
+ * locate by keyid.
+ * FIXME: we must have a way to enumerate thru the list opf fingerprints
+ */
+static int
+do_gdbm_locate_by_keyid( GDBM_FILE dbf, KBPOS *kbpos, u32 *keyid )
+{
+    byte keybuf[9];
+    datum key, content;
+    int rc;
+
+    /* construct the fingerprint which is used as the primary key */
+    *keybuf = 2;
+    u32tobuf( keybuf+1, keyid[0] );
+    u32tobuf( keybuf+5, keyid[1] );
+
+    /* fetch the data */
+    key.dptr  = keybuf;
+    key.dsize = 9;
+    content = gdbm_fetch( dbf, key );
+    if( !content.dptr )
+       return -1;
+
+    if( content.dsize < 2 ) {
+       log_error("gdbm_fetch did not return enough data\n" );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( *content.dptr != 2 ) {
+       log_error("gdbm_fetch returned unexpected type %d\n",
+                   *(byte*)content.dptr );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( content.dsize < 21 ) {
+       log_error("gdbm_fetch did not return a complete fingerprint\n" );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( content.dsize > 21 )
+       log_info("gdbm_fetch: WARNING: more than one fingerprint\n" );
+
+    rc = do_gdbm_locate( dbf, kbpos, content.dptr+1, 20 );
+    free( content.dptr ); /* can't use m_free() here */
+    return rc;
+}
+
+
+
 static int
 do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root )
 {
@@ -1189,23 +1674,33 @@ do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root )
     RESTBL *rentry;
     KBNODE root = NULL;
     IOBUF a;
-    int in_cert = 0;
+    datum key, content;
 
     if( !(rentry=check_pos(kbpos)) )
        return G10ERR_GENERAL;
 
-    a = iobuf_fopen( rentry->fname, "rb" );
-    if( !a ) {
-       log_error("can't open '%s'\n", rentry->fname );
-       return G10ERR_OPEN_FILE;
+    key.dptr  = kbpos->keybuf;
+    key.dsize = 21;
+    content = gdbm_fetch( rentry->dbf, key );
+    if( !content.dptr ) {
+       log_error("gdbm_fetch failed: %s\n", gdbm_strerror(gdbm_errno) );
+       return G10ERR_INV_KEYRING;
     }
-
-    if( iobuf_seek( a, kbpos->offset ) ) {
-       log_error("can't seek to %lu\n", kbpos->offset);
-       iobuf_close(a);
-       return G10ERR_KEYRING_OPEN;
+    if( content.dsize < 2 ) {
+       log_error("gdbm_fetch did not return enough data\n" );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( *content.dptr != 1 ) {
+       log_error("gdbm_fetch returned unexpected type %d\n",
+                   *(byte*)content.dptr );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
     }
 
+    a = iobuf_temp_with_content( content.dptr+1, content.dsize-1 );
+    free( content.dptr ); /* can't use m_free() here */
+
     pkt = m_alloc( sizeof *pkt );
     init_packet(pkt);
     kbpos->count=0;
@@ -1214,7 +1709,7 @@ do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root )
            if( rc != G10ERR_UNKNOWN_PACKET ) {
                log_error("read_keyblock: read error: %s\n", g10_errstr(rc) );
                rc = G10ERR_INV_KEYRING;
-               goto ready;
+               break;
            }
            kbpos->count++;
            free_packet( pkt );
@@ -1222,27 +1717,16 @@ do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root )
            continue;
        }
        /* make a linked list of all packets */
-       switch( pkt->pkttype ) {
-         case PKT_PUBLIC_KEY:
-         case PKT_SECRET_KEY:
-           if( in_cert )
-               goto ready;
-           in_cert = 1;
-         default:
-           kbpos->count++;
-           if( !root )
-               root = new_kbnode( pkt );
-           else
-               add_kbnode( root, new_kbnode( pkt ) );
-           pkt = m_alloc( sizeof *pkt );
-           init_packet(pkt);
-           break;
-       }
+       kbpos->count++;
+       if( !root )
+           root = new_kbnode( pkt );
+       else
+           add_kbnode( root, new_kbnode( pkt ) );
+       pkt = m_alloc( sizeof *pkt );
+       init_packet(pkt);
     }
-  ready:
     if( rc == -1 && root )
        rc = 0;
-
     if( rc )
        release_kbnode( root );
     else
@@ -1254,84 +1738,43 @@ do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root )
 }
 
 
+/****************
+ * Enum over keyblok data
+ */
 static int
-do_gdbm_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs )
+do_gdbm_enum( KBPOS *kbpos, KBNODE *ret_root )
 {
-    PACKET *pkt;
-    int rc;
     RESTBL *rentry;
-    KBNODE root = NULL;
+    datum key, helpkey;
 
     if( !(rentry=check_pos(kbpos)) )
        return G10ERR_GENERAL;
 
-    if( kbpos->pkt ) {
-       root = new_kbnode( kbpos->pkt );
-       kbpos->pkt = NULL;
+    if( !kbpos->offset ) {
+       kbpos->offset = 1;
+       key = gdbm_firstkey( rentry->dbf );
     }
-
-    pkt = m_alloc( sizeof *pkt );
-    init_packet(pkt);
-    while( (rc=parse_packet(kbpos->fp, pkt)) != -1 ) {
-       if( rc ) {  /* ignore errors */
-           if( rc != G10ERR_UNKNOWN_PACKET ) {
-               log_error("read_keyblock: read error: %s\n", g10_errstr(rc) );
-               rc = G10ERR_INV_KEYRING;
-               goto ready;
-           }
-           free_packet( pkt );
-           init_packet( pkt );
-           continue;
-       }
-       /* make a linked list of all packets */
-       switch( pkt->pkttype ) {
-         case PKT_PUBLIC_KEY:
-         case PKT_SECRET_KEY:
-           if( root ) { /* store this packet */
-               kbpos->pkt = pkt;
-               pkt = NULL;
-               goto ready;
-           }
-           root = new_kbnode( pkt );
-           pkt = m_alloc( sizeof *pkt );
-           init_packet(pkt);
-           break;
-
-         default:
-           /* skip pakets at the beginning of a keyring, until we find
-            * a start packet; issue a warning if it is not a comment */
-           if( !root && pkt->pkttype != PKT_COMMENT
-                     && pkt->pkttype != PKT_OLD_COMMENT ) {
-               log_info("keyring_enum: skipped packet of type %d\n",
-                           pkt->pkttype );
-               break;
-           }
-           if( !root || (skipsigs && ( pkt->pkttype == PKT_SIGNATURE
-                                     ||pkt->pkttype == PKT_COMMENT
-                                     ||pkt->pkttype == PKT_OLD_COMMENT )) ) {
-               init_packet(pkt);
-               break;
-           }
-           add_kbnode( root, new_kbnode( pkt ) );
-           pkt = m_alloc( sizeof *pkt );
-           init_packet(pkt);
-           break;
-       }
+    else {
+       helpkey.dptr = kbpos->keybuf;
+       helpkey.dsize= 21;
+       key = gdbm_nextkey( rentry->dbf, helpkey );
     }
-  ready:
-    if( rc == -1 && root )
-       rc = 0;
-
-    if( rc )
-       release_kbnode( root );
-    else
-       *ret_root = root;
-    free_packet( pkt );
-    m_free( pkt );
+    while( key.dptr && (!key.dsize || *key.dptr != 1) ) {
+       helpkey = key;
+       key = gdbm_nextkey( rentry->dbf, helpkey );
+       free( helpkey.dptr ); /* free and not m_free() ! */
+    }
+    if( !key.dptr )
+       return -1; /* eof */
 
-    return rc;
+    if( key.dsize < 21 ) {
+       free( key.dptr ); /* free and not m_free() ! */
+       log_error("do_gdm_enum: key is too short\n" );
+       return G10ERR_INV_KEYRING;
+    }
+    memcpy( kbpos->keybuf, key.dptr, 21 );
+    free( key.dptr ); /* free and not m_free() ! */
+    return do_gdbm_read( kbpos, ret_root );
 }
 
 #endif /*HAVE_LIBGDBM*/
-
-