See ChangeLog: Thu Jan 21 06:22:10 CET 1999 Werner Koch
[gnupg.git] / g10 / trustdb.c
index 4cdb7e8..674240a 100644 (file)
@@ -1,14 +1,14 @@
 /* trustdb.c
  *     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.
   #error Must change structure of trustdb
 #endif
 
+struct keyid_list {
+    struct keyid_list *next;
+    u32 keyid[2];
+};
+
 struct local_id_item {
     struct local_id_item *next;
     ulong lid;
@@ -76,6 +81,13 @@ struct trust_seg_list {
 };
 
 
+struct enum_cert_paths_ctx {
+   int init;
+   TRUST_SEG_LIST tsl_head;
+   TRUST_SEG_LIST tsl;
+   int idx;
+};
+
 
 struct recno_list_struct {
     struct recno_list_struct *next;
@@ -93,6 +105,7 @@ static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag );
 static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag );
 
 static void print_user_id( const char *text, u32 *keyid );
+static void sort_tsl_list( TRUST_SEG_LIST *trust_seg_list );
 static int list_sigs( ulong pubkey_id );
 static int do_check( TRUSTREC *drec, unsigned *trustlevel );
 static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec );
@@ -102,9 +115,10 @@ static void upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid,
                 TRUSTREC *drec, RECNO_LIST *recno_list, int recheck,
                 TRUSTREC *urec, const byte *uidhash, int revoke );
 
+static struct keyid_list *trusted_key_list;
 
 /* a table used to keep track of ultimately trusted keys
- * which are the ones from our secrings */
+ * which are the ones from our secrings and the trusted keys */
 static LOCAL_ID_TABLE ultikey_table;
 
 /* list of unused lid items and tables */
@@ -125,7 +139,7 @@ static void
 die_invalid_db()
 {
     log_error(_(
-       "The trust DB is corrupted; please run \"gpgm --fix-trust-db\".\n") );
+       "The trustdb is corrupted; please run \"gpgm --fix-trustdb\".\n") );
     g10_exit(2);
 }
 
@@ -181,7 +195,7 @@ do_sync( )
     int rc = tdbio_sync();
     if( !rc )
        return;
-    log_error(_("trust db: sync failed: %s\n"), g10_errstr(rc) );
+    log_error(_("trustdb: sync failed: %s\n"), g10_errstr(rc) );
     g10_exit(2);
 }
 
@@ -423,9 +437,45 @@ walk_sigrecs( SIGREC_CONTEXT *c )
  ************* Trust  stuff  ******************
  ***********************************************/
 
+static int
+trust_letter( unsigned value )
+{
+    switch( value ) {
+      case TRUST_UNKNOWN:   return '-';
+      case TRUST_EXPIRED:   return 'e';
+      case TRUST_UNDEFINED: return 'q';
+      case TRUST_NEVER:     return 'n';
+      case TRUST_MARGINAL:  return 'm';
+      case TRUST_FULLY:     return 'f';
+      case TRUST_ULTIMATE:  return 'u';
+      default:             return  0 ;
+    }
+}
+
+
+void
+register_trusted_key( const char *string )
+{
+    u32 keyid[2];
+    struct keyid_list *r;
+
+    if( classify_user_id( string, keyid, NULL, NULL, NULL ) != 11 ) {
+       log_error(_("'%s' is not a valid long keyID\n"), string );
+       return;
+    }
+
+    for( r = trusted_key_list; r; r = r->next )
+       if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] )
+           return;
+    r = m_alloc( sizeof *r );
+    r->keyid[0] = keyid[0];
+    r->keyid[1] = keyid[1];
+    r->next = trusted_key_list;
+    trusted_key_list = r;
+}
 
 /****************
- * Verify that all our public keys are in the trustDB.
+ * Verify that all our public keys are in the trustdb.
  */
 static int
 verify_own_keys()
@@ -435,8 +485,48 @@ verify_own_keys()
     PKT_secret_key *sk = m_alloc_clear( sizeof *sk );
     PKT_public_key *pk = m_alloc_clear( sizeof *pk );
     u32 keyid[2];
+    struct keyid_list *kl;
+
+    /* put the trusted keys into the trusted key table */
+    for( kl = trusted_key_list; kl; kl = kl->next ) {
+       keyid[0] = kl->keyid[0];
+       keyid[1] = kl->keyid[1];
+       /* get the public key */
+       memset( pk, 0, sizeof *pk );
+       rc = get_pubkey( pk, keyid );
+       if( rc ) {
+           log_info(_("key %08lX: no public key for trusted key - skipped\n"),
+                                                           (ulong)keyid[1] );
+       }
+       else {
+           /* make sure that the pubkey is in the trustdb */
+           rc = query_trust_record( pk );
+           if( rc == -1 ) { /* put it into the trustdb */
+               rc = insert_trust_record( pk );
+               if( rc ) {
+                   log_error(_("key %08lX: can't put it into the trustdb\n"),
+                                                       (ulong)keyid[1] );
+               }
+           }
+           else if( rc ) {
+               log_error(_("key %08lX: query record failed\n"),
+                                                       (ulong)keyid[1] );
+           }
+           else {
+               if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) )
+                   log_error(_("key %08lX: already in trusted key table\n"),
+                                                         (ulong)keyid[1]);
+               else if( opt.verbose > 1 )
+                   log_info(_("key %08lX: accepted as trusted key.\n"),
+                                                         (ulong)keyid[1]);
+           }
+       }
+       release_public_key_parts( pk );
+    }
 
     while( !(rc=enum_secret_keys( &enum_context, sk, 0 ) ) ) {
+       int have_pk = 0;
+
        keyid_from_sk( sk, keyid );
 
        if( DBG_TRUST )
@@ -446,6 +536,11 @@ verify_own_keys()
            log_info(_("NOTE: secret key %08lX is NOT protected.\n"),
                                                            (ulong)keyid[1] );
 
+       for( kl = trusted_key_list; kl; kl = kl->next ) {
+           if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] )
+               goto skip; /* already in trusted key table */
+       }
+
        /* see whether we can access the public key of this secret key */
        memset( pk, 0, sizeof *pk );
        rc = get_pubkey( pk, keyid );
@@ -454,6 +549,7 @@ verify_own_keys()
                                                            (ulong)keyid[1] );
            goto skip;
        }
+       have_pk=1;
 
        if( cmp_public_secret_key( pk, sk ) ) {
            log_info(_("key %08lX: secret and public key don't match\n"),
@@ -481,20 +577,30 @@ verify_own_keys()
            log_debug("key %08lX.%lu: stored into ultikey_table\n",
                                    (ulong)keyid[1], pk->local_id );
        if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) )
-           log_error(_("key %08lX: already in secret key table\n"),
+           log_error(_("key %08lX: already in trusted key table\n"),
                                                        (ulong)keyid[1]);
        else if( opt.verbose > 1 )
-           log_info(_("key %08lX: accepted as secret key.\n"),
+           log_info(_("key %08lX: accepted as trusted key.\n"),
                                                        (ulong)keyid[1]);
       skip:
        release_secret_key_parts( sk );
-       release_public_key_parts( pk );
+       if( have_pk )
+           release_public_key_parts( pk );
     }
     if( rc != -1 )
        log_error(_("enumerate secret keys failed: %s\n"), g10_errstr(rc) );
     else
        rc = 0;
 
+    /* release the trusted keyid table */
+    {  struct keyid_list *kl2;
+       for( kl = trusted_key_list; kl; kl = kl2 ) {
+           kl2 = kl->next;
+           m_free( kl );
+       }
+       trusted_key_list = NULL;
+    }
+
     enum_secret_keys( &enum_context, NULL, 0 ); /* free context */
     free_secret_key( sk );
     free_public_key( pk );
@@ -737,22 +843,23 @@ collect_paths( int depth, int max_depth, int all, TRUSTREC *drec,
 
     if( depth >= max_depth )  /* max cert_depth reached */
        return TRUST_UNDEFINED;
+
+    stack[depth].lid = drec->r.dir.lid;
+    stack[depth].otrust = drec->r.dir.ownertrust;
+    stack[depth].trust = 0;
     {  int i;
 
        for(i=0; i < depth; i++ )
            if( stack[i].lid == drec->r.dir.lid )
                return TRUST_UNDEFINED; /* closed (we already visited this lid) */
     }
-
-    stack[depth].lid = drec->r.dir.lid;
-    stack[depth].otrust = drec->r.dir.ownertrust;
-    stack[depth].trust = 0;
     if( !qry_lid_table_flag( ultikey_table, drec->r.dir.lid, NULL ) ) {
        /* we are at the end of a path */
        TRUST_SEG_LIST tsl;
        int i;
 
        stack[depth].trust = TRUST_ULTIMATE;
+       stack[depth].otrust = TRUST_ULTIMATE;
        if( trust_seg_head ) {
            /* we can now put copy our current stack to the trust_seg_list */
            tsl = m_alloc( sizeof *tsl + (depth+1)*sizeof( TRUST_INFO ) );
@@ -791,17 +898,23 @@ collect_paths( int depth, int max_depth, int all, TRUSTREC *drec,
            for(i=0; i < SIGS_PER_RECORD; i++ ) {
                TRUSTREC tmp;
                int ot, nt;
+               int unchecked = 0;
 
                if( !rec.r.sig.sig[i].lid )
                    continue; /* skip deleted sigs */
-               if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) )
-                   continue; /* skip unchecked signatures */
-               if( !(rec.r.sig.sig[i].flag & SIGF_VALID) )
-                   continue; /* skip invalid signatures */
-               if( (rec.r.sig.sig[i].flag & SIGF_EXPIRED) )
-                   continue; /* skip expired signatures */
-               if( (rec.r.sig.sig[i].flag & SIGF_REVOKED) )
-                   continue; /* skip revoked signatures */
+               if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) ) {
+                   if( !all )
+                       continue; /* skip unchecked signatures */
+                   unchecked = 1;
+               }
+               else {
+                   if( !(rec.r.sig.sig[i].flag & SIGF_VALID) )
+                       continue; /* skip invalid signatures */
+                   if( (rec.r.sig.sig[i].flag & SIGF_EXPIRED) )
+                       continue; /* skip expired signatures */
+                   if( (rec.r.sig.sig[i].flag & SIGF_REVOKED) )
+                       continue; /* skip revoked signatures */
+               }
 
                /* visit every signer only once (a signer may have
                 * signed more than one user ID) */
@@ -824,7 +937,7 @@ collect_paths( int depth, int max_depth, int all, TRUSTREC *drec,
                                                        trust_seg_head );
                nt &= TRUST_MASK;
 
-               if( nt < TRUST_MARGINAL ) {
+               if( nt < TRUST_MARGINAL || unchecked ) {
                    continue;
                }
 
@@ -838,6 +951,9 @@ collect_paths( int depth, int max_depth, int all, TRUSTREC *drec,
                    }
                }
 
+               if( nt > ot )
+                   nt = ot;
+
                if( nt >= TRUST_FULLY )
                    fully++;
                if( nt >= TRUST_MARGINAL )
@@ -1073,7 +1189,7 @@ import_ownertrust( const char *fname )
            continue;
        n = strlen(line);
        if( line[n-1] != '\n' ) {
-           log_error_f(fname, _("line to long\n") );
+           log_error_f(fname, _("line too long\n") );
            /* ... or last line does not have a LF */
            break; /* can't continue */
        }
@@ -1104,10 +1220,10 @@ import_ownertrust( const char *fname )
        rc = tdbio_search_dir_byfpr( line, fprlen, 0, &rec );
        if( !rc ) { /* found: update */
            if( rec.r.dir.ownertrust )
-               log_info(_("LID %lu: changing trust from %u to %u\n"),
+               log_info("LID %lu: changing trust from %u to %u\n",
                          rec.r.dir.lid, rec.r.dir.ownertrust, otrust );
            else
-               log_info(_("LID %lu: setting trust to %u\n"),
+               log_info("LID %lu: setting trust to %u\n",
                                   rec.r.dir.lid, otrust );
            rec.r.dir.ownertrust = otrust;
            write_record( &rec );
@@ -1147,42 +1263,85 @@ import_ownertrust( const char *fname )
 
 
 static void
-print_path( int pathlen, TRUST_INFO *path )
+print_path( int pathlen, TRUST_INFO *path, FILE *fp, ulong highlight )
 {
-    int rc, i;
+    int rc, c, i;
     u32 keyid[2];
+    char *p;
+    size_t n;
 
-    fputs("path:", stdout);
     for( i = 0; i < pathlen; i++ )  {
-       if( i && !(i%4) )
-           fputs("     ", stdout );
+       if( highlight )
+           fputs(highlight == path[i].lid? "* ":"  ", fp );
        rc = keyid_from_lid( path[i].lid, keyid );
        if( rc )
-           printf(" ????????.%lu:", path[i].lid );
+           fprintf(fp, "????????.%lu:", path[i].lid );
+       else
+           fprintf(fp,"%08lX.%lu:", (ulong)keyid[1], path[i].lid );
+       c = trust_letter(path[i].otrust);
+       if( c )
+           putc( c, fp );
+       else
+           fprintf( fp, "%02x", path[i].otrust );
+       putc('/', fp);
+       c = trust_letter(path[i].trust);
+       if( c )
+           putc( c, fp );
        else
-           printf(" %08lX.%lu:", (ulong)keyid[1], path[i].lid );
-       print_sigflags( stdout, path[i].otrust );
+           fprintf( fp, "%02x", path[i].trust );
+       putc(' ', fp);
+       p = get_user_id( keyid, &n );
+       putc(' ', fp);
+       putc('\"', fp);
+       print_string( fp, p, n > 40? 40:n, 0 );
+       putc('\"', fp);
+       m_free(p);
+       putc('\n', fp );
     }
-    putchar('\n');
 }
 
 
+static int
+cmp_tsl_array( const void *xa, const void *xb )
+{
+    TRUST_SEG_LIST a = *(TRUST_SEG_LIST*)xa;
+    TRUST_SEG_LIST b = *(TRUST_SEG_LIST*)xb;
+    return a->pathlen - b->pathlen;
+}
+
+
+static void
+sort_tsl_list( TRUST_SEG_LIST *trust_seg_list )
+{
+    TRUST_SEG_LIST *array, *tail, tsl;
+    size_t n;
+
+    for(n=0, tsl = *trust_seg_list; tsl; tsl = tsl->next )
+       n++;
+    array = m_alloc( (n+1) * sizeof *array );
+    for(n=0, tsl = *trust_seg_list; tsl; tsl = tsl->next )
+       array[n++] = tsl;
+    array[n] = NULL;
+    qsort( array, n, sizeof *array, cmp_tsl_array );
+    *trust_seg_list = NULL;
+    tail = trust_seg_list;
+    for(n=0; (tsl=array[n]); n++ ) {
+       *tail = tsl;
+       tail = &tsl->next;
+    }
+    m_free( array );
+}
+
 
 void
-list_trust_path( int max_depth, const char *username )
+list_trust_path( const char *username )
 {
     int rc;
-    int wipe=0;
     TRUSTREC rec;
     TRUST_INFO *tmppath;
     TRUST_SEG_LIST trust_seg_list, tsl, tsl2;
     PKT_public_key *pk = m_alloc_clear( sizeof *pk );
 
-    if( max_depth < 0 ) {
-       wipe = 1;
-       max_depth = -max_depth;
-    }
-
     if( (rc = get_pubkey_byname(NULL, pk, username, NULL )) )
        log_error(_("user '%s' not found: %s\n"), username, g10_errstr(rc) );
     else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 )
@@ -1201,13 +1360,16 @@ list_trust_path( int max_depth, const char *username )
     free_public_key( pk );
 
     /* collect the paths */
-    tmppath = m_alloc_clear( (max_depth+1)* sizeof *tmppath );
+    tmppath = m_alloc_clear( (opt.max_cert_depth+1)* sizeof *tmppath );
     trust_seg_list = NULL;
-    collect_paths( 0, max_depth, 1, &rec, tmppath, &trust_seg_list );
+    collect_paths( 0, opt.max_cert_depth, 1, &rec, tmppath, &trust_seg_list );
     m_free( tmppath );
+    sort_tsl_list( &trust_seg_list );
     /* and now print them */
     for(tsl = trust_seg_list; tsl; tsl = tsl->next ) {
-       print_path( tsl->pathlen, tsl->path );
+       print_path( tsl->pathlen, tsl->path, stdout, 0 );
+       if( tsl->next )
+           putchar('\n');
     }
 
     /* release the list */
@@ -1221,7 +1383,9 @@ list_trust_path( int max_depth, const char *username )
 
 /****************
  * Check the complete trustdb or only the entries for the given username.
- * We check the complete database and recalculate all flags.
+ * We check the complete database. If a username is given or the special
+ * username "*" is used, a complete recheck is done.  With no user ID
+ * only the records which are not yet checkd are now checked.
  */
 void
 check_trustdb( const char *username )
@@ -1230,8 +1394,9 @@ check_trustdb( const char *username )
     KBNODE keyblock = NULL;
     KBPOS kbpos;
     int rc;
+    int recheck = username && *username == '*' && !username[1];
 
-    if( username ) {
+    if( username && !recheck ) {
        rc = find_keyblock_byname( &kbpos, username );
        if( !rc )
            rc = read_keyblock( &kbpos, &keyblock );
@@ -1242,7 +1407,7 @@ check_trustdb( const char *username )
        else {
            int modified;
 
-           rc = update_trust_record( keyblock, 0, &modified );
+           rc = update_trust_record( keyblock, 1, &modified );
            if( rc == -1 ) { /* not yet in trustdb: insert */
                rc = insert_trust_record(
                            find_kbnode( keyblock, PKT_PUBLIC_KEY
@@ -1290,7 +1455,7 @@ check_trustdb( const char *username )
                    continue;
                }
 
-               rc = update_trust_record( keyblock, 0, &modified );
+               rc = update_trust_record( keyblock, recheck, &modified );
                if( rc ) {
                    log_error(_("lid %lu: update failed: %s\n"),
                                                 recnum, g10_errstr(rc) );
@@ -1480,6 +1645,8 @@ check_trust( PKT_public_key *pk, unsigned *r_trustlevel )
 }
 
 
+
+
 int
 query_trust_info( PKT_public_key *pk )
 {
@@ -1490,16 +1657,9 @@ query_trust_info( PKT_public_key *pk )
        return '?';
     if( trustlevel & TRUST_FLAG_REVOKED )
        return 'r';
-    switch( (trustlevel & TRUST_MASK) ) {
-      case TRUST_UNKNOWN:   c = 'o'; break;
-      case TRUST_EXPIRED:   c = 'e'; break;
-      case TRUST_UNDEFINED: c = 'q'; break;
-      case TRUST_NEVER:     c = 'n'; break;
-      case TRUST_MARGINAL:  c = 'm'; break;
-      case TRUST_FULLY:     c = 'f'; break;
-      case TRUST_ULTIMATE:  c = 'u'; break;
-      default: BUG();
-    }
+    c = trust_letter( (trustlevel & TRUST_MASK) );
+    if( !c )
+       c = '?';
     return c;
 }
 
@@ -1527,12 +1687,7 @@ int
 enum_cert_paths( void **context, ulong *lid,
                 unsigned *ownertrust, unsigned *validity )
 {
-    struct {
-       int init;
-       TRUST_SEG_LIST tsl_head;
-       TRUST_SEG_LIST tsl;
-       int idx;
-    } *ctx;
+    struct enum_cert_paths_ctx *ctx;
     TRUST_SEG_LIST tsl;
 
     if( !lid ) {  /* release the context */
@@ -1553,7 +1708,7 @@ enum_cert_paths( void **context, ulong *lid,
        TRUST_INFO *tmppath;
        TRUSTREC rec;
 
-       if( !lid )
+       if( !*lid )
            return -1;
 
        ctx = m_alloc_clear( sizeof *ctx );
@@ -1564,7 +1719,8 @@ enum_cert_paths( void **context, ulong *lid,
        tsl = NULL;
        collect_paths( 0, opt.max_cert_depth, 1, &rec, tmppath, &tsl );
        m_free( tmppath );
-       /* and now print them */
+       sort_tsl_list( &tsl );
+       /* setup the context */
        ctx->tsl_head = tsl;
        ctx->tsl = ctx->tsl_head;
        ctx->idx = 0;
@@ -1572,7 +1728,7 @@ enum_cert_paths( void **context, ulong *lid,
     else
        ctx = *context;
 
-    while( ctx->tsl && ctx->idx >= tsl->pathlen )  {
+    while( ctx->tsl && ctx->idx >= ctx->tsl->pathlen ) {
        ctx->tsl = ctx->tsl->next;
        ctx->idx = 0;
     }
@@ -1591,6 +1747,50 @@ enum_cert_paths( void **context, ulong *lid,
 
 
 /****************
+ * Print the current path
+ */
+void
+enum_cert_paths_print( void **context, FILE *fp,
+                                      int refresh, ulong selected_lid )
+{
+    struct enum_cert_paths_ctx *ctx;
+    TRUST_SEG_LIST tsl;
+
+    if( !*context )
+       return;
+    ctx = *context;
+    if( !ctx->tsl )
+       return;
+    tsl = ctx->tsl;
+
+    if( !fp )
+       fp = stderr;
+
+    if( refresh ) { /* update the ownertrust and if possible the validity */
+       int i;
+       int match = tdbio_db_matches_options();
+
+       for( i = 0; i < tsl->pathlen; i++ )  {
+           TRUSTREC rec;
+
+           read_record( tsl->path[i].lid, &rec, RECTYPE_DIR );
+           tsl->path[i].otrust = rec.r.dir.ownertrust;
+           /* update validity only if we have it in the cache
+            * calculation is too time consuming */
+           if( match && (rec.r.dir.dirflags & DIRF_VALVALID)
+                     && rec.r.dir.validity ) {
+               tsl->path[i].trust = rec.r.dir.validity;
+               if( rec.r.dir.dirflags & DIRF_REVOKED )
+                   tsl->path[i].trust = TRUST_FLAG_REVOKED;
+           }
+       }
+    }
+
+    print_path( tsl->pathlen, tsl->path, fp, selected_lid );
+}
+
+
+/****************
  * Return the assigned ownertrust value for the given LID
  */
 unsigned
@@ -1609,17 +1809,21 @@ get_ownertrust_info( ulong lid )
     int c;
 
     otrust = get_ownertrust( lid );
-    switch( (otrust & TRUST_MASK) ) {
-      case TRUST_NEVER:     c = 'n'; break;
-      case TRUST_MARGINAL:  c = 'm'; break;
-      case TRUST_FULLY:     c = 'f'; break;
-      case TRUST_ULTIMATE:  c = 'u'; break;
-      default:             c = '-'; break;
-    }
+    c = trust_letter( (otrust & TRUST_MASK) );
+    if( !c )
+       c = '?';
     return c;
 }
 
-
+/*
+ * Return an allocated buffer with the preference values for
+ * the key with LID and the userid which is identified by the
+ * HAMEHASH or the firstone if namehash is NULL.  ret_n receives
+ * the length of the allcoated buffer. Structure of the buffer is
+ * a repeated sequences of 2 bytes; where the first byte describes the
+ * type of the preference and the second one the value.  The constants
+ * PREFTYPE_xxxx should be used to reference a type.
+ */
 byte *
 get_pref_data( ulong lid, const byte *namehash, size_t *ret_n )
 {
@@ -1696,7 +1900,7 @@ get_dir_record( PKT_public_key *pk, TRUSTREC *rec )
 
 /****************
  * This function simply looks for the key in the trustdb
- * and makes sure that pk->local_id is set to the coreect value.
+ * and makes sure that pk->local_id is set to the correct value.
  * Return: 0 = found
  *        -1 = not found
  *       other = error
@@ -2251,6 +2455,9 @@ upd_uid_record( KBNODE keyblock, KBNODE uidnode, u32 *keyid,
                    urec.r.uid.uidflags |= UIDF_CHECKED | UIDF_VALID;
                    if( !selfsig )
                        selfsig = sig; /* use the first valid sig */
+                   else if( sig->timestamp > selfsig->timestamp
+                            && sig->sig_class >= selfsig->sig_class )
+                       selfsig = sig; /* but this one is newer */
                }
                else {
                    log_info( "uid %08lX/%02X%02X: %s: %s\n",
@@ -2262,7 +2469,13 @@ upd_uid_record( KBNODE keyblock, KBNODE uidnode, u32 *keyid,
            }
            else if( sig->sig_class == 0x30 ) { /* cert revocation */
                rc = check_key_signature( keyblock, node, NULL );
-               if( !rc ) {
+               if( !rc && selfsig && selfsig->timestamp > sig->timestamp ) {
+                   log_info( "uid %08lX.%lu/%02X%02X: %s\n",
+                          (ulong)keyid[1], lid, uidhash[18], uidhash[19],
+                          _("Valid user ID revocation skipped "
+                            "due to a newer self signature\n") );
+               }
+               else if( !rc ) {
                    if( opt.verbose )
                        log_info( "uid %08lX.%lu/%02X%02X: %s\n",
                           (ulong)keyid[1], lid, uidhash[18], uidhash[19],
@@ -2353,14 +2566,16 @@ upd_pref_record( TRUSTREC *urec, u32 *keyid, PKT_signature *sig )
     for(k=0; ptable[k].subpkttype; k++ ) {
        s = parse_sig_subpkt2( sig, ptable[k].subpkttype, &n );
        if( s ) {
-           if( n_prefs_sig >= DIM(prefs_sig)-1 ) {
-               log_info("uid %08lX.%lu/%02X%02X: %s\n",
-                         (ulong)keyid[1], lid, uidhash[18], uidhash[19],
-                         _("Too many preferences") );
-               break;
+           for( ; n; n--, s++ ) {
+               if( n_prefs_sig >= DIM(prefs_sig)-1 ) {
+                   log_info("uid %08lX.%lu/%02X%02X: %s\n",
+                             (ulong)keyid[1], lid, uidhash[18], uidhash[19],
+                             _("Too many preferences") );
+                   break;
+               }
+               prefs_sig[n_prefs_sig++] = ptable[k].preftype;
+               prefs_sig[n_prefs_sig++] = *s;
            }
-           prefs_sig[n_prefs_sig++] = ptable[k].preftype;
-           prefs_sig[n_prefs_sig++] = *s;
        }
     }
     for( recno=urec->r.uid.prefrec; recno; recno = prec.r.pref.next ) {
@@ -2372,13 +2587,15 @@ upd_pref_record( TRUSTREC *urec, u32 *keyid, PKT_signature *sig )
                          _("Too many preference items") );
                break;
            }
-           prefs_rec[n_prefs_rec++] = prec.r.pref.data[i];
-           prefs_rec[n_prefs_rec++] = prec.r.pref.data[i+1];
+           if( prec.r.pref.data[i] ) {
+               prefs_rec[n_prefs_rec++] = prec.r.pref.data[i];
+               prefs_rec[n_prefs_rec++] = prec.r.pref.data[i+1];
+           }
        }
     }
     if( n_prefs_sig == n_prefs_rec
        && !memcmp( prefs_sig, prefs_rec, n_prefs_sig ) )
-       return;  /* not chnaged */
+       return;  /* not changed */
 
     /* Preferences have changed:  Delete all pref records
      * This is much simpler than checking whether we have to
@@ -2534,11 +2751,13 @@ upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid,
                        rec.r.sig.sig[i].flag |= SIGF_REVOKED;
                }
                else if( rc == G10ERR_NO_PUBKEY ) {
+                 #if 0 /* fixme: For some reason this really happens? */
                    if( (rec.r.sig.sig[i].flag & SIGF_CHECKED) )
                        log_info("sig %08lX.%lu/%02X%02X/%08lX: %s\n",
                                  (ulong)keyid[1], lid, uidhash[18],
                                 uidhash[19], (ulong)sig->keyid[1],
                                 _("Hmmm, public key lost?") );
+                 #endif
                    rec.r.sig.sig[i].flag = SIGF_NOPUBKEY;
                    if( revoke )
                        rec.r.sig.sig[i].flag |= SIGF_REVOKED;
@@ -2720,9 +2939,18 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified )
     /* delete keyrecords from the trustdb which are not anymore used */
     /* should we really do this, or is it better to keep them and */
     /* mark as unused? */
+    /* And set the revocation flag into the dir record */
+    drec.r.dir.dirflags &= ~DIRF_REVOKED;
     lastrecno = 0;
     for( recno=drec.r.dir.keylist; recno; recno = krec.r.key.next ) {
        read_record( recno, &krec, RECTYPE_KEY );
+       if( recno == drec.r.dir.keylist ) { /* this is the primary key */
+           if( (krec.r.key.keyflags & KEYF_REVOKED) ) {
+               drec.r.dir.dirflags |= DIRF_REVOKED;
+               drec.dirty = 1;
+           }
+       }
+
        if( !qry_recno_list( recno_list, recno, RECTYPE_KEY ) ) {
            /* delete this one */
            if( !lastrecno ) {
@@ -2775,11 +3003,11 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified )
     if( rc )
        rc = tdbio_cancel_transaction();
     else {
+       if( modified && tdbio_is_dirty() )
+           *modified = 1;
        drec.r.dir.dirflags |= DIRF_CHECKED;
        drec.r.dir.dirflags &= ~DIRF_VALVALID;
        write_record( &drec );
-       if( modified && tdbio_is_dirty() )
-           *modified = 1;
        rc = tdbio_end_transaction();
     }
     rel_recno_list( &recno_list );
@@ -2883,7 +3111,7 @@ insert_trust_record( PKT_public_key *pk )
     }
 
     /* and put all the other stuff into the keydb */
-    rc = update_trust_record( keyblock, 0, NULL );
+    rc = update_trust_record( keyblock, 1, NULL );
     if( !rc )
        process_hintlist( hintlist, dirrec.r.dir.lid );