diff options
author | David Shaw <dshaw@jabberwocky.com> | 2002-06-29 15:46:34 +0200 |
---|---|---|
committer | David Shaw <dshaw@jabberwocky.com> | 2002-06-29 15:46:34 +0200 |
commit | 3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37 (patch) | |
tree | dac7a3780fb4edf9ca89c93800230e88ab255434 /g10/trustdb.c | |
parent | Update head to match stable 1.0 (diff) | |
download | gnupg2-3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37.tar.xz gnupg2-3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37.zip |
Update head to match stable 1.0
Diffstat (limited to 'g10/trustdb.c')
-rw-r--r-- | g10/trustdb.c | 3848 |
1 files changed, 1336 insertions, 2512 deletions
diff --git a/g10/trustdb.c b/g10/trustdb.c index 1fd2383c0..85b7dbcb9 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,5 +1,5 @@ /* trustdb.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -22,599 +22,353 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <errno.h> -#include <ctype.h> #include <assert.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" -#include "trustdb.h" #include "options.h" #include "packet.h" #include "main.h" #include "i18n.h" #include "tdbio.h" -#include "ttyio.h" - -#if MAX_FINGERPRINT_LEN > 20 - #error Must change structure of trustdb -#endif - -struct keyid_list { - struct keyid_list *next; - u32 keyid[2]; -}; +#include "trustdb.h" -struct local_id_item { - struct local_id_item *next; - ulong lid; - unsigned flag; -}; -struct local_id_table { - struct local_id_table *next; /* only used to keep a list of unused tables */ - struct local_id_item *items[16]; +/* + * A structure to store key identification as well as some stuff needed + * for validation + */ +struct key_item { + struct key_item *next; + unsigned int ownertrust; + u32 kid[2]; }; -typedef struct local_id_table *LOCAL_ID_TABLE; - - -struct enum_cert_paths_ctx { - int init; - int idx; -}; - +typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ -struct recno_list_struct { - struct recno_list_struct *next; - ulong recno; - int type; -}; -typedef struct recno_list_struct *RECNO_LIST; - - - -typedef struct trust_node *TN; -struct trust_node { - TN back; /* parent */ - TN list; /* list of other node (should all be of the same type)*/ - TN next; /* used to build the list */ - int is_uid; /* set if this is an uid node */ - ulong lid; /* key or uid recordnumber */ - union { - struct { - int ownertrust; - int validity; - /* helper */ - int buckstop; - } k; - struct { - int marginal_count; - int fully_count; - int validity; - } u; - } n; +/* + * Structure to keep track of keys, this is used as an array wherre + * the item right after the last one has a keyblock set to NULL. + * Maybe we can drop this thing and replace it by key_item + */ +struct key_array { + KBNODE keyblock; }; -static TN used_tns; -static int alloced_tns; -static int max_alloced_tns; - -static struct keyid_list *trusted_key_list; - -static LOCAL_ID_TABLE new_lid_table(void); -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 int propagate_validity( TN root, TN node, - int (*add_fnc)(ulong), unsigned *retflgs ); - -static void print_user_id( FILE *fp, const char *text, u32 *keyid ); -static int do_check( TRUSTREC *drec, unsigned *trustlevel, - const char *nhash, int (*add_fnc)(ulong), - unsigned *retflgs); -static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); -static int do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, - int sigs_only, int *modified ); -static int check_trust_record( TRUSTREC *drec, int sigs_only ); -static void mark_fresh_keys(void); - -/* a table used to keep track of ultimately trusted keys - * which are the ones from our secrings and the trusted keys */ -static LOCAL_ID_TABLE ultikey_table; - - -/* a table to keep track of newly importted keys. This one is - * create by the insert_trust_record function and from time to time - * used to verify key signature which have been done with these new keys */ -static LOCAL_ID_TABLE fresh_imported_keys; -static int fresh_imported_keys_count; -#define FRESH_KEY_CHECK_THRESHOLD 200 - -/* list of unused lid items and tables */ -static LOCAL_ID_TABLE unused_lid_tables; -static struct local_id_item *unused_lid_items; - +/* control information for the trust DB */ static struct { int init; int level; char *dbname; } trustdb_args; - -/********************************************** - *********** record read write ************** - **********************************************/ - - -/**************** - * Read a record but die if it does not exist - */ -static void -read_record( ulong recno, TRUSTREC *rec, int rectype ) -{ - int rc = tdbio_read_record( recno, rec, rectype ); - if( !rc ) - return; - log_error(_("trust record %lu, req type %d: read failed: %s\n"), - recno, rectype, gpg_errstr(rc) ); - tdbio_invalid(); -} +/* some globals */ +static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */ +static struct key_item *utk_list; /* all ultimately trusted keys */ +static int pending_check_trustdb; -/**************** - * Wirte a record but die on error - */ -static void -write_record( TRUSTREC *rec ) -{ - int rc = tdbio_write_record( rec ); - if( !rc ) - return; - log_error(_("trust record %lu, type %d: write failed: %s\n"), - rec->recnum, rec->rectype, gpg_errstr(rc) ); - tdbio_invalid(); -} - -/**************** - * Delete a record but die on error - */ -static void -delete_record( ulong recno ) -{ - int rc = tdbio_delete_record( recno ); - if( !rc ) - return; - log_error(_("trust record %lu: delete failed: %s\n"), - recno, gpg_errstr(rc) ); - tdbio_invalid(); -} - -/**************** - * sync the db - */ -static void -do_sync(void) -{ - int rc = tdbio_sync(); - if( !rc ) - return; - log_error(_("trustdb: sync failed: %s\n"), gpg_errstr(rc) ); - gpg_exit(2); -} - +static int validate_keys (int interactive); /********************************************** - ***************** helpers ****************** + ************* some helpers ******************* **********************************************/ - -static LOCAL_ID_TABLE -new_lid_table(void) +static struct key_item * +new_key_item (void) { - LOCAL_ID_TABLE a; - - a = unused_lid_tables; - if( a ) { - unused_lid_tables = a->next; - memset( a, 0, sizeof *a ); - } - else - a = gcry_xcalloc( 1, sizeof *a ); - return a; + struct key_item *k; + + k = m_alloc_clear (sizeof *k); + return k; } -#if 0 static void -release_lid_table( LOCAL_ID_TABLE tbl ) +release_key_items (struct key_item *k) { - struct local_id_item *a, *a2; - int i; - - for(i=0; i < 16; i++ ) { - for(a=tbl->items[i]; a; a = a2 ) { - a2 = a->next; - a->next = unused_lid_items; - unused_lid_items = a; - } - } - tbl->next = unused_lid_tables; - unused_lid_tables = tbl; -} -#endif - + struct key_item *k2; -/**************** - * Remove all items from a LID table - */ -static void -clear_lid_table( LOCAL_ID_TABLE tbl ) -{ - struct local_id_item *a, *a2; - int i; - - for(i=0; i < 16; i++ ) { - for(a=tbl->items[i]; a; a = a2 ) { - a2 = a->next; - a->next = unused_lid_items; - unused_lid_items = a; - } - tbl->items[i] = NULL; + for (; k; k = k2) + { + k2 = k->next; + m_free (k); } } - -/**************** - * Add a new item to the table or return 1 if we already have this item +/* + * For fast keylook up we need a hash table. Each byte of a KeyIDs + * should be distributed equally over the 256 possible values (except + * for v3 keyIDs but we consider them as not important here). So we + * can just use 10 bits to index a table of 1024 key items. + * Possible optimization: Don not use key_items but other hash_table when the + * duplicates lists gets too large. */ -static int -ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ) +static KeyHashTable +new_key_hash_table (void) { - struct local_id_item *a; - - for( a = tbl->items[lid & 0x0f]; a; a = a->next ) - if( a->lid == lid ) - return 1; - a = unused_lid_items; - if( a ) - unused_lid_items = a->next; - else - a = gcry_xmalloc( sizeof *a ); - a->lid = lid; - a->flag = flag; - a->next = tbl->items[lid & 0x0f]; - tbl->items[lid & 0x0f] = a; - return 0; + struct key_item **tbl; + + tbl = m_alloc_clear (1024 * sizeof *tbl); + return tbl; } -static int -qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ) +static void +release_key_hash_table (KeyHashTable tbl) { - struct local_id_item *a; + int i; - for( a = tbl->items[lid & 0x0f]; a; a = a->next ) - if( a->lid == lid ) { - if( flag ) - *flag = a->flag; - return 0; - } - return -1; + if (!tbl) + return; + for (i=0; i < 1024; i++) + release_key_items (tbl[i]); + m_free (tbl); } - -static TN -new_tn(void) +/* + * Returns: True if the keyID is in the given hash table + */ +static int +test_key_hash_table (KeyHashTable tbl, u32 *kid) { - TN t; + struct key_item *k; - if( used_tns ) { - t = used_tns; - used_tns = t->next; - memset( t, 0, sizeof *t ); - } - else - t = gcry_xcalloc( 1, sizeof *t ); - if( ++alloced_tns > max_alloced_tns ) - max_alloced_tns = alloced_tns; - return t; + for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next) + if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) + return 1; + return 0; } - +/* + * Add a new key to the hash table. The key is identified by its key ID. + */ static void -release_tn( TN t ) +add_key_hash_table (KeyHashTable tbl, u32 *kid) { - if( t ) { - t->next = used_tns; - used_tns = t; - alloced_tns--; - } -} + struct key_item *k, *kk; + for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next) + if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) + return; /* already in table */ + + kk = new_key_item (); + kk->kid[0] = kid[0]; + kk->kid[1] = kid[1]; + kk->next = tbl[(kid[1] & 0x03ff)]; + tbl[(kid[1] & 0x03ff)] = kk; +} +/* + * Release a key_array + */ static void -release_tn_tree( TN kr ) +release_key_array ( struct key_array *keys ) { - TN kr2; + struct key_array *k; - for( ; kr; kr = kr2 ) { - release_tn_tree( kr->list ); - kr2 = kr->next; - release_tn( kr ); + if (keys) { + for (k=keys; k->keyblock; k++) + release_kbnode (k->keyblock); + m_free (keys); } } + +/********************************************* + ********** Initialization ***************** + *********************************************/ - -/********************************************** - ****** access by LID and other helpers ******* - **********************************************/ -/**************** - * Return the keyid from the primary key identified by LID. +/* + * Used to register extra ultimately trusted keys - this has to be done + * before initializing the validation module. + * FIXME: Should be replaced by a function to add those keys to the trustdb. */ -int -keyid_from_lid( ulong lid, u32 *keyid ) +void +register_trusted_key( const char *string ) { - TRUSTREC rec; - int rc; - - init_trustdb(); - keyid[0] = keyid[1] = 0; - rc = tdbio_read_record( lid, &rec, 0 ); - if( rc ) { - log_error(_("error reading dir record for LID %lu: %s\n"), - lid, gpg_errstr(rc)); - return GPGERR_TRUSTDB; - } - if( rec.rectype == RECTYPE_SDIR ) - return 0; - if( rec.rectype != RECTYPE_DIR ) { - log_error(_("lid %lu: expected dir record, got type %d\n"), - lid, rec.rectype ); - return GPGERR_TRUSTDB; - } - if( !rec.r.dir.keylist ) { - log_error(_("no primary key for LID %lu\n"), lid ); - return GPGERR_TRUSTDB; - } - rc = tdbio_read_record( rec.r.dir.keylist, &rec, RECTYPE_KEY ); - if( rc ) { - log_error(_("error reading primary key for LID %lu: %s\n"), - lid, gpg_errstr(rc)); - return GPGERR_TRUSTDB; - } - keyid_from_fingerprint( rec.r.key.fingerprint, rec.r.key.fingerprint_len, - keyid ); - - return 0; -} + KEYDB_SEARCH_DESC desc; + struct key_item *k; + if (classify_user_id (string, &desc) != KEYDB_SEARCH_MODE_LONG_KID ) { + log_error(_("`%s' is not a valid long keyID\n"), string ); + return; + } -ulong -lid_from_keyblock( KBNODE keyblock ) -{ - KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); - PKT_public_key *pk; - if( !node ) - BUG(); - pk = node->pkt->pkt.public_key; - if( !pk->local_id ) { - TRUSTREC rec; - init_trustdb(); - - get_dir_record( pk, &rec ); - } - return pk->local_id; + k = new_key_item (); + k->kid[0] = desc.u.kid[0]; + k->kid[1] = desc.u.kid[1]; + k->next = user_utk_list; + user_utk_list = k; } - +/* + * Helper to add a key to the global list of ultimately trusted keys. + * Retruns: true = inserted, false = already in in list. + */ static int -get_dir_record( PKT_public_key *pk, TRUSTREC *rec ) +add_utk (u32 *kid) { - int rc=0; + struct key_item *k; - if( pk->local_id ) { - read_record( pk->local_id, rec, RECTYPE_DIR ); - } - else { /* no local_id: scan the trustdb */ - if( (rc=tdbio_search_dir_bypk( pk, rec )) && rc != -1 ) - log_error(_("get_dir_record: search_record failed: %s\n"), - gpg_errstr(rc)); + for (k = utk_list; k; k = k->next) + { + if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) + { + return 0; + } } - return rc; -} -static ulong -lid_from_keyid_no_sdir( u32 *keyid ) -{ - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - TRUSTREC rec; - ulong lid = 0; - int rc; - - rc = get_pubkey( pk, keyid ); - if( !rc ) { - if( pk->local_id ) - lid = pk->local_id; - else { - rc = tdbio_search_dir_bypk( pk, &rec ); - if( !rc ) - lid = rec.recnum; - } - } - free_public_key( pk ); - return lid; + k = new_key_item (); + k->kid[0] = kid[0]; + k->kid[1] = kid[1]; + k->ownertrust = TRUST_ULTIMATE; + k->next = utk_list; + utk_list = k; + if( opt.verbose > 1 ) + log_info(_("key %08lX: accepted as trusted key\n"), (ulong)kid[1]); + return 1; } - -/*********************************************** - ************* Initialization **************** - ***********************************************/ - -void -register_trusted_key( const char *string ) +/**************** + * Verify that all our secret keys are usable and put them into the utk_list. + */ +static void +verify_own_keys(void) { - u32 keyid[2]; - struct keyid_list *r; + TRUSTREC rec; + ulong recnum; + int rc; + struct key_item *k; + int hint_shown = 0; + + if (utk_list) + return; - if( classify_user_id( string, keyid, NULL, NULL, NULL ) != 11 ) { - log_error(_("'%s' is not a valid long keyID\n"), string ); - return; + /* scan the trustdb to find all ultimately trusted keys */ + for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) + { + if ( rec.rectype == RECTYPE_TRUST + && (rec.r.trust.ownertrust & TRUST_MASK) == TRUST_ULTIMATE) + { + byte *fpr = rec.r.trust.fingerprint; + int fprlen; + u32 kid[2]; + + /* Problem: We do only use fingerprints in the trustdb but + * we need the keyID here to indetify the key; we can only + * use that ugly hack to distinguish between 16 and 20 + * butes fpr - it does not work always so we better change + * the whole validation code to only work with + * fingerprints */ + fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20; + keyid_from_fingerprint (fpr, fprlen, kid); + if (!add_utk (kid)) + log_info(_("key %08lX occurs more than once in the trustdb\n"), + (ulong)kid[1]); + } + } + + /* the --trusted-key option is again deprecated; however we automagically + * add those keys to the trustdb */ + for (k = user_utk_list; k; k = k->next) + { + if ( add_utk (k->kid) ) + { /* not yet in trustDB as ultimately trusted */ + PKT_public_key pk; + + memset (&pk, 0, sizeof pk); + rc = get_pubkey (&pk, k->kid); + if (rc) { + log_info(_("key %08lX: no public key for trusted key - skipped\n"), + (ulong)k->kid[1] ); + } + else { + update_ownertrust (&pk, + ((get_ownertrust (&pk) & ~TRUST_MASK) + | TRUST_ULTIMATE )); + release_public_key_parts (&pk); + } + if (!hint_shown) + { + log_info ("the --trusted-key option is now obsolete; " + "use the --edit command instead.\n"); + log_info ("given keys will be marked as trusted\n"); + hint_shown = 1; + } + log_info ("key %08lX marked as ultimately trusted\n", + (ulong)k->kid[1]); + } } - for( r = trusted_key_list; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) - return; - r = gcry_xmalloc( sizeof *r ); - r->keyid[0] = keyid[0]; - r->keyid[1] = keyid[1]; - r->next = trusted_key_list; - trusted_key_list = r; -} + /* release the helper table table */ + release_key_items (user_utk_list); + user_utk_list = NULL; + return; +} + +/********************************************* + *********** TrustDB stuff ******************* + *********************************************/ +/* + * Read a record but die if it does not exist + */ static void -add_ultimate_key( PKT_public_key *pk, u32 *keyid ) +read_record (ulong recno, TRUSTREC *rec, int rectype ) { - int rc; - - /* first make sure that the pubkey is in the trustdb */ - rc = query_trust_record( pk ); - if( rc == -1 && opt.dry_run ) - return; - if( rc == -1 ) { /* put it into the trustdb */ - rc = insert_trust_record_by_pk( pk ); - if( rc ) { - log_error(_("key %08lX: can't put it into the trustdb\n"), - (ulong)keyid[1] ); - return; - } + int rc = tdbio_read_record (recno, rec, rectype); + if (rc) + { + log_error(_("trust record %lu, req type %d: read failed: %s\n"), + recno, rec->rectype, g10_errstr(rc) ); + tdbio_invalid(); } - else if( rc ) { - log_error(_("key %08lX: query record failed\n"), (ulong)keyid[1] ); - return; + if (rectype != rec->rectype) + { + log_error(_("trust record %lu is not of requested type %d\n"), + rec->recnum, rectype); + tdbio_invalid(); } - - if( DBG_TRUST ) - 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 trusted key table\n"), - (ulong)keyid[1]); - else if( opt.verbose > 1 ) - log_info(_("key %08lX: accepted as trusted key.\n"), - (ulong)keyid[1]); - } -/**************** - * Verify that all our public keys are in the trustdb. +/* + * Write a record and die on error */ -static int -verify_own_keys(void) +static void +write_record (TRUSTREC *rec) { - int rc; - void *enum_context = NULL; - PKT_secret_key *sk = gcry_xcalloc( 1, sizeof *sk ); - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - u32 keyid[2]; - struct keyid_list *kl; - - - /* put the trusted keys into the ultikey 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 { - add_ultimate_key( pk, keyid ); - release_public_key_parts( pk ); - } + int rc = tdbio_write_record (rec); + if (rc) + { + log_error(_("trust record %lu, type %d: write failed: %s\n"), + rec->recnum, rec->rectype, g10_errstr(rc) ); + tdbio_invalid(); } - - /* And now add all secret keys to the ultikey table */ - while( !(rc=enum_secret_keys( &enum_context, sk, 0 ) ) ) { - int have_pk = 0; - - keyid_from_sk( sk, keyid ); - - if( DBG_TRUST ) - log_debug("key %08lX: checking secret key\n", (ulong)keyid[1] ); - - if( !opt.quiet && is_secret_key_protected( sk ) < 1 ) - 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 ); - if( rc ) { - log_info(_("key %08lX: secret key without public key - skipped\n"), - (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"), - (ulong)keyid[1] ); - goto skip; - } - - add_ultimate_key( pk, keyid ); - - skip: - release_secret_key_parts( sk ); - if( have_pk ) - release_public_key_parts( pk ); - } - if( rc != -1 ) - log_error(_("enumerate secret keys failed: %s\n"), gpg_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; - gcry_free( kl ); - } - trusted_key_list = NULL; - } - - enum_secret_keys( &enum_context, NULL, 0 ); /* free context */ - free_secret_key( sk ); - free_public_key( pk ); - return rc; } - +/* + * sync the TrustDb and die on error + */ +static void +do_sync(void) +{ + int rc = tdbio_sync (); + if(rc) + { + log_error (_("trustdb: sync failed: %s\n"), g10_errstr(rc) ); + g10_exit(2); + } +} /**************** @@ -629,2198 +383,1268 @@ setup_trustdb( int level, const char *dbname ) if( trustdb_args.init ) return 0; trustdb_args.level = level; - trustdb_args.dbname = dbname? gcry_xstrdup(dbname): NULL; + trustdb_args.dbname = dbname? m_strdup(dbname): NULL; return 0; } void init_trustdb() { - int rc=0; - int level = trustdb_args.level; - const char* dbname = trustdb_args.dbname; - - if( trustdb_args.init ) - return; + int rc=0; + int level = trustdb_args.level; + const char* dbname = trustdb_args.dbname; - trustdb_args.init = 1; - - if( !ultikey_table ) - ultikey_table = new_lid_table(); - - if( !level || level==1 ) { - rc = tdbio_set_dbname( dbname, !!level ); - if( !rc ) { - if( !level ) - return; + if( trustdb_args.init ) + return; - /* verify that our own keys are in the trustDB - * or move them to the trustdb. */ - rc = verify_own_keys(); + trustdb_args.init = 1; - /* should we check whether there is no other ultimately trusted - * key in the database? */ - } + if ( !level || level==1) + { + rc = tdbio_set_dbname( dbname, !!level ); + if( !rc ) + { + if( !level ) + return; + + /* verify that our own keys are in the trustDB + * or move them to the trustdb. */ + verify_own_keys(); + + /* should we check whether there is no other ultimately trusted + * key in the database? */ + } } - else - BUG(); - if( rc ) - log_fatal("can't init trustdb: %s\n", gpg_errstr(rc) ); + else + BUG(); + if( rc ) + log_fatal("can't init trustdb: %s\n", g10_errstr(rc) ); } -/**************** - * This function should be called in certain cases to sync the internal state - * of the trustdb with the file image. Currently it is needed after - * a sequence of insert_trust_record() calls. - */ -void -sync_trustdb() -{ - if( fresh_imported_keys && fresh_imported_keys_count ) - mark_fresh_keys(); -} - - /*********************************************** ************* Print helpers **************** ***********************************************/ -static void -print_user_id( FILE *fp, const char *text, u32 *keyid ) -{ - char *p; - size_t n; - - p = get_user_id( keyid, &n ); - if( fp ) { - fprintf( fp, "%s \"", text ); - print_utf8_string( fp, p, n ); - putc('\"', fp); - putc('\n', fp); - } - else { - tty_printf( "%s \"", text ); - tty_print_utf8_string( p, n ); - tty_printf( "\"\n" ); - } - gcry_free(p); -} - - /**************** * This function returns a letter for a trustvalue Trust flags * are ignore. */ int -trust_letter( unsigned value ) +trust_letter (unsigned int value) { - switch( (value & TRUST_MASK) ) { - 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 ; + switch( (value & TRUST_MASK) ) + { + 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; } } -#if 0 -static void -print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight ) -{ - int rc, c, i; - u32 keyid[2]; - char *p; - size_t n; - - for( i = 0; i < pathlen; i++ ) { - if( highlight ) - fputs(highlight == path[i].lid? "* ":" ", fp ); - rc = keyid_from_lid( path[i].lid, keyid ); - if( rc ) - 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 - fprintf( fp, "%02x", path[i].trust ); - putc(' ', fp); - p = get_user_id( keyid, &n ); - putc(' ', fp); - putc('\"', fp); - print_utf8_string( fp, p, n > 40? 40:n ); - putc('\"', fp); - gcry_free(p); - putc('\n', fp ); - } -} -#endif - - -static void -print_default_uid( FILE *fp, ulong lid ) -{ - u32 keyid[2]; - - if( !keyid_from_lid( lid, keyid ) ) - print_user_id( fp, "", keyid ); -} - +/**************** + * Recreate the WoT but do not ask for new ownertrusts. Special + * feature: In batch mode and without a forced yes, this is only done + * when a check is due. This can be used to run the check from a crontab + */ +void +check_trustdb () +{ + init_trustdb(); + if (opt.batch && !opt.answer_yes) + { + ulong scheduled; + + scheduled = tdbio_read_nextcheck (); + if (!scheduled) + { + log_info (_("no need for a trustdb check\n")); + return; + } -static void -print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno ) -{ - TRUSTREC urec; - KBNODE node; - byte uhash[20]; - - read_record( urecno, &urec, RECTYPE_UID ); - for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID ) { - PKT_user_id *uidpkt = node->pkt->pkt.user_id; - - if( uidpkt->photo ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, - uidpkt->photo, uidpkt->photolen ); - } - else { - gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, - uidpkt->name, uidpkt->len ); - } - if( !memcmp( uhash, urec.r.uid.namehash, 20 ) ) { - print_string( fp, uidpkt->name, uidpkt->len, ':' ); - return; - } - } + if (scheduled > make_timestamp ()) + { + log_info (_("next trustdb check due at %s\n"), + strtimestamp (scheduled)); + return; + } } - fputs("[?]", fp ); + validate_keys (0); } - -static void -dump_tn_tree( FILE *fp, int level, TN tree ) +/* + * Recreate the WoT. + */ +void +update_trustdb() { - TN kr, ur; - - for( kr=tree; kr; kr = kr->next ) { - if( fp ) { - fprintf( fp, "%*s", level*4, "" ); - fprintf( fp, "K%lu(ot=%d,val=%d) ", kr->lid, - kr->n.k.ownertrust, - kr->n.k.validity ); - } - else { - tty_printf("%*s", level*4, "" ); - tty_printf("K%lu(ot=%d,val=%d) ", kr->lid, - kr->n.k.ownertrust, - kr->n.k.validity ); - } - print_default_uid( fp, kr->lid ); - for( ur=kr->list; ur; ur = ur->next ) { - if( fp ) { - fprintf(fp, "%*s ", level*4, "" ); - fprintf(fp, "U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, - ur->n.u.marginal_count, - ur->n.u.fully_count, - ur->n.u.validity - ); - } - else { - tty_printf("%*s ", level*4, "" ); - tty_printf("U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, - ur->n.u.marginal_count, - ur->n.u.fully_count, - ur->n.u.validity - ); - } - dump_tn_tree( fp, level+1, ur->list ); - } - } + init_trustdb(); + validate_keys (1); } -/**************** - * Special version of dump_tn_tree, which prints it colon delimited. - * Format: - * level:keyid:type:recno:ot:val:mc:cc:name: - * With TYPE = U for a user ID - * K for a key - * The RECNO is either the one of the dir record or the one of the uid record. - * OT is the the usual trust letter and only availabel on K lines. - * VAL is the calcualted validity - * MC is the marginal trust counter and only available on U lines - * CC is the same for the complete count - * NAME ist the username and only printed on U lines - */ -static void -dump_tn_tree_with_colons( int level, TN tree ) +void +revalidation_mark (void) { - TN kr, ur; - - for( kr=tree; kr; kr = kr->next ) { - KBNODE kb = NULL; - u32 kid[2]; - - keyid_from_lid( kr->lid, kid ); - get_keyblock_bylid( &kb, kr->lid ); - - printf( "%d:%08lX%08lX:K:%lu:%c:%c::::\n", - level, (ulong)kid[0], (ulong)kid[1], kr->lid, - trust_letter( kr->n.k.ownertrust ), - trust_letter( kr->n.k.validity ) ); - for( ur=kr->list; ur; ur = ur->next ) { - printf( "%d:%08lX%08lX:U:%lu::%c:%d:%d:", - level, (ulong)kid[0], (ulong)kid[1], ur->lid, - trust_letter( kr->n.u.validity ), - ur->n.u.marginal_count, - ur->n.u.fully_count ); - print_uid_from_keyblock( stdout, kb, ur->lid ); - putchar(':'); - putchar('\n'); - dump_tn_tree_with_colons( level+1, ur->list ); - } - release_kbnode( kb ); - } + init_trustdb(); + /* we simply set the time for the next check to 1 (far back in 1970) + * so that a --update-trustdb will be scheduled */ + if (tdbio_write_nextcheck (1)) + do_sync (); + pending_check_trustdb = 1; } - /*********************************************** - ************* trustdb maintenance *********** + *********** Ownertrust et al. **************** ***********************************************/ -/**************** - * Create or update shadow dir record and return the LID of the record - */ -static ulong -create_shadow_dir( PKT_signature *sig ) -{ - TRUSTREC sdir; - int rc; - - /* first see whether we already have such a record */ - rc = tdbio_search_sdir( sig->keyid, sig->pubkey_algo, &sdir ); - if( rc && rc != -1 ) { - log_error("tdbio_search_sdir failed: %s\n", gpg_errstr(rc)); - tdbio_invalid(); - } - if( rc == -1 ) { /* not found: create */ - memset( &sdir, 0, sizeof sdir ); - sdir.recnum = tdbio_new_recnum(); - sdir.rectype= RECTYPE_SDIR; - sdir.r.sdir.lid = sdir.recnum; - sdir.r.sdir.keyid[0] = sig->keyid[0]; - sdir.r.sdir.keyid[1] = sig->keyid[1]; - sdir.r.sdir.pubkey_algo = sig->pubkey_algo; - write_record( &sdir ); - } - return sdir.recnum; -} - - -static ulong -find_or_create_lid( PKT_signature *sig ) -{ - ulong lid; - - lid = lid_from_keyid_no_sdir( sig->keyid ); - if( !lid ) - lid = create_shadow_dir( sig ); - return lid; +static int +read_trust_record (PKT_public_key *pk, TRUSTREC *rec) +{ + int rc; + + init_trustdb(); + rc = tdbio_search_trust_bypk (pk, rec); + if (rc == -1) + return -1; /* no record yet */ + if (rc) + { + log_error ("trustdb: searching trust record failed: %s\n", + g10_errstr (rc)); + return rc; + } + + if (rec->rectype != RECTYPE_TRUST) + { + log_error ("trustdb: record %lu is not a trust record\n", + rec->recnum); + return G10ERR_TRUSTDB; + } + + return 0; } - /**************** - * Check the validity of a key and calculate the keyflags - * keynode points to - * a node with a [sub]key. mainkid has the key ID of the primary key - * keyblock is the complete keyblock which is needed for signature - * checking. LID and PK is only used in verbose mode. + * Return the assigned ownertrust value for the given public key. + * The key should be the primary key. */ -static unsigned int -check_keybinding( KBNODE keyblock, KBNODE keynode, u32 *mainkid, - ulong lid, PKT_public_key *pk ) +unsigned int +get_ownertrust ( PKT_public_key *pk) { - KBNODE node; - int keybind_seen = 0; - int revoke_seen = 0; - unsigned int keyflags=0; - int is_main = (keynode->pkt->pkttype == PKT_PUBLIC_KEY); - int rc; - - if( DBG_TRUST ) - log_debug("check_keybinding: %08lX.%lu\n", - (ulong)mainkid[1], lid ); - - if( is_main ) { - /* a primary key is always valid (user IDs are handled elsewhere)*/ - keyflags = KEYF_CHECKED | KEYF_VALID; - } - - for( node=keynode->next; node; node = node->next ) { - PKT_signature *sig; - - if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - - sig = node->pkt->pkt.signature; - - if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures */ - - if( sig->sig_class == 0x18 && !keybind_seen && !is_main ) { - /* check until we find a valid keybinding */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info(_("key %08lX.%lu: Good subkey binding\n"), - (ulong)keyid_from_pk(pk,NULL), lid ); - keyflags |= KEYF_CHECKED | KEYF_VALID; - } - else { - log_info(_( - "key %08lX.%lu: Invalid subkey binding: %s\n"), - (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) ); - keyflags |= KEYF_CHECKED; - keyflags &= ~KEYF_VALID; - } - keybind_seen = 1; - } - else if( sig->sig_class == 0x20 && !revoke_seen ) { - /* this is a key revocation certificate: check it */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info(_("key %08lX.%lu: Valid key revocation\n"), - (ulong)keyid_from_pk(pk, NULL), lid ); - keyflags |= KEYF_REVOKED; - } - else { - log_info(_( - "key %08lX.%lu: Invalid key revocation: %s\n"), - (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) ); - } - revoke_seen = 1; - } - else if( sig->sig_class == 0x28 && !revoke_seen && !is_main ) { - /* this is a subkey revocation certificate: check it */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info(_( - "key %08lX.%lu: Valid subkey revocation\n"), - (ulong)keyid_from_pk(pk,NULL), lid ); - keyflags |= KEYF_REVOKED; - } - else { - log_info(_( - "key %08lX.%lu: Invalid subkey binding: %s\n"), - (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) ); - } - revoke_seen = 1; - } - /* Hmmm: should we handle direct key signatures here? */ + TRUSTREC rec; + int rc; + + rc = read_trust_record (pk, &rec); + if (rc == -1) + return TRUST_UNKNOWN; /* no record yet */ + if (rc) + { + tdbio_invalid (); + return rc; /* actually never reached */ } - - return keyflags; + + return rec.r.trust.ownertrust; } - -static ulong -make_key_records( KBNODE keyblock, ulong lid, u32 *keyid, int *mainrev ) +/* + * Same as get_ownertrust but return a trust letter instead of an value. + */ +int +get_ownertrust_info (PKT_public_key *pk) { - TRUSTREC *krecs, **kend, *k, *k2; - KBNODE node; - PKT_public_key *pk; - byte fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - ulong keyrecno; - - *mainrev = 0; - krecs = NULL; kend = &krecs; - for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype != PKT_PUBLIC_KEY - && node->pkt->pkttype != PKT_PUBLIC_SUBKEY ) - continue; - pk = node->pkt->pkt.public_key; - fingerprint_from_pk( pk, fpr, &fprlen ); - - /* create the key record */ - k = gcry_xcalloc( 1, sizeof *k ); - k->rectype = RECTYPE_KEY; - k->r.key.lid = lid; - k->r.key.pubkey_algo = pk->pubkey_algo; - k->r.key.fingerprint_len = fprlen; - memcpy(k->r.key.fingerprint, fpr, fprlen ); - k->recnum = tdbio_new_recnum(); - *kend = k; - kend = &k->next; - - k->r.key.keyflags = check_keybinding( keyblock, node, keyid, lid, pk ); - if( (k->r.key.keyflags & KEYF_REVOKED) - && node->pkt->pkttype == PKT_PUBLIC_KEY ) - *mainrev = 1; - } + unsigned int otrust; + int c; - keyrecno = krecs? krecs->recnum : 0; - /* write the keylist and release the memory */ - for( k = krecs; k ; k = k2 ) { - if( k->next ) - k->r.key.next = k->next->recnum; - write_record( k ); - k2 = k->next; - gcry_free( k ); - } - return keyrecno; + otrust = get_ownertrust (pk); + c = trust_letter( (otrust & TRUST_MASK) ); + if( !c ) + c = '?'; + return c; } - -/**************** - * Check the validity of a user ID and calculate the uidflags - * keynode points to a node with a user ID. - * mainkid has the key ID of the primary key, keyblock is the complete - * keyblock which is needed for signature checking. - * Returns: The uid flags and the self-signature which is considered to - * be the most current. +/* + * Set the trust value of the given public key to the new value. + * The key should be a primary one. */ -static unsigned int -check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, - PKT_signature **bestsig ) -{ - KBNODE node; - unsigned int uidflags = 0; - PKT_signature *sig; - PKT_signature *selfsig = NULL; /* the latest valid self signature */ - int rc; - - if( DBG_TRUST ) { - PKT_user_id *uid; - log_debug("check_uidsigs: %08lX.%lu \"", - (ulong)mainkid[1], lid ); - assert(keynode->pkt->pkttype == PKT_USER_ID ); - uid = keynode->pkt->pkt.user_id; - print_string( log_stream(), uid->name, uid->len, '\"' ); - fputs("\"\n", log_stream()); +void +update_ownertrust (PKT_public_key *pk, unsigned int new_trust ) +{ + TRUSTREC rec; + int rc; + + rc = read_trust_record (pk, &rec); + if (!rc) + { + if (DBG_TRUST) + log_debug ("update ownertrust from %u to %u\n", + (unsigned int)rec.r.trust.ownertrust, new_trust ); + if (rec.r.trust.ownertrust != new_trust) + { + rec.r.trust.ownertrust = new_trust; + write_record( &rec ); + revalidation_mark (); + do_sync (); + } } + else if (rc == -1) + { /* no record yet - create a new one */ + size_t dummy; - /* first we check only the selfsignatures */ - for( node=keynode->next; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - sig = node->pkt->pkt.signature; - if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures for now */ - - if( (sig->sig_class&~3) == 0x10 ) { /* regular self signature */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info( "uid %08lX.%lu: %s\n", - (ulong)mainkid[1], lid, _("Good self-signature") ); - 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: %s: %s\n", - (ulong)mainkid[1], _("Invalid self-signature"), - gpg_errstr(rc) ); - uidflags |= UIDF_CHECKED; - } - } - } + if (DBG_TRUST) + log_debug ("insert ownertrust %u\n", new_trust ); - /* and now check for revocations - we must do this after the - * self signature check because a self-signature which is newer - * than a revocation makes the revocation invalid. - * RFC2440 is quiet about tis but I feel this is reasonable for - * non-primary-key revocations. */ - for( node=keynode->next; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - sig = node->pkt->pkt.signature; - if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures for now */ - - if( sig->sig_class == 0x30 ) { /* cert revocation */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc && selfsig && selfsig->timestamp > sig->timestamp ) { - log_info( "uid %08lX.%lu: %s\n", - (ulong)mainkid[1], lid, - _("Valid user ID revocation skipped " - "due to a newer self signature") ); - } - else if( !rc ) { - if( opt.verbose ) - log_info( "uid %08lX.%lu: %s\n", - (ulong)mainkid[1], lid, _("Valid user ID revocation") ); - uidflags |= UIDF_CHECKED | UIDF_VALID | UIDF_REVOKED; - } - else { - log_info("uid %08lX: %s: %s\n", - (ulong)mainkid[1], _("Invalid user ID revocation"), - gpg_errstr(rc) ); - } - } + memset (&rec, 0, sizeof rec); + rec.recnum = tdbio_new_recnum (); + rec.rectype = RECTYPE_TRUST; + fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy); + rec.r.trust.ownertrust = new_trust; + write_record (&rec); + revalidation_mark (); + do_sync (); + rc = 0; + } + else + { + tdbio_invalid (); } - - *bestsig = selfsig; - return uidflags; } - -static unsigned int -check_sig_record( KBNODE keyblock, KBNODE signode, - ulong siglid, int sigidx, u32 *keyid, ulong lid, - u32 *r_expiretime, int *mod_down, int *mod_up ) -{ - PKT_signature *sig = signode->pkt->pkt.signature; - unsigned int sigflag = 0; - TRUSTREC tmp; - int revocation=0, expired=0, rc; - - if( DBG_TRUST ) - log_debug("check_sig_record: %08lX.%lu %lu[%d]\n", - (ulong)keyid[1], lid, siglid, sigidx ); - *r_expiretime = 0; - if( (sig->sig_class&~3) == 0x10 ) /* regular certification */ - ; - else if( sig->sig_class == 0x30 ) /* cert revocation */ - revocation = 1; - else - return SIGF_CHECKED | SIGF_IGNORED; - - read_record( siglid, &tmp, 0 ); - if( tmp.rectype == RECTYPE_DIR ) { - /* the public key is in the trustdb: check sig */ - rc = check_key_signature2( keyblock, signode, NULL, - r_expiretime, &expired ); - if( !rc ) { /* valid signature */ - if( opt.verbose ) - log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s\n", - (ulong)keyid[1], lid, siglid, sigidx, - (ulong)sig->keyid[1], - revocation? _("Valid certificate revocation") - : _("Good certificate") ); - sigflag |= SIGF_CHECKED | SIGF_VALID; - if( expired ) { - sigflag |= SIGF_EXPIRED; - /* We have to reset the expiretime, so that this signature - * does not get checked over and over due to the reached - * expiretime */ - *r_expiretime = 0; - } - if( revocation ) { - sigflag |= SIGF_REVOKED; - *mod_down = 1; - } - else - *mod_up = 1; - } - else if( rc == GPGERR_NO_PUBKEY ) { - /* This may happen if the key is still in the trustdb - * but not available in the keystorage */ - sigflag |= SIGF_NOPUBKEY; - *mod_down = 1; - if( revocation ) - sigflag |= SIGF_REVOKED; - } - else { - log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s: %s\n", - (ulong)keyid[1], lid, siglid, sigidx, - (ulong)sig->keyid[1], - revocation? _("Invalid certificate revocation") - : _("Invalid certificate"), - gpg_errstr(rc)); - sigflag |= SIGF_CHECKED; - if( revocation ) { - sigflag |= SIGF_REVOKED; - *mod_down = 1; - } - } - } - else if( tmp.rectype == RECTYPE_SDIR ) { - /* better check that it is the right one */ - if( tmp.r.sdir.keyid[0] == sig->keyid[0] - && tmp.r.sdir.keyid[1] == sig->keyid[1] - && (!tmp.r.sdir.pubkey_algo - || tmp.r.sdir.pubkey_algo == sig->pubkey_algo )) - sigflag |= SIGF_NOPUBKEY; - else - log_error(_("sig record %lu[%d] points to wrong record.\n"), - siglid, sigidx ); +/* Clear the ownertrust value. Return true if a changed actually happend. */ +int +clear_ownertrust (PKT_public_key *pk) +{ + TRUSTREC rec; + int rc; + + rc = read_trust_record (pk, &rec); + if (!rc) + { + if (DBG_TRUST) + log_debug ("clearing ownertrust (old value %u)\n", + (unsigned int)rec.r.trust.ownertrust); + if (rec.r.trust.ownertrust) + { + rec.r.trust.ownertrust = 0; + write_record( &rec ); + revalidation_mark (); + do_sync (); + return 1; + } } - else { - log_error(_("sig record %lu[%d] points to wrong record.\n"), - siglid, sigidx ); - tdbio_invalid(); + else if (rc != -1) + { + tdbio_invalid (); } - - return sigflag; + return 0; } -/**************** - * Make the sig records for the given uid record - * We don't set flags here or even check the signatures; this will - * happen latter. +/* + * Note: Caller has to do a sync */ -static ulong -make_sig_records( KBNODE keyblock, KBNODE uidnode, - ulong lid, u32 *mainkid, u32 *min_expire, - int *mod_down, int *mod_up ) -{ - TRUSTREC *srecs, **s_end, *s=NULL, *s2; - KBNODE node; - PKT_signature *sig; - ulong sigrecno, siglid; - int i, sigidx = 0; - u32 expiretime; - - srecs = NULL; s_end = &srecs; - for( node=uidnode->next; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - sig = node->pkt->pkt.signature; - if( mainkid[0] == sig->keyid[0] && mainkid[1] == sig->keyid[1] ) - continue; /* we don't care about self-signatures here */ - - siglid = find_or_create_lid( sig ); - /* smash dups */ - /* FIXME: Here we have a problem: - * We can't distinguish between a certification and a certification - * revocation without looking at class of the signature - we have - * to see how we can store the sigclass in the sigrecord.. - * Argg- I hope I can get rid of this ugly trustdb ASAP. - */ - for( s2 = s; s2 ; s2 = s2->next ) { - for(i=0; i < sigidx; i++ ) { - if( s2->r.sig.sig[i].lid == siglid ) - goto leaveduptest; - } - } - for( s2 = srecs; s2 ; s2 = s2->next ) { - for(i=0; i < SIGS_PER_RECORD; i++ ) { - if( s2->r.sig.sig[i].lid == siglid ) - goto leaveduptest; - } - } - leaveduptest: - if( s2 ) { - log_info( "sig %08lX.%lu: %s\n", (ulong)mainkid[1], lid, - _("duplicated certificate - deleted") ); - continue; - } +static void +update_validity (PKT_public_key *pk, const byte *namehash, + int depth, int validity) +{ + TRUSTREC trec, vrec; + int rc; + ulong recno; + + rc = read_trust_record (pk, &trec); + if (rc && rc != -1) + { + tdbio_invalid (); + return; + } + if (rc == -1) /* no record yet - create a new one */ + { + size_t dummy; + + rc = 0; + memset (&trec, 0, sizeof trec); + trec.recnum = tdbio_new_recnum (); + trec.rectype = RECTYPE_TRUST; + fingerprint_from_pk (pk, trec.r.trust.fingerprint, &dummy); + trec.r.trust.ownertrust = 0; + } + + /* locate an existing one */ + recno = trec.r.trust.validlist; + while (recno) + { + read_record (recno, &vrec, RECTYPE_VALID); + if ( !memcmp (vrec.r.valid.namehash, namehash, 20) ) + break; + recno = vrec.r.valid.next; + } + + if (!recno) /* insert a new validity record */ + { + memset (&vrec, 0, sizeof vrec); + vrec.recnum = tdbio_new_recnum (); + vrec.rectype = RECTYPE_VALID; + memcpy (vrec.r.valid.namehash, namehash, 20); + vrec.r.valid.next = trec.r.trust.validlist; + } + vrec.r.valid.validity = validity; + write_record (&vrec); + trec.r.trust.depth = depth; + trec.r.trust.validlist = vrec.recnum; + write_record (&trec); +} + + +/* reset validity for all user IDs. Caller must sync. */ +static int +clear_validity (PKT_public_key *pk) +{ + TRUSTREC trec, vrec; + int rc; + ulong recno; + int any = 0; + + rc = read_trust_record (pk, &trec); + if (rc && rc != -1) + { + tdbio_invalid (); + return 0; + } + if (rc == -1) /* no record yet - no need to clerar it then ;-) */ + return 0; - /* create the sig record */ - if( !sigidx ) { - s = gcry_xcalloc( 1, sizeof *s ); - s->rectype = RECTYPE_SIG; - s->r.sig.lid = lid; - } - s->r.sig.sig[sigidx].lid = siglid; - s->r.sig.sig[sigidx].flag= check_sig_record( keyblock, node, - siglid, sigidx, - mainkid, lid, &expiretime, - mod_down, mod_up ); - - sigidx++; - if( sigidx == SIGS_PER_RECORD ) { - s->recnum = tdbio_new_recnum(); - *s_end = s; - s_end = &s->next; - sigidx = 0; - } - /* keep track of signers pk expire time */ - if( expiretime && (!*min_expire || *min_expire > expiretime ) ) - *min_expire = expiretime; - } - if( sigidx ) { - s->recnum = tdbio_new_recnum(); - *s_end = s; - s_end = &s->next; + /* reset validity for all user IDs */ + recno = trec.r.trust.validlist; + while (recno) + { + read_record (recno, &vrec, RECTYPE_VALID); + if ((vrec.r.valid.validity & TRUST_MASK)) + { + vrec.r.valid.validity &= ~TRUST_MASK; + write_record (&vrec); + any = 1; + } + recno = vrec.r.valid.next; } - sigrecno = srecs? srecs->recnum : 0; - /* write the keylist and release the memory */ - for( s = srecs; s ; s = s2 ) { - if( s->next ) - s->r.sig.next = s->next->recnum; - write_record( s ); - s2 = s->next; - gcry_free( s ); - } - return sigrecno; + return any; } + +/*********************************************** + ********* Query trustdb values ************** + ***********************************************/ -/**************** - * Make a preference record (or a list of them) according to the supplied - * signature. - * Returns: The record number of the first pref record. +/* + * Return the validity information for PK. If the namehash is not + * NULL, the validity of the corresponsing user ID is returned, + * otherwise, a reasonable value for the entire key is returned. */ -static ulong -make_pref_record( PKT_signature *sig, ulong lid ) -{ - static struct { - sigsubpkttype_t subpkttype; - int preftype; - } ptable[] = { - { SIGSUBPKT_PREF_SYM, PREFTYPE_SYM }, - { SIGSUBPKT_PREF_HASH, PREFTYPE_HASH }, - { SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR }, - { 0, 0 } - }; - TRUSTREC *precs, **p_end, *p=NULL, *p2; - ulong precno; - int k, idx=0; - const byte *s; - size_t n; - - #if (ITEMS_PER_PREF_RECORD % 2) != 0 - #error ITEMS_PER_PREF_RECORD must have an even value - #endif - - precs = NULL; p_end = &precs; - for(k=0; ptable[k].subpkttype; k++ ) { - s = parse_sig_subpkt2( sig, ptable[k].subpkttype, &n ); - if( !s ) - continue; - for( ; n; n--, s++ ) { - if( !idx ) { - p = gcry_xcalloc( 1, sizeof *p ); - p->rectype = RECTYPE_PREF; - p->r.pref.lid = lid; - } - p->r.pref.data[idx++] = ptable[k].preftype; - p->r.pref.data[idx++] = *s; - if( idx >= ITEMS_PER_PREF_RECORD ) { - p->recnum = tdbio_new_recnum(); - *p_end = p; - p_end = &p->next; - idx = 0; - } - } - } - if( idx ) { - p->recnum = tdbio_new_recnum(); - *p_end = p; - p_end = &p->next; - } - - precno = precs? precs->recnum : 0; - /* write the precs and release the memory */ - for( p = precs; p ; p = p2 ) { - if( p->next ) - p->r.pref.next = p->next->recnum; - write_record( p ); - p2 = p->next; - gcry_free( p ); +unsigned int +get_validity (PKT_public_key *pk, const byte *namehash) +{ + static int did_nextcheck; + TRUSTREC trec, vrec; + int rc; + ulong recno; + unsigned int validity; + u32 kid[2]; + PKT_public_key *main_pk; + + init_trustdb (); + if (!did_nextcheck) + { + ulong scheduled; + + did_nextcheck = 1; + scheduled = tdbio_read_nextcheck (); + if (scheduled && scheduled <= make_timestamp ()) + { + if (opt.no_auto_check_trustdb) + { + pending_check_trustdb = 1; + log_info ("please do a --check-trustdb\n"); + } + else + { + log_info (_("checking the trustdb\n")); + validate_keys (0); + } + } } - return precno; -} - - -static ulong -make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire, - int *mod_down, int *mod_up ) -{ - TRUSTREC *urecs, **uend, *u, *u2; - KBNODE node; - PKT_user_id *uid; - byte uidhash[20]; - ulong uidrecno; - - urecs = NULL; uend = &urecs; - for( node=keyblock; node; node = node->next ) { - PKT_signature *bestsig; - - if( node->pkt->pkttype != PKT_USER_ID ) - continue; - uid = node->pkt->pkt.user_id; - if( uid->photo ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, - uid->photo, uid->photolen ); - } - else { - gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, - uid->name, uid->len ); - } - /* create the uid record */ - u = gcry_xcalloc( 1, sizeof *u ); - u->rectype = RECTYPE_UID; - u->r.uid.lid = lid; - memcpy(u->r.uid.namehash, uidhash, 20 ); - u->recnum = tdbio_new_recnum(); - *uend = u; - uend = &u->next; - - u->r.uid.uidflags = check_uidsigs( keyblock, node, keyid, - lid, &bestsig ); - if( (u->r.uid.uidflags & UIDF_CHECKED) - && (u->r.uid.uidflags & UIDF_VALID) ) { - u->r.uid.prefrec = bestsig? make_pref_record( bestsig, lid ) : 0; + keyid_from_pk (pk, kid); + if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1]) + { /* this is a subkey - get the mainkey */ + main_pk = m_alloc_clear (sizeof *main_pk); + rc = get_pubkey (main_pk, pk->main_keyid); + if (rc) + { + log_error ("error getting main key %08lX of subkey %08lX: %s\n", + (ulong)pk->main_keyid[1], (ulong)kid[1], g10_errstr(rc)); + validity = TRUST_UNKNOWN; + goto leave; } - - /* the next test is really bad because we should modify - * out modification timestamps only if we really have a change. - * But because we are deleting the uid records first it is somewhat - * difficult to track those changes. fixme */ - if( !( u->r.uid.uidflags & UIDF_VALID ) - || ( u->r.uid.uidflags & UIDF_REVOKED ) ) - *mod_down=1; - else - *mod_up=1; - - /* create the list of signatures */ - u->r.uid.siglist = make_sig_records( keyblock, node, - lid, keyid, min_expire, - mod_down, mod_up ); - } - - uidrecno = urecs? urecs->recnum : 0; - /* write the uidlist and release the memory */ - for( u = urecs; u ; u = u2 ) { - if( u->next ) - u->r.uid.next = u->next->recnum; - write_record( u ); - u2 = u->next; - gcry_free( u ); } - return uidrecno; + else + main_pk = pk; + + rc = read_trust_record (main_pk, &trec); + if (rc && rc != -1) + { + tdbio_invalid (); + return 0; + } + if (rc == -1) /* no record found */ + { + validity = TRUST_UNKNOWN; + goto leave; + } + + /* loop over all user IDs */ + recno = trec.r.trust.validlist; + validity = 0; + while (recno) + { + read_record (recno, &vrec, RECTYPE_VALID); + if ( validity < (vrec.r.valid.validity & TRUST_MASK) ) + validity = (vrec.r.valid.validity & TRUST_MASK); + if ( namehash && !memcmp (vrec.r.valid.namehash, namehash, 20) ) + break; + recno = vrec.r.valid.next; + } + + if (recno) /* okay, use the user ID associated one */ + validity = (vrec.r.valid.validity & TRUST_MASK); + + if ( (trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) ) + validity |= TRUST_FLAG_DISABLED; + + leave: + /* set some flags direct from the key */ + if (main_pk->is_revoked) + validity |= TRUST_FLAG_REVOKED; + if (main_pk != pk && pk->is_revoked) + validity |= TRUST_FLAG_SUB_REVOKED; + /* Note: expiration is a trust value and not a flag - don't know why + * I initially designed it that way */ + if (main_pk->has_expired || pk->has_expired) + validity = (validity & ~TRUST_MASK) | TRUST_EXPIRED; + + if (pending_check_trustdb) + validity |= TRUST_FLAG_PENDING_CHECK; + + if (main_pk != pk) + free_public_key (main_pk); + return validity; } - -/**************** - * Update all the info from the public keyblock. - * The key must already exist in the keydb. - */ int -update_trust_record( KBNODE keyblock, int recheck, int *modified ) +get_validity_info (PKT_public_key *pk, const byte *namehash) { - TRUSTREC drec; - int rc; + int trustlevel; + int c; - /* NOTE: We don't need recheck anymore, but this might chnage again in - * the future */ - if( opt.dry_run ) - return 0; - if( modified ) - *modified = 0; - init_trustdb(); - rc = get_dir_record( find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key, &drec ); - if( rc ) - return rc; - - rc = do_update_trust_record( keyblock, &drec, 0, modified ); - return rc; + trustlevel = get_validity (pk, namehash); + if( trustlevel & TRUST_FLAG_DISABLED ) + return 'd'; + if( trustlevel & TRUST_FLAG_REVOKED ) + return 'r'; + c = trust_letter ( (trustlevel & TRUST_MASK) ); + if( !c ) + c = '?'; + return c; } -/**************** - * Same as update_trust_record, but this functions expects the dir record. - * On exit the dir record will reflect any changes made. - * With sigs_only set only foreign key signatures are checked. - */ -static int -do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, - int sigs_only, int *modified ) -{ - PKT_public_key *primary_pk; - TRUSTREC krec, urec, prec, helprec; - int i, rc = 0; - u32 keyid[2]; /* keyid of primary key */ - int mod_up = 0; - int mod_down = 0; - ulong recno, r2; - u32 expiretime; - - primary_pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; - if( !primary_pk->local_id ) - primary_pk->local_id = drec->recnum; - - keyid_from_pk( primary_pk, keyid ); - if( DBG_TRUST ) - log_debug("do_update_trust_record: %08lX.%lu\n", - (ulong)keyid[1], drec->recnum ); - - rc = tdbio_begin_transaction(); - if( rc ) - return rc; - - /* delete the old stuff FIXME: implementend sigs_only */ - for( recno=drec->r.dir.keylist; recno; recno = krec.r.key.next ) { - read_record( recno, &krec, RECTYPE_KEY ); - delete_record( recno ); - } - drec->r.dir.keylist = 0; - for( recno=drec->r.dir.uidlist; recno; recno = urec.r.uid.next ) { - read_record( recno, &urec, RECTYPE_UID ); - for(r2=urec.r.uid.prefrec ; r2; r2 = prec.r.pref.next ) { - read_record( r2, &prec, RECTYPE_PREF ); - delete_record( r2 ); - } - for(r2=urec.r.uid.siglist ; r2; r2 = helprec.r.sig.next ) { - read_record( r2, &helprec, RECTYPE_SIG ); - delete_record( r2 ); - } - delete_record( recno ); - } - drec->r.dir.uidlist = 0; - - - /* insert new stuff */ - drec->r.dir.dirflags &= ~DIRF_REVOKED; - drec->r.dir.dirflags &= ~DIRF_NEWKEYS; - drec->r.dir.keylist = make_key_records( keyblock, drec->recnum, keyid, &i ); - if( i ) /* primary key has been revoked */ - drec->r.dir.dirflags |= DIRF_REVOKED; - expiretime = 0; - drec->r.dir.uidlist = make_uid_records( keyblock, drec->recnum, keyid, - &expiretime, &mod_down, &mod_up ); - if( rc ) - rc = tdbio_cancel_transaction(); - else { - if( modified && tdbio_is_dirty() ) - *modified = 1; - drec->r.dir.dirflags |= DIRF_CHECKED; - drec->r.dir.valcheck = 0; - drec->r.dir.checkat = expiretime; - write_record( drec ); - tdbio_write_modify_stamp( mod_up, mod_down ); - rc = tdbio_end_transaction(); - } - return rc; -} -/**************** - * Insert a trust record into the TrustDB - * This function assumes that the record does not yet exist. - */ -int -insert_trust_record( KBNODE keyblock ) +void +list_trust_path( const char *username ) { - TRUSTREC dirrec; - TRUSTREC shadow; - KBNODE node; - int rc = 0; - PKT_public_key *pk; - - - if( opt.dry_run ) - return 0; - - init_trustdb(); - - pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; - if( pk->local_id ) { - log_debug("insert_trust_record with pk->local_id=%lu (2)\n", - pk->local_id ); - rc = update_trust_record( keyblock, 1, NULL ); - return rc; - } - - /* We have to look for a shadow dir record which must be reused - * as the dir record. */ - rc = tdbio_search_sdir( pk->keyid, pk->pubkey_algo, &shadow ); - if( rc && rc != -1 ) { - log_error(_("tdbio_search_dir failed: %s\n"), gpg_errstr(rc)); - tdbio_invalid(); - } - memset( &dirrec, 0, sizeof dirrec ); - dirrec.rectype = RECTYPE_DIR; - if( !rc ) /* we have a shadow dir record - convert to dir record */ - dirrec.recnum = shadow.recnum; - else - dirrec.recnum = tdbio_new_recnum(); - dirrec.r.dir.lid = dirrec.recnum; - write_record( &dirrec ); - - /* put the LID into the keyblock */ - pk->local_id = dirrec.r.dir.lid; - for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_PUBLIC_KEY - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - PKT_public_key *a_pk = node->pkt->pkt.public_key; - a_pk->local_id = dirrec.r.dir.lid; - } - else if( node->pkt->pkttype == PKT_SIGNATURE ) { - PKT_signature *a_sig = node->pkt->pkt.signature; - a_sig->local_id = dirrec.r.dir.lid; - } - } - - - /* mark tdb as modified upwards */ - tdbio_write_modify_stamp( 1, 0 ); - - /* and put all the other stuff into the keydb */ - rc = do_update_trust_record( keyblock, &dirrec, 0, NULL ); - - do_sync(); - - /* keep track of new keys */ - if( !fresh_imported_keys ) - fresh_imported_keys = new_lid_table(); - ins_lid_table_item( fresh_imported_keys, pk->local_id, 0 ); - if( ++fresh_imported_keys_count > FRESH_KEY_CHECK_THRESHOLD ) - mark_fresh_keys(); - - return rc; } /**************** - * Insert a trust record indentified by a PK into the TrustDB + * Enumerate all keys, which are needed to build all trust paths for + * the given key. This function does not return the key itself or + * the ultimate key (the last point in cerificate chain). Only + * certificate chains which ends up at an ultimately trusted key + * are listed. If ownertrust or validity is not NULL, the corresponding + * value for the returned LID is also returned in these variable(s). + * + * 1) create a void pointer and initialize it to NULL + * 2) pass this void pointer by reference to this function. + * Set lid to the key you want to enumerate and pass it by reference. + * 3) call this function as long as it does not return -1 + * to indicate EOF. LID does contain the next key used to build the web + * 4) Always call this function a last time with LID set to NULL, + * so that it can free its context. + * + * Returns: -1 on EOF or the level of the returned LID */ int -insert_trust_record_by_pk( PKT_public_key *pk ) -{ - KBNODE keyblock = NULL; - byte fingerprint[MAX_FINGERPRINT_LEN]; - size_t fingerlen; - int rc; - - /* get the keyblock */ - fingerprint_from_pk( pk, fingerprint, &fingerlen ); - rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen ); - if( rc ) { /* that should never happen */ - log_debug( "insert_trust_record_by_pk: keyblock not found: %s\n", - gpg_errstr(rc) ); - } - else { - rc = insert_trust_record( keyblock ); - if( !rc ) /* copy the LID into the PK */ - pk->local_id = find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key->local_id; - } - - release_kbnode( keyblock ); - return rc; -} - - -/**************** - * Check one trust record. This function is called for every - * directory record which is to be checked. The supplied - * dir record is modified according to the performed actions. - * Currently we only do an update_trust_record. - */ -static int -check_trust_record( TRUSTREC *drec, int sigs_only ) +enum_cert_paths( void **context, ulong *lid, + unsigned *ownertrust, unsigned *validity ) { - KBNODE keyblock; - int modified, rc; - - rc = get_keyblock_bylid( &keyblock, drec->recnum ); - if( rc ) { - log_debug( "check_trust_record %lu: keyblock not found: %s\n", - drec->recnum, gpg_errstr(rc) ); - return rc; - } - - rc = do_update_trust_record( keyblock, drec, sigs_only, &modified ); - release_kbnode( keyblock ); - - return rc; + return -1; } /**************** - * Walk over the keyrings and create trustdb records for all keys - * which are not currently in the trustdb. - * It is intended to be used after a fast-import operation. + * Print the current path */ void -update_trustdb() +enum_cert_paths_print( void **context, FILE *fp, + int refresh, ulong selected_lid ) { - KBNODE keyblock = NULL; - KBPOS kbpos; - int rc; - - if( opt.dry_run ) - return; - - init_trustdb(); - rc = enum_keyblocks_begin( &kbpos, 0 ); - if( !rc ) { - ulong count=0, err_count=0, new_count=0; - - while( !(rc = enum_keyblocks_next( kbpos, 1, &keyblock )) ) { - /*int modified;*/ - TRUSTREC drec; - PKT_public_key *pk = find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key; - - rc = get_dir_record( pk, &drec ); - if( rc == -1 ) { /* not in trustdb: insert */ - rc = insert_trust_record( keyblock ); - if( rc && !pk->local_id ) { - log_error(_("lid ?: insert failed: %s\n"), - gpg_errstr(rc) ); - err_count++; - } - else if( rc ) { - log_error(_("lid %lu: insert failed: %s\n"), - pk->local_id, gpg_errstr(rc) ); - err_count++; - } - else { - if( opt.verbose ) - log_info(_("lid %lu: inserted\n"), pk->local_id ); - new_count++; - } - } - else if( rc ) { - log_error(_("error reading dir record: %s\n"), gpg_errstr(rc)); - err_count++; - } - - release_kbnode( keyblock ); keyblock = NULL; - if( !(++count % 100) ) - log_info(_("%lu keys so far processed\n"), count); - } - log_info(_("%lu keys processed\n"), count); - if( err_count ) - log_info(_("\t%lu keys with errors\n"), err_count); - if( new_count ) - log_info(_("\t%lu keys inserted\n"), new_count); - } - if( rc && rc != -1 ) - log_error(_("enumerate keyblocks failed: %s\n"), gpg_errstr(rc)); - - enum_keyblocks_end( kbpos ); - release_kbnode( keyblock ); + return; } + +/**************************************** + *********** NEW NEW NEW **************** + ****************************************/ -/**************** - * Do all required checks in the trustdb. This function walks over all - * records in the trustdb and does scheduled processing. - */ -void -check_trustdb( const char *username ) +static int +ask_ownertrust (u32 *kid) { - TRUSTREC rec; - ulong recnum; - ulong count=0, upd_count=0, err_count=0, skip_count=0, sigonly_count=0; - ulong current_time = make_timestamp(); - - if( username ) - log_info("given user IDs ignored in check_trustdb\n"); - - init_trustdb(); - - for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { - int sigs_only; - - if( rec.rectype != RECTYPE_DIR ) - continue; /* we only want the dir records */ - - if( count && !(count % 100) && !opt.quiet ) - log_info(_("%lu keys so far processed\n"), count); - count++; - sigs_only = 0; - - if( !(rec.r.dir.dirflags & DIRF_CHECKED) ) - ; - else if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) { - if( !(rec.r.dir.dirflags & DIRF_NEWKEYS) ) { - skip_count++; - continue; /* not scheduled for checking */ - } - sigs_only = 1; /* new public keys - check them */ - sigonly_count++; - } - - if( !rec.r.dir.keylist ) { - log_info(_("lid %lu: dir record w/o key - skipped\n"), recnum); - skip_count++; - continue; - } + PKT_public_key *pk; + int rc; + int ot; - check_trust_record( &rec, sigs_only ); + pk = m_alloc_clear (sizeof *pk); + rc = get_pubkey (pk, kid); + if (rc) + { + log_error (_("public key %08lX not found: %s\n"), + (ulong)kid[1], g10_errstr(rc) ); + return TRUST_UNKNOWN; } - - log_info(_("%lu keys processed\n"), count); - if( sigonly_count ) - log_info(_("\t%lu due to new pubkeys\n"), sigonly_count); - if( skip_count ) - log_info(_("\t%lu keys skipped\n"), skip_count); - if( err_count ) - log_info(_("\t%lu keys with errors\n"), err_count); - if( upd_count ) - log_info(_("\t%lu keys updated\n"), upd_count); + + ot=edit_ownertrust(pk,0); + if(ot>0) + ot = get_ownertrust (pk); + else if(ot==0) + ot = TRUST_UNDEFINED; + else + ot = -1; /* quit */ + free_public_key( pk ); + return ot; } - -/*********************************************** - ********* Trust calculation ***************** - ***********************************************/ - -/**************** - * Find all certification paths of a given LID. - * Limit the search to MAX_DEPTH. stack is a helper variable which - * should have been allocated with size max_depth, stack[0] should - * be setup to the key we are investigating, so the minimal depth - * we should ever see in this function is 1. - * Returns: a new tree - * certchain_set must be a valid set or point to NULL; this function - * may modifiy it. - * - * Hmmm: add a fastscan mode which stops at valid validity nodes. - */ -static TN -build_cert_tree( ulong lid, int depth, int max_depth, TN helproot ) +static void +mark_keyblock_seen (KeyHashTable tbl, KBNODE node) { - TRUSTREC dirrec; - TRUSTREC uidrec; - ulong uidrno; - TN keynode; - - if( depth >= max_depth ) - return NULL; - - keynode = new_tn(); - if( !helproot ) - helproot = keynode; - keynode->lid = lid; - if( !qry_lid_table_flag( ultikey_table, lid, NULL ) ) { - /* this is an ultimately trusted key; - * which means that we have found the end of the chain: - * We do this here prior to reading the dir record - * because we don't really need the info from that record */ - keynode->n.k.ownertrust = TRUST_ULTIMATE; - keynode->n.k.buckstop = 1; - return keynode; - } - read_record( lid, &dirrec, 0 ); - if( dirrec.rectype != RECTYPE_DIR ) { - if( dirrec.rectype != RECTYPE_SDIR ) - log_debug("lid %lu, has rectype %d" - " - skipped\n", lid, dirrec.rectype ); - gcry_free(keynode); - return NULL; - } - - if( dirrec.r.dir.checkat && dirrec.r.dir.checkat <= make_timestamp() ) { - check_trust_record( &dirrec, 0 ); - } - else if( (dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) { - check_trust_record( &dirrec, 1 ); - } - - keynode->n.k.ownertrust = dirrec.r.dir.ownertrust & TRUST_MASK; - - /* loop over all user ids */ - for( uidrno = dirrec.r.dir.uidlist; uidrno; uidrno = uidrec.r.uid.next ) { - TRUSTREC sigrec; - ulong sigrno; - TN uidnode = NULL; - - read_record( uidrno, &uidrec, RECTYPE_UID ); - - if( !(uidrec.r.uid.uidflags & UIDF_CHECKED) ) - continue; /* user id has not been checked */ - if( !(uidrec.r.uid.uidflags & UIDF_VALID) ) - continue; /* user id is not valid */ - if( (uidrec.r.uid.uidflags & UIDF_REVOKED) ) - continue; /* user id has been revoked */ - - /* loop over all signature records */ - for(sigrno=uidrec.r.uid.siglist; sigrno; sigrno = sigrec.r.sig.next ) { - int i; - TN tn; - - read_record( sigrno, &sigrec, RECTYPE_SIG ); - - for(i=0; i < SIGS_PER_RECORD; i++ ) { - if( !sigrec.r.sig.sig[i].lid ) - continue; /* skip deleted sigs */ - if( !(sigrec.r.sig.sig[i].flag & SIGF_CHECKED) ) - continue; /* skip unchecked signatures */ - if( !(sigrec.r.sig.sig[i].flag & SIGF_VALID) ) - continue; /* skip invalid signatures */ - if( (sigrec.r.sig.sig[i].flag & SIGF_EXPIRED) ) - continue; /* skip expired signatures */ - if( (sigrec.r.sig.sig[i].flag & SIGF_REVOKED) ) - continue; /* skip revoked signatures */ - /* check for cycles */ - for( tn=keynode; tn && tn->lid != sigrec.r.sig.sig[i].lid; - tn = tn->back ) - ; - if( tn ) - continue; /* cycle found */ - - tn = build_cert_tree( sigrec.r.sig.sig[i].lid, - depth+1, max_depth, helproot ); - if( !tn ) - continue; /* cert chain too deep or error */ - - if( !uidnode ) { - uidnode = new_tn(); - uidnode->back = keynode; - uidnode->lid = uidrno; - uidnode->is_uid = 1; - uidnode->next = keynode->list; - keynode->list = uidnode; - } - - tn->back = uidnode; - tn->next = uidnode->list; - uidnode->list = tn; - if( tn->n.k.buckstop ) { - /* ultimately trusted key found: - * no need to check more signatures of this uid */ - sigrec.r.sig.next = 0; - break; - } - } - } /* end loop over sig recs */ - } /* end loop over user ids */ + for ( ;node; node = node->next ) + if (node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + u32 aki[2]; - if( !keynode->list ) { - release_tn_tree( keynode ); - keynode = NULL; - } - - return keynode; + keyid_from_pk (node->pkt->pkt.public_key, aki); + add_key_hash_table (tbl, aki); + } } + static void -upd_one_ownertrust( ulong lid, unsigned new_trust, unsigned *retflgs ) -{ - TRUSTREC rec; - - read_record( lid, &rec, RECTYPE_DIR ); - if( DBG_TRUST ) - log_debug("upd_one_ownertrust of %lu from %u to %u\n", - lid, (unsigned)rec.r.dir.ownertrust, new_trust ); - if( retflgs ) { - if( (new_trust & TRUST_MASK) > (rec.r.dir.ownertrust & TRUST_MASK) ) - *retflgs |= 16; /* modified up */ - else - *retflgs |= 32; /* modified down */ +dump_key_array (int depth, struct key_array *keys) +{ + struct key_array *kar; + + for (kar=keys; kar->keyblock; kar++) + { + KBNODE node = kar->keyblock; + u32 kid[2]; + + keyid_from_pk(node->pkt->pkt.public_key, kid); + printf ("%d:%08lX%08lX:K::%c::::\n", + depth, (ulong)kid[0], (ulong)kid[1], '?'); + + for (; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + int len = node->pkt->pkt.user_id->len; + + if (len > 30) + len = 30; + printf ("%d:%08lX%08lX:U:::%c:::", + depth, (ulong)kid[0], (ulong)kid[1], + (node->flag & 4)? 'f': + (node->flag & 2)? 'm': + (node->flag & 1)? 'q':'-'); + print_string (stdout, node->pkt->pkt.user_id->name, len, ':'); + putchar (':'); + putchar ('\n'); + } + } } +} - /* we preserve the disabled state here */ - if( (rec.r.dir.ownertrust & TRUST_FLAG_DISABLED) ) - rec.r.dir.ownertrust = new_trust | TRUST_FLAG_DISABLED; - else - rec.r.dir.ownertrust = new_trust & ~TRUST_FLAG_DISABLED; - write_record( &rec ); -} -/**************** - * Update the ownertrust in the complete tree. - */ static void -propagate_ownertrust( TN kr, ulong lid, unsigned trust ) -{ - TN ur; - - for( ; kr; kr = kr->next ) { - if( kr->lid == lid ) - kr->n.k.ownertrust = trust; - for( ur=kr->list; ur; ur = ur->next ) - propagate_ownertrust( ur->list, lid, trust ); +store_validation_status (int depth, KBNODE keyblock) +{ + KBNODE node; + byte namehash[20]; + int status; + int any = 0; + + for (node=keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + PKT_user_id *uid = node->pkt->pkt.user_id; + + if (node->flag & 4) + status = TRUST_FULLY; + else if (node->flag & 2) + status = TRUST_MARGINAL; + else if (node->flag & 1) + status = TRUST_UNDEFINED; + else + status = 0; + + if (status) + { + if( uid->attrib_data ) + rmd160_hash_buffer (namehash,uid->attrib_data,uid->attrib_len); + else + rmd160_hash_buffer (namehash, uid->name, uid->len ); + + update_validity (keyblock->pkt->pkt.public_key, + namehash, depth, status); + any = 1; + } + } } -} -/**************** - * Calculate the validity of all keys in the tree and especially - * the one of the top key. If add_fnc is not NULL, it is used to - * ask for missing ownertrust values (but only if this will help - * us to increase the validity. - * add_fnc is expected to take the LID of the key under question - * and return a ownertrust value or an error: positive values - * are assumed to be the new ownertrust value; a 0 does mean no change, - * a -1 is a request to cancel this validation procedure, a -2 requests - * a listing of the sub-tree using the tty functions. - * - * - * Returns: 0 = okay + if (any) + do_sync (); +} + +/* + * check whether the signature sig is in the klist k */ -static int -propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ) +static struct key_item * +is_in_klist (struct key_item *k, PKT_signature *sig) { - TN kr, ur; - int max_validity = 0; - - assert( !node->is_uid ); - if( node->n.k.ownertrust == TRUST_ULTIMATE ) { - /* this is one of our keys */ - assert( !node->list ); /* it should be a leaf */ - node->n.k.validity = TRUST_ULTIMATE; - if( retflgs ) - *retflgs |= 1; /* found a path to an ultimately trusted key */ - return 0; + for (; k; k = k->next) + { + if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) + return k; } - - /* loop over all user ids */ - for( ur=node->list; ur && max_validity <= TRUST_FULLY; ur = ur->next ) { - assert( ur->is_uid ); - /* loop over all signators */ - for(kr=ur->list; kr && max_validity <= TRUST_FULLY; kr = kr->next ) { - if( propagate_validity( root, kr, add_fnc, retflgs ) ) - return -1; /* quit */ - if( kr->n.k.validity == TRUST_ULTIMATE ) { - ur->n.u.fully_count = opt.completes_needed; - } - else if( kr->n.k.validity == TRUST_FULLY ) { - if( add_fnc && !kr->n.k.ownertrust ) { - int rc; - - if( retflgs ) - *retflgs |= 2; /* found key with undefined ownertrust*/ - do { - rc = add_fnc( kr->lid ); - switch( rc ) { - case TRUST_NEVER: - case TRUST_MARGINAL: - case TRUST_FULLY: - propagate_ownertrust( root, kr->lid, rc ); - upd_one_ownertrust( kr->lid, rc, retflgs ); - if( retflgs ) - *retflgs |= 4; /* changed */ - break; - case -1: - return -1; /* cancel */ - case -2: - dump_tn_tree( NULL, 0, kr ); - tty_printf("\n"); - break; - default: - break; - } - } while( rc == -2 ); - } - if( kr->n.k.ownertrust == TRUST_FULLY ) - ur->n.u.fully_count++; - else if( kr->n.k.ownertrust == TRUST_MARGINAL ) - ur->n.u.marginal_count++; - } - - if( ur->n.u.fully_count >= opt.completes_needed - || ur->n.u.marginal_count >= opt.marginals_needed ) - ur->n.u.validity = TRUST_FULLY; - else if( ur->n.u.fully_count || ur->n.u.marginal_count ) - ur->n.u.validity = TRUST_MARGINAL; - - if( ur->n.u.validity >= max_validity ) - max_validity = ur->n.u.validity; - } - } - - node->n.k.validity = max_validity; - return 0; + return NULL; } - - -/**************** - * Given the directory record of a key, check whether we can - * find a path to an ultimately trusted key. We do this by - * checking all key signatures up to a some depth. +/* + * Mark the signature of the given UID which are used to certify it. + * To do this, we first revmove all signatures which are not valid and + * from the remain ones we look for the latest one. If this is not a + * certification revocation signature we mark the signature by setting + * node flag bit 8. Note that flag bits 9 and 10 are used for internal + * purposes. */ -static int -verify_key( int max_depth, TRUSTREC *drec, const char *namehash, - int (*add_fnc)(ulong), unsigned *retflgs ) -{ - TN tree; - int keytrust; - int pv_result; - - tree = build_cert_tree( drec->r.dir.lid, 0, opt.max_cert_depth, NULL ); - if( !tree ) - return TRUST_UNDEFINED; - pv_result = propagate_validity( tree, tree, add_fnc, retflgs ); - if( namehash && tree->n.k.validity != TRUST_ULTIMATE ) { - /* find the matching user id. - * We don't do this here if the key is ultimately trusted; in - * this case there will be no lids for the user IDs and frankly - * it does not make sense to compare by the name if we do - * have the secret key. - * fixme: the way we handle this is too inefficient */ - TN ur; - TRUSTREC rec; - - keytrust = 0; - for( ur=tree->list; ur; ur = ur->next ) { - read_record( ur->lid, &rec, RECTYPE_UID ); - if( !memcmp( namehash, rec.r.uid.namehash, 20 ) ) { - keytrust = ur->n.u.validity; - break; - } - } - } - else - keytrust = tree->n.k.validity; - - /* update the cached validity values */ - if( !pv_result - && keytrust >= TRUST_UNDEFINED - && tdbio_db_matches_options() - && ( !drec->r.dir.valcheck || drec->r.dir.validity != keytrust ) ) { - TN ur; - TRUSTREC rec; - - for( ur=tree->list; ur; ur = ur->next ) { - read_record( ur->lid, &rec, RECTYPE_UID ); - if( rec.r.uid.validity != ur->n.u.validity ) { - rec.r.uid.validity = ur->n.u.validity; - write_record( &rec ); - } - } +static void +mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, + u32 *main_kid, struct key_item *klist, + u32 curtime, u32 *next_expire) +{ + KBNODE node; + PKT_signature *sig; + + /* first check all signatures */ + for (node=uidnode->next; node; node = node->next) + { + node->flag &= ~(1<<8 | 1<<9 | 1<<10); + if (node->pkt->pkttype == PKT_USER_ID + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + break; /* ready */ + if (node->pkt->pkttype != PKT_SIGNATURE) + continue; + + sig = node->pkt->pkt.signature; + if (sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) + continue; /* ignore self-signatures */ + if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) + continue; /* we only look at these signature classes */ + if (!is_in_klist (klist, sig)) + continue; /* no need to check it then */ + if (check_key_signature (keyblock, node, NULL)) + continue; /* ignore invalid signatures */ + node->flag |= 1<<9; + } + /* reset the remaining flags */ + for (; node; node = node->next) + node->flag &= ~(1<<8 | 1<<9 | 1 << 10); + + /* kbnode flag usage: bit 9 is here set for signatures to consider, + * bit 10 will be set by the loop to keep track of keyIDs already + * processed, bit 8 will be set for the usable signatures */ + + /* for each cert figure out the latest valid one */ + for (node=uidnode->next; node; node = node->next) + { + KBNODE n, signode; + u32 kid[2]; + u32 sigdate; + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + break; + if ( !(node->flag & (1<<9)) ) + continue; /* not a node to look at */ + if ( (node->flag & (1<<10)) ) + continue; /* signature with a keyID already processed */ + node->flag |= (1<<10); /* mark this node as processed */ + sig = node->pkt->pkt.signature; + signode = node; + sigdate = sig->timestamp; + kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; + for (n=uidnode->next; n; n = n->next) + { + if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) + break; + if ( !(n->flag & (1<<9)) ) + continue; + if ( (n->flag & (1<<10)) ) + continue; /* shortcut already processed signatures */ + sig = n->pkt->pkt.signature; + if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) + continue; + n->flag |= (1<<10); /* mark this node as processed */ + + /* If signode is nonrevocable and unexpired and n isn't, + then take signode (skip). It doesn't matter which is + older: if signode was older then we don't want to take n + as signode is nonrevocable. If n was older then we're + automatically fine. */ + + if(((IS_UID_SIG(signode->pkt->pkt.signature) && + !signode->pkt->pkt.signature->flags.revocable && + (signode->pkt->pkt.signature->expiredate==0 || + signode->pkt->pkt.signature->expiredate>curtime))) && + (!(IS_UID_SIG(n->pkt->pkt.signature) && + !n->pkt->pkt.signature->flags.revocable && + (n->pkt->pkt.signature->expiredate==0 || + n->pkt->pkt.signature->expiredate>curtime)))) + continue; - drec->r.dir.validity = tree->n.k.validity; - drec->r.dir.valcheck = make_timestamp(); - write_record( drec ); - do_sync(); + /* If n is nonrevocable and unexpired and signode isn't, + then take n. Again, it doesn't matter which is older: if + n was older then we don't want to take signode as n is + nonrevocable. If signode was older then we're + automatically fine. */ + + if((!(IS_UID_SIG(signode->pkt->pkt.signature) && + !signode->pkt->pkt.signature->flags.revocable && + (signode->pkt->pkt.signature->expiredate==0 || + signode->pkt->pkt.signature->expiredate>curtime))) && + ((IS_UID_SIG(n->pkt->pkt.signature) && + !n->pkt->pkt.signature->flags.revocable && + (n->pkt->pkt.signature->expiredate==0 || + n->pkt->pkt.signature->expiredate>curtime)))) + { + signode = n; + sigdate = sig->timestamp; + continue; + } + + /* At this point, if it's newer, it goes in as the only + remaining possibilities are signode and n are both either + revocable or expired or both nonrevocable and unexpired. + If the timestamps are equal take the later ordered + packet, presuming that the key packets are hopefully in + their original order. */ + + if (sig->timestamp >= sigdate) + { + signode = n; + sigdate = sig->timestamp; + } + } + sig = signode->pkt->pkt.signature; + if (IS_UID_SIG (sig)) + { /* this seems to be a usable one which is not revoked. + * Just need to check whether there is an expiration time, + * We do the expired certification after finding a suitable + * certification, the assumption is that a signator does not + * want that after the expiration of his certificate the + * system falls back to an older certification which has a + * different expiration time */ + const byte *p; + u32 expire; + + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); + expire = p? sig->timestamp + buffer_to_u32(p) : 0; + + if (expire==0 || expire > curtime ) + { + signode->flag |= (1<<8); /* yeah, found a good cert */ + if (expire && expire < *next_expire) + *next_expire = expire; + } + } } - - release_tn_tree( tree ); - return keytrust; } -/**************** - * we have the pubkey record and all needed informations are in the trustdb - * but nothing more is known. +/* + * Return true if the key is signed by one of the keys in the given + * key ID list. User IDs with a valid signature are marked by node + * flags as follows: + * flag bit 0: There is at least one signature + * 1: There is marginal confidence that this is a legitimate uid + * 2: There is full confidence that this is a legitimate uid. + * 8: Used for internal purposes. + * 9: Ditto (in mark_usable_uid_certs()) + * 10: Ditto (ditto) + * This function assumes that all kbnode flags are cleared on entry. */ static int -do_check( TRUSTREC *dr, unsigned *validity, - const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) -{ - if( !dr->r.dir.keylist ) { - log_error(_("Ooops, no keys\n")); - return GPGERR_TRUSTDB; - } - if( !dr->r.dir.uidlist ) { - log_error(_("Ooops, no user IDs\n")); - return GPGERR_TRUSTDB; +validate_one_keyblock (KBNODE kb, struct key_item *klist, + u32 curtime, u32 *next_expire) +{ + struct key_item *kr; + KBNODE node, uidnode=NULL; + PKT_public_key *pk = kb->pkt->pkt.public_key; + u32 main_kid[2]; + int issigned=0, any_signed = 0, fully_count =0, marginal_count = 0; + + keyid_from_pk(pk, main_kid); + for (node=kb; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + if (uidnode && issigned) + { + if (fully_count >= opt.completes_needed + || marginal_count >= opt.marginals_needed ) + uidnode->flag |= 4; + else if (fully_count || marginal_count) + uidnode->flag |= 2; + uidnode->flag |= 1; + any_signed = 1; + } + uidnode = node; + issigned = 0; + fully_count = marginal_count = 0; + mark_usable_uid_certs (kb, uidnode, main_kid, klist, + curtime, next_expire); + } + else if (node->pkt->pkttype == PKT_SIGNATURE + && (node->flag & (1<<8)) ) + { + PKT_signature *sig = node->pkt->pkt.signature; + + kr = is_in_klist (klist, sig); + if (kr) + { + if (kr->ownertrust == TRUST_ULTIMATE) + fully_count = opt.completes_needed; + else if (kr->ownertrust == TRUST_FULLY) + fully_count++; + else if (kr->ownertrust == TRUST_MARGINAL) + marginal_count++; + issigned = 1; + } + } } - if( retflgs ) - *retflgs &= ~(16|32); /* reset the 2 special flags */ - - if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) - *validity = 0; /* no need to check further */ - else if( namehash ) { - /* Fixme: use a cache */ - *validity = verify_key( opt.max_cert_depth, dr, namehash, - add_fnc, retflgs ); - } - else if( !add_fnc - && tdbio_db_matches_options() - /* FIXME, TODO: This comparision is WRONG ! */ - && dr->r.dir.valcheck - > tdbio_read_modify_stamp( (dr->r.dir.validity < TRUST_FULLY) ) - && dr->r.dir.validity ) - *validity = dr->r.dir.validity; - else - *validity = verify_key( opt.max_cert_depth, dr, NULL, - add_fnc, retflgs ); - - if( !(*validity & TRUST_MASK) ) - *validity = TRUST_UNDEFINED; - - if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) - *validity |= TRUST_FLAG_DISABLED; - - if( dr->r.dir.dirflags & DIRF_REVOKED ) - *validity |= TRUST_FLAG_REVOKED; - - /* If we have changed some ownertrusts, set the trustdb timestamps - * and do a sync */ - if( retflgs && (*retflgs & (16|32)) ) { - tdbio_write_modify_stamp( (*retflgs & 16), (*retflgs & 32) ); - do_sync(); + if (uidnode && issigned) + { + if (fully_count >= opt.completes_needed + || marginal_count >= opt.marginals_needed ) + uidnode->flag |= 4; + else if (fully_count || marginal_count) + uidnode->flag |= 2; + uidnode->flag |= 1; + any_signed = 1; } - - return 0; + return any_signed; } - -/*********************************************** - ********* Change trustdb values ************** - ***********************************************/ - -int -update_ownertrust( ulong lid, unsigned new_trust ) -{ - TRUSTREC rec; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - if( DBG_TRUST ) - log_debug("update_ownertrust of %lu from %u to %u\n", - lid, (unsigned)rec.r.dir.ownertrust, new_trust ); - rec.r.dir.ownertrust = new_trust; - write_record( &rec ); - do_sync(); - return 0; -} - - -int -clear_trust_checked_flag( PKT_public_key *pk ) -{ - TRUSTREC rec; - int rc; - - if( opt.dry_run ) - return 0; - - init_trustdb(); - rc = get_dir_record( pk, &rec ); - if( rc ) - return rc; - - /* check whether they are already reset */ - if( !(rec.r.dir.dirflags & DIRF_CHECKED) && !rec.r.dir.valcheck ) - return 0; - - /* reset the flag */ - rec.r.dir.dirflags &= ~DIRF_CHECKED; - rec.r.dir.valcheck = 0; - write_record( &rec ); - do_sync(); - return 0; -} - - - - - -/*********************************************** - ********* Query trustdb values ************** - ***********************************************/ - - -/**************** - * This function simply looks for the key in the trustdb - * and makes sure that pk->local_id is set to the correct value. - * Return: 0 = found - * -1 = not found - * other = error - */ -int -query_trust_record( PKT_public_key *pk ) +static int +search_skipfnc (void *opaque, u32 *kid) { - TRUSTREC rec; - init_trustdb(); - return get_dir_record( pk, &rec ); + return test_key_hash_table ((KeyHashTable)opaque, kid); } -/**************** - * Get the trustlevel for this PK. - * Note: This does not ask any questions - * Returns: 0 okay of an errorcode - * - * It operates this way: - * locate the pk in the trustdb - * found: - * Do we have a valid cache record for it? - * yes: return trustlevel from cache - * no: make a cache record and all the other stuff - * not found: - * try to insert the pubkey into the trustdb and check again - * - * Problems: How do we get the complete keyblock to check that the - * cache record is actually valid? Think we need a clever - * cache in getkey.c to keep track of this stuff. Maybe it - * is not necessary to check this if we use a local pubring. Hmmmm. +/* + * Scan all keys and return a key_array of all suitable keys from + * kllist. The caller has to pass keydb handle so that we don't use + * to create our own. Returns either a key_array or NULL in case of + * an error. No results found are indicated by an empty array. + * Caller hast to release the returned array. */ -int -check_trust( PKT_public_key *pk, unsigned *r_trustlevel, - const byte *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) -{ - TRUSTREC rec; - unsigned trustlevel = TRUST_UNKNOWN; - int rc=0; - u32 cur_time; - u32 keyid[2]; - - - init_trustdb(); - keyid_from_pk( pk, keyid ); - - /* get the pubkey record */ - if( pk->local_id ) { - read_record( pk->local_id, &rec, RECTYPE_DIR ); - } - else { /* no local_id: scan the trustdb */ - if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) { - log_error(_("check_trust: search dir record failed: %s\n"), - gpg_errstr(rc)); - return rc; - } - else if( rc == -1 && opt.dry_run ) - return GPGERR_GENERAL; - else if( rc == -1 ) { /* not found - insert */ - rc = insert_trust_record_by_pk( pk ); - if( rc ) { - log_error(_("key %08lX: insert trust record failed: %s\n"), - (ulong)keyid[1], gpg_errstr(rc)); - goto leave; - } - log_info(_("key %08lX.%lu: inserted into trustdb\n"), - (ulong)keyid[1], pk->local_id ); - /* and re-read the dir record */ - read_record( pk->local_id, &rec, RECTYPE_DIR ); - } - } - cur_time = make_timestamp(); - if( pk->timestamp > cur_time ) { - log_info(_("key %08lX.%lu: created in future " - "(time warp or clock problem)\n"), - (ulong)keyid[1], pk->local_id ); - if( !opt.ignore_time_conflict ) - return GPGERR_TIME_CONFLICT; - } +static struct key_array * +validate_key_list (KEYDB_HANDLE hd, KeyHashTable visited, + struct key_item *klist, u32 curtime, u32 *next_expire) +{ + KBNODE keyblock = NULL; + struct key_array *keys = NULL; + size_t nkeys, maxkeys; + int rc; + KEYDB_SEARCH_DESC desc; + + maxkeys = 1000; + keys = m_alloc ((maxkeys+1) * sizeof *keys); + nkeys = 0; + + rc = keydb_search_reset (hd); + if (rc) + { + log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc)); + m_free (keys); + return NULL; + } + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FIRST; + desc.skipfnc = search_skipfnc; + desc.skipfncvalue = visited; + rc = keydb_search (hd, &desc, 1); + if (rc == -1) + { + keys[nkeys].keyblock = NULL; + return keys; + } + if (rc) + { + log_error ("keydb_search_first failed: %s\n", g10_errstr(rc)); + m_free (keys); + return NULL; + } + + desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */ + do + { + PKT_public_key *pk; + + rc = keydb_get_keyblock (hd, &keyblock); + if (rc) + { + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + m_free (keys); + return NULL; + } + + if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY) + { + log_debug ("ooops: invalid pkttype %d encountered\n", + keyblock->pkt->pkttype); + dump_kbnode (keyblock); + release_kbnode(keyblock); + continue; + } - if( !(rec.r.dir.dirflags & DIRF_CHECKED) ) - check_trust_record( &rec, 0 ); - else if( rec.r.dir.checkat && rec.r.dir.checkat <= cur_time ) - check_trust_record( &rec, 0 ); - else if( (rec.r.dir.dirflags & DIRF_NEWKEYS) ) - check_trust_record( &rec, 1 ); - - if( pk->expiredate && pk->expiredate <= cur_time ) { - log_info(_("key %08lX.%lu: expired at %s\n"), - (ulong)keyid[1], pk->local_id, - asctimestamp( pk->expiredate) ); - trustlevel = TRUST_EXPIRED; - } - else { - rc = do_check( &rec, &trustlevel, namehash, add_fnc, retflgs ); - if( rc ) { - log_error(_("key %08lX.%lu: trust check failed: %s\n"), - (ulong)keyid[1], pk->local_id, gpg_errstr(rc)); - return rc; - } - } + /* prepare the keyblock for further processing */ + merge_keys_and_selfsig (keyblock); + clear_kbnode_flags (keyblock); + pk = keyblock->pkt->pkt.public_key; + if (pk->has_expired || pk->is_revoked) + { + /* it does not make sense to look further at those keys */ + mark_keyblock_seen (visited, keyblock); + } + else if (validate_one_keyblock (keyblock, klist, curtime, next_expire)) + { + if (pk->expiredate && pk->expiredate >= curtime + && pk->expiredate < *next_expire) + *next_expire = pk->expiredate; + + if (nkeys == maxkeys) { + maxkeys += 1000; + keys = m_realloc (keys, (maxkeys+1) * sizeof *keys); + } + keys[nkeys++].keyblock = keyblock; + /* this key is signed - don't check it again */ + mark_keyblock_seen (visited, keyblock); + keyblock = NULL; + } - /* is a subkey has been requested, we have to check its keyflags */ - if( !rc ) { - TRUSTREC krec; - byte fpr[MAX_FINGERPRINT_LEN] = {0}; /* to avoid compiler warnings */ - size_t fprlen = 0; - ulong recno; - int kcount=0; - - for( recno = rec.r.dir.keylist; recno; recno = krec.r.key.next ) { - read_record( recno, &krec, RECTYPE_KEY ); - if( ++kcount == 1 ) - continue; /* skip the primary key */ - if( kcount == 2 ) /* now we need the fingerprint */ - fingerprint_from_pk( pk, fpr, &fprlen ); - - if( krec.r.key.fingerprint_len == fprlen - && !memcmp( krec.r.key.fingerprint, fpr, fprlen ) ) { - /* found the subkey */ - if( (krec.r.key.keyflags & KEYF_REVOKED) ) - trustlevel |= TRUST_FLAG_SUB_REVOKED; - /* should we check for keybinding here??? */ - /* Hmmm: Maybe this whole checking stuff should not go - * into the trustdb, but be done direct from the keyblock. - * Chnage this all when we add an abstarction layer around - * the way certificates are handled by different standards */ - break; - } - } + release_kbnode (keyblock); + keyblock = NULL; + } + while ( !(rc = keydb_search (hd, &desc, 1)) ); + if (rc && rc != -1) + { + log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); + m_free (keys); + return NULL; } - - leave: - if( DBG_TRUST ) - log_debug("check_trust() returns trustlevel %04x.\n", trustlevel); - *r_trustlevel = trustlevel; - return 0; -} + keys[nkeys].keyblock = NULL; + return keys; +} -/**************** - * scan the whole trustdb and mark all signature records whose keys - * are freshly imported. - */ static void -mark_fresh_keys() -{ - TRUSTREC dirrec, rec; - ulong recnum, lid; - int i; - - memset( &dirrec, 0, sizeof dirrec ); - - for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { - if( rec.rectype != RECTYPE_SIG ) - continue; - /* if we have already have the dir record, we can check it now */ - if( dirrec.recnum == rec.r.sig.lid - && (dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) - continue; /* flag is already set */ - - for(i=0; i < SIGS_PER_RECORD; i++ ) { - if( !(lid=rec.r.sig.sig[i].lid) ) - continue; /* skip deleted sigs */ - if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) ) - continue; /* skip checked signatures */ - if( qry_lid_table_flag( fresh_imported_keys, lid, NULL ) ) - continue; /* not in the list of new keys */ - read_record( rec.r.sig.lid, &dirrec, RECTYPE_DIR ); - if( !(dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) { - dirrec.r.dir.dirflags |= DIRF_NEWKEYS; - write_record( &dirrec ); - } - break; - } - } - - do_sync(); - - clear_lid_table( fresh_imported_keys ); - fresh_imported_keys_count = 0; -} - - - -int -query_trust_info( PKT_public_key *pk, const byte *namehash ) -{ - unsigned trustlevel; - int c; - - init_trustdb(); - if( check_trust( pk, &trustlevel, namehash, NULL, NULL ) ) - return '?'; - if( trustlevel & TRUST_FLAG_DISABLED ) - return 'd'; - if( trustlevel & TRUST_FLAG_REVOKED ) - return 'r'; - c = trust_letter( (trustlevel & TRUST_MASK) ); - if( !c ) - c = '?'; - return c; -} - - - -/**************** - * Return the assigned ownertrust value for the given LID - */ -unsigned -get_ownertrust( ulong lid ) -{ - TRUSTREC rec; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - return rec.r.dir.ownertrust; -} - -int -get_ownertrust_info( ulong lid ) -{ - unsigned otrust; - int c; - - init_trustdb(); - otrust = get_ownertrust( lid ); - c = trust_letter( (otrust & TRUST_MASK) ); - if( !c ) - c = '?'; - return c; -} - - - -void -list_trust_path( const char *username ) -{ - int rc; - ulong lid; - TRUSTREC rec; - TN tree; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - - init_trustdb(); - if( (rc = get_pubkey_byname(NULL, pk, username, NULL )) ) - log_error(_("user '%s' not found: %s\n"), username, gpg_errstr(rc) ); - else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) - log_error(_("problem finding '%s' in trustdb: %s\n"), - username, gpg_errstr(rc)); - else if( rc == -1 ) { - log_info(_("user '%s' not in trustdb - inserting\n"), username); - rc = insert_trust_record_by_pk( pk ); - if( rc ) - log_error(_("failed to put '%s' into trustdb: %s\n"), - username, gpg_errstr(rc)); - else { - assert( pk->local_id ); - } +reset_unconnected_keys (KEYDB_HANDLE hd, KeyHashTable visited) +{ + int rc; + KBNODE keyblock = NULL; + KEYDB_SEARCH_DESC desc; + int count = 0, nreset = 0; + + rc = keydb_search_reset (hd); + if (rc) + { + log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc)); + return; + } + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FIRST; + desc.skipfnc = search_skipfnc; + desc.skipfncvalue = visited; + rc = keydb_search (hd, &desc, 1); + if (rc && rc != -1 ) + log_error ("keydb_search_first failed: %s\n", g10_errstr(rc)); + else if (!rc) + { + desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */ + do + { + rc = keydb_get_keyblock (hd, &keyblock); + if (rc) + { + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + break; + } + count++; + + if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) /* paranoid assertion*/ + { + nreset += clear_validity (keyblock->pkt->pkt.public_key); + release_kbnode (keyblock); + } + } + while ( !(rc = keydb_search (hd, &desc, 1)) ); + if (rc && rc != -1) + log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); } - lid = pk->local_id; - - tree = build_cert_tree( lid, 0, opt.max_cert_depth, NULL ); - if( tree ) - propagate_validity( tree, tree, NULL, NULL ); - if( opt.with_colons ) - dump_tn_tree_with_colons( 0, tree ); - else - dump_tn_tree( stdout, 0, tree ); - /*printf("(alloced tns=%d max=%d)\n", alloced_tns, max_alloced_tns );*/ - release_tn_tree( tree ); - /*printf("Ownertrust=%c Validity=%c\n", get_ownertrust_info( lid ), - query_trust_info( pk, NULL ) ); */ - - free_public_key( pk ); - -} + if (opt.verbose) + log_info ("%d unconnected keys (%d trust records cleared)\n", + count, nreset); + do_sync (); +} - - -/**************** - * Enumerate all keys, which are needed to build all trust paths for - * the given key. This function does not return the key itself or - * the ultimate key (the last point in cerificate chain). Only - * certificate chains which ends up at an ultimately trusted key - * are listed. If ownertrust or validity is not NULL, the corresponding - * value for the returned LID is also returned in these variable(s). +/* + * Run the key validation procedure. * - * 1) create a void pointer and initialize it to NULL - * 2) pass this void pointer by reference to this function. - * Set lid to the key you want to enumerate and pass it by reference. - * 3) call this function as long as it does not return -1 - * to indicate EOF. LID does contain the next key used to build the web - * 4) Always call this function a last time with LID set to NULL, - * so that it can free its context. + * This works this way: + * Step 1: Find all ultimately trusted keys (UTK). + * mark them all as seen and put them into klist. + * Step 2: loop max_cert_times + * Step 3: if OWNERTRUST of any key in klist is undefined + * ask user to assign ownertrust + * Step 4: Loop over all keys in the keyDB which are not marked seen + * Step 5: if key is revoked or expired + * mark key as seen + * continue loop at Step 4 + * Step 6: For each user ID of that key signed by a key in klist + * Calculate validity by counting trusted signatures. + * Set validity of user ID + * Step 7: If any signed user ID was found + * mark key as seen + * End Loop + * Step 8: Build a new klist from all fully trusted keys from step 6 + * End Loop + * Ready * - * Returns: -1 on EOF or the level of the returned LID - */ -int -enum_cert_paths( void **context, ulong *lid, - unsigned *ownertrust, unsigned *validity ) -{ - return -1; - #if 0 - struct enum_cert_paths_ctx *ctx; - fixme: ..... tsl; - - init_trustdb(); - if( !lid ) { /* release the context */ - if( *context ) { - FIXME: ........tsl2; - - ctx = *context; - for(tsl = ctx->tsl_head; tsl; tsl = tsl2 ) { - tsl2 = tsl->next; - gcry_free( tsl ); - } - *context = NULL; - } - return -1; - } - - if( !*context ) { - FIXME .... *tmppath; - TRUSTREC rec; - - if( !*lid ) - return -1; - - ctx = gcry_xcalloc( 1, sizeof *ctx ); - *context = ctx; - /* collect the paths */ - #if 0 - read_record( *lid, &rec, RECTYPE_DIR ); - tmppath = gcry_xcalloc( 1, (opt.max_cert_depth+1)* sizeof *tmppath ); - tsl = NULL; - collect_paths( 0, opt.max_cert_depth, 1, &rec, tmppath, &tsl ); - gcry_free( tmppath ); - sort_tsl_list( &tsl ); - #endif - /* setup the context */ - ctx->tsl_head = tsl; - ctx->tsl = ctx->tsl_head; - ctx->idx = 0; - } - else - ctx = *context; - - while( ctx->tsl && ctx->idx >= ctx->tsl->pathlen ) { - ctx->tsl = ctx->tsl->next; - ctx->idx = 0; - } - tsl = ctx->tsl; - if( !tsl ) - return -1; /* eof */ - - if( ownertrust ) - *ownertrust = tsl->path[ctx->idx].otrust; - if( validity ) - *validity = tsl->path[ctx->idx].trust; - *lid = tsl->path[ctx->idx].lid; - ctx->idx++; - return ctx->idx-1; - #endif -} - - -/**************** - * Print the current path */ -void -enum_cert_paths_print( void **context, FILE *fp, - int refresh, ulong selected_lid ) -{ - return; - #if 0 - struct enum_cert_paths_ctx *ctx; - FIXME......... tsl; - - if( !*context ) - return; - init_trustdb(); - 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.valcheck && 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; +static int +validate_keys (int interactive) +{ + int rc = 0; + int quit=0; + struct key_item *klist = NULL; + struct key_item *k; + struct key_array *keys = NULL; + struct key_array *kar; + KEYDB_HANDLE kdb = NULL; + KBNODE node; + int depth; + int key_count; + int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate; + KeyHashTable visited; + u32 start_time, next_expire; + + start_time = make_timestamp (); + next_expire = 0xffffffff; /* set next expire to the year 2106 */ + visited = new_key_hash_table (); + /* Fixme: Instead of always building a UTK list, we could just build it + * here when needed */ + if (!utk_list) + { + log_info ("no ultimately trusted keys found\n"); + goto leave; + } + + + /* mark all UTKs as visited and set validity to ultimate */ + for (k=utk_list; k; k = k->next) + { + KBNODE keyblock; + PKT_public_key *pk; + + keyblock = get_pubkeyblock (k->kid); + if (!keyblock) + { + log_error (_("public key of ultimately" + " trusted key %08lX not found\n"), (ulong)k->kid[1]); + continue; + } + mark_keyblock_seen (visited, keyblock); + pk = keyblock->pkt->pkt.public_key; + for (node=keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + byte namehash[20]; + PKT_user_id *uid = node->pkt->pkt.user_id; + + if( uid->attrib_data ) + rmd160_hash_buffer (namehash,uid->attrib_data,uid->attrib_len); + else + rmd160_hash_buffer (namehash, uid->name, uid->len ); + update_validity (pk, namehash, 0, TRUST_ULTIMATE); + } + } + if ( pk->expiredate && pk->expiredate >= start_time + && pk->expiredate < next_expire) + next_expire = pk->expiredate; + + release_kbnode (keyblock); + do_sync (); + } + + + klist = utk_list; + kdb = keydb_new (0); + + for (depth=0; depth < opt.max_cert_depth; depth++) + { + /* See whether we should assign ownertrust values to the keys in + utk_list. */ + ot_unknown = ot_undefined = ot_never = 0; + ot_marginal = ot_full = ot_ultimate = 0; + for (k=klist; k; k = k->next) + { + if (interactive && k->ownertrust == TRUST_UNKNOWN) + k->ownertrust = ask_ownertrust (k->kid); + if (k->ownertrust == -1) + { + quit=1; + goto leave; } - } - } + else if (k->ownertrust == TRUST_UNKNOWN) + ot_unknown++; + else if (k->ownertrust == TRUST_UNDEFINED) + ot_undefined++; + else if (k->ownertrust == TRUST_NEVER) + ot_never++; + else if (k->ownertrust == TRUST_MARGINAL) + ot_marginal++; + else if (k->ownertrust == TRUST_FULLY) + ot_full++; + else if (k->ownertrust == TRUST_ULTIMATE) + ot_ultimate++; + } - print_path( tsl->pathlen, tsl->path, fp, selected_lid ); - #endif -} + /* Find all keys which are signed by a key in kdlist */ + keys = validate_key_list (kdb, visited, klist, start_time, &next_expire); + if (!keys) + { + log_error ("validate_key_list failed\n"); + rc = G10ERR_GENERAL; + goto leave; + } -/* - * Return an allocated buffer with the preference values for - * the key with LID and the userid which is identified by the - * HAMEHASH or the first one if namehash is NULL. ret_n receives - * the length of the allocated 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 ) -{ - TRUSTREC rec; - ulong recno; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { - read_record( recno, &rec, RECTYPE_UID ); - if( rec.r.uid.prefrec - && ( !namehash || !memcmp(namehash, rec.r.uid.namehash, 20) )) { - byte *buf; - /* found the correct one or the first one */ - read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); - if( rec.r.pref.next ) - log_info(_("WARNING: can't yet handle long pref records\n")); - buf = gcry_xmalloc( ITEMS_PER_PREF_RECORD ); - memcpy( buf, rec.r.pref.data, ITEMS_PER_PREF_RECORD ); - *ret_n = ITEMS_PER_PREF_RECORD; - return buf; - } + for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++) + ; + + /* Store the calculated valididation status somewhere */ + if (opt.verbose > 1) + dump_key_array (depth, keys); + + log_info (_("checking at depth %d signed=%d" + " ot(-/q/n/m/f/u)=%d/%d/%d/%d/%d/%d\n"), + depth, key_count, ot_unknown, ot_undefined, + ot_never, ot_marginal, ot_full, ot_ultimate ); + + for (kar=keys; kar->keyblock; kar++) + store_validation_status (depth, kar->keyblock); + + /* Build a new kdlist from all fully valid keys in KEYS */ + if (klist != utk_list) + release_key_items (klist); + klist = NULL; + for (kar=keys; kar->keyblock; kar++) + { + for (node=kar->keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID && (node->flag & 4)) + { + k = new_key_item (); + keyid_from_pk (kar->keyblock->pkt->pkt.public_key, k->kid); + k->ownertrust = get_ownertrust (kar->keyblock + ->pkt->pkt.public_key); + k->next = klist; + klist = k; + break; + } + } + } + release_key_array (keys); + keys = NULL; + if (!klist) + break; /* no need to dive in deeper */ + } + + reset_unconnected_keys (kdb, visited); + + leave: + keydb_release (kdb); + release_key_array (keys); + release_key_items (klist); + release_key_hash_table (visited); + if (!rc && !quit) /* mark trustDB as checked */ + { + if (next_expire == 0xffffffff || next_expire < start_time ) + tdbio_write_nextcheck (0); + else + { + tdbio_write_nextcheck (next_expire); + log_info (_("next trustdb check due at %s\n"), + strtimestamp (next_expire)); + } + do_sync (); + pending_check_trustdb = 0; } - return NULL; + return rc; } - -/**************** - * Check whether the algorithm is in one of the pref records - */ -int -is_algo_in_prefs( ulong lid, int preftype, int algo ) -{ - TRUSTREC rec; - ulong recno; - int i; - byte *pref; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { - read_record( recno, &rec, RECTYPE_UID ); - if( rec.r.uid.prefrec ) { - read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); - if( rec.r.pref.next ) - log_info(_("WARNING: can't yet handle long pref records\n")); - pref = rec.r.pref.data; - for(i=0; i+1 < ITEMS_PER_PREF_RECORD; i+=2 ) { - if( pref[i] == preftype && pref[i+1] == algo ) - return 1; - } - } - } - return 0; -} - |