diff options
Diffstat (limited to '')
-rw-r--r-- | g10/trustdb.c | 294 |
1 files changed, 227 insertions, 67 deletions
diff --git a/g10/trustdb.c b/g10/trustdb.c index 610b3b355..f75ccc52f 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,5 +1,5 @@ /* trustdb.c - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -110,7 +110,6 @@ static int alloced_tns; static int max_alloced_tns; - 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 ); @@ -125,13 +124,22 @@ static int do_check( TRUSTREC *drec, unsigned *trustlevel, unsigned *retflgs); static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); static int do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, - int recheck, int *modified ); -static int check_trust_record( 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; @@ -245,6 +253,27 @@ release_lid_table( LOCAL_ID_TABLE tbl ) } #endif + +/**************** + * 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; + } +} + + /**************** * Add a new item to the table or return 1 if we already have this item */ @@ -454,7 +483,7 @@ verify_own_keys(void) if( DBG_TRUST ) log_debug("key %08lX: checking secret key\n", (ulong)keyid[1] ); - if( is_secret_key_protected( sk ) < 1 ) + if( !opt.quiet && is_secret_key_protected( sk ) < 1 ) log_info(_("NOTE: secret key %08lX is NOT protected.\n"), (ulong)keyid[1] ); @@ -572,6 +601,18 @@ init_trustdb() +/**************** + * 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(); +} + /*********************************************** @@ -652,7 +693,7 @@ print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight ) p = get_user_id( keyid, &n ); putc(' ', fp); putc('\"', fp); - print_utf8_string( fp, p, n > 40? 40:n, 0 ); + print_utf8_string( fp, p, n > 40? 40:n ); putc('\"', fp); gcry_free(p); putc('\n', fp ); @@ -683,8 +724,14 @@ print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno ) if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uidpkt = node->pkt->pkt.user_id; - gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, - uidpkt->name, uidpkt->len ); + 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; @@ -1001,7 +1048,7 @@ check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, (ulong)mainkid[1], lid ); assert(keynode->pkt->pkttype == PKT_USER_ID ); uid = keynode->pkt->pkt.user_id; - print_utf8_string( log_stream(), uid->name, uid->len ); + print_string( log_stream(), uid->name, uid->len, '\"' ); fputs("\"\n", log_stream()); } @@ -1083,17 +1130,17 @@ check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, static unsigned int check_sig_record( KBNODE keyblock, KBNODE signode, ulong siglid, int sigidx, u32 *keyid, ulong lid, - u32 *r_expire ) + 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, rc; + 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_expire = 0; + *r_expiretime = 0; if( (sig->sig_class&~3) == 0x10 ) /* regular certification */ ; else if( sig->sig_class == 0x30 ) /* cert revocation */ @@ -1104,7 +1151,8 @@ check_sig_record( KBNODE keyblock, KBNODE signode, 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_expire ); + 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", @@ -1113,18 +1161,25 @@ check_sig_record( KBNODE keyblock, KBNODE signode, 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;*/ + *mod_down = 1; } else - /**mod_up = 1*/; + *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;*/ + *mod_down = 1; if( revocation ) sigflag |= SIGF_REVOKED; } @@ -1138,7 +1193,7 @@ check_sig_record( KBNODE keyblock, KBNODE signode, sigflag |= SIGF_CHECKED; if( revocation ) { sigflag |= SIGF_REVOKED; - /**mod_down = 1;*/ + *mod_down = 1; } } } @@ -1169,14 +1224,15 @@ check_sig_record( KBNODE keyblock, KBNODE signode, */ static ulong make_sig_records( KBNODE keyblock, KBNODE uidnode, - ulong lid, u32 *mainkid, u32 *min_expire ) + 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 expire; + u32 expiretime; srecs = NULL; s_end = &srecs; for( node=uidnode->next; node; node = node->next ) { @@ -1191,6 +1247,12 @@ make_sig_records( KBNODE keyblock, KBNODE uidnode, 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 ) @@ -1219,7 +1281,8 @@ make_sig_records( KBNODE keyblock, KBNODE uidnode, s->r.sig.sig[sigidx].lid = siglid; s->r.sig.sig[sigidx].flag= check_sig_record( keyblock, node, siglid, sigidx, - mainkid, lid, &expire ); + mainkid, lid, &expiretime, + mod_down, mod_up ); sigidx++; if( sigidx == SIGS_PER_RECORD ) { @@ -1229,8 +1292,8 @@ make_sig_records( KBNODE keyblock, KBNODE uidnode, sigidx = 0; } /* keep track of signers pk expire time */ - if( expire && (!*min_expire || *min_expire > expire ) ) - *min_expire = expire; + if( expiretime && (!*min_expire || *min_expire > expiretime ) ) + *min_expire = expiretime; } if( sigidx ) { s->recnum = tdbio_new_recnum(); @@ -1320,7 +1383,8 @@ make_pref_record( PKT_signature *sig, ulong lid ) static ulong -make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire ) +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; @@ -1335,7 +1399,14 @@ make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire ) if( node->pkt->pkttype != PKT_USER_ID ) continue; uid = node->pkt->pkt.user_id; - gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, uid->name, uid->len ); + 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 ); @@ -1352,9 +1423,21 @@ make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire ) && (u->r.uid.uidflags & UIDF_VALID) ) { u->r.uid.prefrec = bestsig? make_pref_record( bestsig, lid ) : 0; } + + /* 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 ); + lid, keyid, min_expire, + mod_down, mod_up ); } uidrecno = urecs? urecs->recnum : 0; @@ -1381,6 +1464,8 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) TRUSTREC drec; int rc; + /* NOTE: We don't need recheck anymore, but this might chnage again in + * the future */ if( opt.dry_run ) return 0; if( modified ) @@ -1391,26 +1476,27 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) if( rc ) return rc; - rc = do_update_trust_record( keyblock, &drec, recheck, modified ); + rc = do_update_trust_record( keyblock, &drec, 0, modified ); return rc; } /**************** - * Same as update_trust_record, but tghis functions expects the dir record. - * On exit the dirrecord will reflect any changes made. + * 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 recheck, int *modified ) + 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; */ + int mod_up = 0; + int mod_down = 0; ulong recno, r2; - u32 expire; + u32 expiretime; primary_pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; if( !primary_pk->local_id ) @@ -1425,7 +1511,7 @@ do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, if( rc ) return rc; - /* delete the old stuff */ + /* 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 ); @@ -1448,22 +1534,13 @@ do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, /* 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; - expire = 0; + drec->r.dir.dirflags |= DIRF_REVOKED; + expiretime = 0; drec->r.dir.uidlist = make_uid_records( keyblock, drec->recnum, keyid, - &expire ); - #if 0 - if( orig_uidflags != urec.r.uid.uidflags ) { - write_record( &urec ); - if( !( urec.r.uid.uidflags & UIDF_VALID ) - || ( urec.r.uid.uidflags & UIDF_REVOKED ) ) - *mod_down=1; - else - *mod_up=1; /*(maybe a new user id)*/ - #endif - + &expiretime, &mod_down, &mod_up ); if( rc ) rc = tdbio_cancel_transaction(); else { @@ -1471,9 +1548,9 @@ do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, *modified = 1; drec->r.dir.dirflags |= DIRF_CHECKED; drec->r.dir.valcheck = 0; - drec->r.dir.checkat = expire; + drec->r.dir.checkat = expiretime; write_record( drec ); - /*tdbio_write_modify_stamp( mod_up, mod_down );*/ + tdbio_write_modify_stamp( mod_up, mod_down ); rc = tdbio_end_transaction(); } return rc; @@ -1538,16 +1615,28 @@ insert_trust_record( KBNODE keyblock ) } } + /* 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, 1, NULL ); + 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 */ @@ -1585,7 +1674,7 @@ insert_trust_record_by_pk( PKT_public_key *pk ) * Currently we only do an update_trust_record. */ static int -check_trust_record( TRUSTREC *drec ) +check_trust_record( TRUSTREC *drec, int sigs_only ) { KBNODE keyblock; int modified, rc; @@ -1597,7 +1686,7 @@ check_trust_record( TRUSTREC *drec ) return rc; } - rc = do_update_trust_record( keyblock, drec, 0, &modified ); + rc = do_update_trust_record( keyblock, drec, sigs_only, &modified ); release_kbnode( keyblock ); return rc; @@ -1674,7 +1763,7 @@ update_trustdb() /**************** - * Do all required check in the trustdb. This function walks over all + * Do all required checks in the trustdb. This function walks over all * records in the trustdb and does scheduled processing. */ void @@ -1682,7 +1771,7 @@ check_trustdb( const char *username ) { TRUSTREC rec; ulong recnum; - ulong count=0, upd_count=0, err_count=0, skip_count=0; + ulong count=0, upd_count=0, err_count=0, skip_count=0, sigonly_count=0; ulong current_time = make_timestamp(); if( username ) @@ -1691,15 +1780,25 @@ check_trustdb( const char *username ) 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++; - if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) { - skip_count++; - continue; /* not scheduled for checking */ + 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 ) { @@ -1708,11 +1807,12 @@ check_trustdb( const char *username ) continue; } - check_trust_record( &rec ); - + check_trust_record( &rec, sigs_only ); } 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 ) @@ -1772,8 +1872,12 @@ build_cert_tree( ulong lid, int depth, int max_depth, TN helproot ) return NULL; } - if( dirrec.r.dir.checkat && dirrec.r.dir.checkat <= make_timestamp() ) - check_trust_record( &dirrec ); + 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; @@ -1924,10 +2028,10 @@ propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ) } /* loop over all user ids */ - for( ur=node->list; ur && max_validity < TRUST_FULLY; ur = ur->next ) { + 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 ) { + 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 ) { @@ -2001,8 +2105,12 @@ verify_key( int max_depth, TRUSTREC *drec, const char *namehash, if( !tree ) return TRUST_UNDEFINED; pv_result = propagate_validity( tree, tree, add_fnc, retflgs ); - if( namehash ) { + 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; @@ -2075,6 +2183,7 @@ do_check( TRUSTREC *dr, unsigned *validity, } 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 ) @@ -2240,10 +2349,16 @@ check_trust( PKT_public_key *pk, unsigned *r_trustlevel, log_info(_("key %08lX.%lu: created in future " "(time warp or clock problem)\n"), (ulong)keyid[1], pk->local_id ); - return GPGERR_TIME_CONFLICT; + if( !opt.ignore_time_conflict ) + return GPGERR_TIME_CONFLICT; } - if( rec.r.dir.checkat && rec.r.dir.checkat <= cur_time ) - check_trust_record( &rec ); + + 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"), @@ -2299,6 +2414,51 @@ check_trust( PKT_public_key *pk, unsigned *r_trustlevel, } +/**************** + * 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 ) { @@ -2532,7 +2692,7 @@ enum_cert_paths_print( void **context, FILE *fp, /* * Return an allocated buffer with the preference values for * the key with LID and the userid which is identified by the - * HAMEHASH or the firstone if namehash is NULL. ret_n receives + * 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 |