diff --git a/TODO b/TODO index b5cd73422..73ed027fb 100644 --- a/TODO +++ b/TODO @@ -4,6 +4,9 @@ * invalid packets (Marco) + * add some sanity checks to read_keyblock, so that we are sure that + the minimal requirements are met (?) + * what about the CR,LF in cleartext singatures? * add option --restore-ownertrust diff --git a/g10/keydb.h b/g10/keydb.h index a0398092a..51da9a628 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -130,7 +130,7 @@ unsigned nbits_from_sk( PKT_secret_key *sk ); const char *datestr_from_pk( PKT_public_key *pk ); const char *datestr_from_sk( PKT_secret_key *sk ); const char *datestr_from_sig( PKT_signature *sig ); -byte *fingerprint_from_sk( PKT_secret_key *sk, byte *buf. size_t *ret_len ); +byte *fingerprint_from_sk( PKT_secret_key *sk, byte *buf; size_t *ret_len ); byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len ); /*-- kbnode.c --*/ diff --git a/g10/sig-check.c b/g10/sig-check.c index f600a4d1e..1dda44529 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -250,7 +250,7 @@ hash_uid_node( KBNODE unode, MD_HANDLE md, PKT_signature *sig ) /**************** * check the signature pointed to by NODE. This is a key signature. * If the function detects a self-signature, it uses the PK from - * NODE and does not read any public key. + * ROOT and does not read any public key. */ int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) diff --git a/g10/tdbio.c b/g10/tdbio.c index 425e51cbb..9355f4c6e 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -287,6 +287,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) ); return G10ERR_READ_FILE; } + rec->recnum = recnum; p = buf; rec->rectype = *p++; if( expected && rec->rectype != expected ) { @@ -383,13 +384,15 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) /**************** * Write the record at RECNUM + * FIXME: create/update keyhash record. */ int -tdbio_write_record( ulong recnum, TRUSTREC *rec ) +tdbio_write_record( TRUSTREC *rec ) { byte buf[TRUST_RECORD_LEN], *p; int rc = 0; int i, n; + ulong recnum = rec->recnum; if( db_fd == -1 ) open_db(); @@ -506,6 +509,7 @@ tdbio_new_recnum() * The local_id of PK is set to the correct value * * Note: To increase performance, we could use a index search here. + * tdbio_write_record shoudl create this index automagically */ int tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec ) diff --git a/g10/tdbio.h b/g10/tdbio.h index 7229d7222..2d28131cd 100644 --- a/g10/tdbio.h +++ b/g10/tdbio.h @@ -42,7 +42,11 @@ struct trust_record { int rectype; - struct trust_record *next; + struct trust_record *next; /* help pointer to build lists in memory */ + struct trust_record *help_pref; + struct trust_record *help_sig; + int mark; + ulong recnum; union { struct { /* version record: */ byte version; /* should be 1 */ @@ -72,7 +76,7 @@ struct trust_record { struct { /* user id reord */ ulong lid; /* point back to the directory record */ ulong next; /* points to next user id record */ - ulong prefrec; /* recno of reference record */ + ulong prefrec; /* recno of preference record */ ulong siglist; /* list of valid signatures (w/o self-sig)*/ byte namehash[20]; /* ripemd hash of the username */ } uid; @@ -127,7 +131,7 @@ int tdbio_set_dbname( const char *new_dbname, int create ); const char *tdbio_get_dbname(void); void tdbio_dump_record( ulong rnum, TRUSTREC *rec, FILE *fp ); int tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ); -int tdbio_write_record( ulong recnum, TRUSTREC *rec ); +int tdbio_write_record( TRUSTREC *rec ); ulong tdbio_new_recnum(void); int tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec ); int tdbio_update_sigflag( ulong lid, int sigflag ); diff --git a/g10/trustdb.c b/g10/trustdb.c index 593e12cd2..b2e478ceb 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -259,7 +259,7 @@ walk_sigrecs( SIGREC_CONTEXT *c, int create ) rc = build_sigrecs( c->local_id ); if( rc ) { if( rc == G10ERR_BAD_CERT ) - rc = -1; /* maybe no selcficnature */ + rc = -1; /* maybe no selfsignature */ if( rc != -1 ) log_info(_("%lu: error building sigs on the fly: %s\n"), c->local_id, g10_errstr(rc) ); @@ -1441,6 +1441,31 @@ query_trust_record( PKT_public_key *pk ) } + +/**************** + * helper function for insert_trust_record() + */ +static void +rel_mem_uidnode( u32 *keyid, int err, TRUSTREC *rec ) +{ + TRUSTREC *r, *r2; + + if( err ) + log_error("key %08lX, uid %02X%02X: invalid user id - removed\n", + (ulong)keyid[1], rec->r.uid.namehash[18], rec->r.uid.namehash[19] ); + for(r=rec->help_pref; r; r = r2 ) { + r2 = r->next; + m_free(r); + } + for(r=rec->help_sig; r; r = r2 ) { + r2 = r->next; + m_free(r); + } + + m_free(rec); +} + + /**************** * Insert a trust record into the TrustDB * This function fails if this record already exists. @@ -1448,17 +1473,20 @@ query_trust_record( PKT_public_key *pk ) int insert_trust_record( PKT_public_key *orig_pk ) { - TRUSTREC dirrec, *rec; - TRUSTREC **keylist_tail, *keylist; - TRUSTREC **uidlist_tail, *uidlist; + TRUSTREC dirrec, *rec, *rec2; + TRUSTREC *keylist_head, **keylist_tail; + TRUSTREC *uidlist_head, **uidlist_tail, uidlist; KBNODE keyblock = NULL; KBNODE node; - u32 keyid[2]; + u32 keyid[2]; /* of primary key */ ulong knum, dnum; byte *fingerprint; size_t fingerlen; int rc = 0; + /* prepare dir record */ + memset( &dirrec, 0, sizeof dirrec ); + dirrec.rectype = RECTYPE_DIR; if( orig_pk->local_id ) log_bug("pk->local_id=%lu\n", (ulong)pk->local_id ); @@ -1474,124 +1502,190 @@ insert_trust_record( PKT_public_key *orig_pk ) if( rc ) { /* that should never happen */ log_error( "insert_trust_record: keyblock not found: %s\n", g10_errstr(rc) ); - return rc; + goto leave; } - /* prepare dir record */ - memset( &dirrec, 0, sizeof dirrec ); - dirrec.rectype = RECTYPE_DIR; - dirrec.r.dir.lid = tdbio_new_recnum(); - - keylist = NULL; - keylist_tail = &dirrec.r.dir.keylist; + /* build data structure as linked lists in memory */ + keylist_head = NULL; keylist_tail = &keylist_head; + uidlist_head = NULL; uidlist_tail = &uidlist_head; uidlist = NULL; - uidlist_tail = &dirrec.r.dir.uidlist; - /* loop over the keyblock */ + keyid[0] = keyid[1] = 0; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *pk = node->pkt->pkt.public_key; - if( keylist && node->pkt->pkttype == PKT_PUBLIC_KEY ) - BUG(); /* more than one primary key */ + if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { + if( keylist_head ) + BUG(); /* more than one primary key */ + keyid_from_pk( pk, keyid ); + } fingerprint = fingerprint_from_pk( orig_pk, &fingerlen ); rec = m_alloc_clear( sizeof *rec ); + rec->rectype = RECTYPE_KEY; rec->r.key.pubkey_algo = pk->pubkey_algo; rec->r.key.fingerprint_len = fingerlen; memcpy(rec->r.key.fingerprint, fingerprint, fingerlen ); - if( keylist ) - keylist_tail = &keylist->next; - *keylist_tail = keylist = rec; + *keylist_tail = rec; keylist_tail = &rec->next; } else if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = node->pkt->pkt.user_id; rec = m_alloc_clear( sizeof *rec ); + rec->rectype = RECTYPE_UID; rmd160_hash_buffer( rec->r.uid.namehash, uid->name, uid->len ); - if( uidlist ) - uidlist_tail = &uidlist->next; - *uidlist_tail = uidlist = rec; + uidlist = rec; + *uidlist_tail = rec; uidlist_tail = &rec->next; } - if( node->pkt->pkttype == PKT_SIGNATURE - && ( (node->pkt->pkt.signature->sig_class&~3) == 0x10 - || node->pkt->pkt.signature->sig_class == 0x20 - || node->pkt->pkt.signature->sig_class == 0x30) ) { - int selfsig; - rc = check_key_signature( keyblock, node, &selfsig ); - if( !rc ) { - rc = set_signature_packets_local_id( node->pkt->pkt.signature ); - if( rc ) - log_fatal("set_signature_packets_local_id failed: %s\n", - g10_errstr(rc)); - if( selfsig ) { - node->flag |= 2; /* mark signature valid */ - *selfsig_okay = 1; - } - else if( node->pkt->pkt.signature->sig_class == 0x20 ) - *revoked = 1; - else - node->flag |= 1; /* mark signature valid */ + else if( node->pkt->pkttype == PKT_SIGNATURE ) { + PKT_signature *sig = node->pkt->pkt.signature; - if( node->pkt->pkt.signature->sig_class != 0x20 ) { - if( !dups ) - dups = new_lid_table(); - if( ins_lid_table_item( dups, - node->pkt->pkt.signature->local_id, 0) ) - node->flag |= 4; /* mark as duplicate */ + if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] + && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) { + /* must verify this selfsignature here, so that we can + * build the preference record and validate the uid record + */ + if( !uidlist ) { + log_error("key %08lX: self-signature without user id\n", + (ulong)keyid[1] ); + } + else if( (rc = check_key_signature( keyblock, node, NULL ))) { + log_error("key %08lX, uid %02X%02X: " + "invalid self-signature: %s\n", + (ulong)keyid[1], uidlist->namehash[18], + uidlist->namehash[19], g10_errstr(rc) ); + rc = 0; + } + else { /* build the prefrecord */ + assert(uidlist); + uidlist->mark |= 1; /* mark valid */ } } - if( DBG_TRUST ) - log_debug("trustdb: sig from %08lX.%lu: %s%s\n", - (ulong)node->pkt->pkt.signature->keyid[1], - node->pkt->pkt.signature->local_id, - g10_errstr(rc), (node->flag&4)?" (dup)":"" ); + else if( 0 /* is revocation sig etc */ ) { + /* handle it here */ + } + else { /* not a selfsignature */ + /* put all this sigs into a list and mark them as unchecked + * we can't check here because we probably have not + * all keys of other signators - we do it on deman + */ + } } } - - - - - - - - - - knum = tdbio_new_recnum(); - /* build dir record */ - memset( &rec, 0, sizeof rec ); - rec.rectype = RECTYPE_DIR; - rec.r.dir.local_id = dnum; - rec.r.dir.keyid[0] = keyid[0]; - rec.r.dir.keyid[1] = keyid[1]; - rec.r.dir.keyrec = knum; - rec.r.dir.no_sigs = 0; - /* and the key record */ - memset( &rec, 0, sizeof rec ); - rec.rectype = RECTYPE_KEY; - rec.r.key.owner = dnum; - rec.r.key.keyid[0] = keyid[0]; - rec.r.key.keyid[1] = keyid[1]; - rec.r.key.pubkey_algo = pk->pubkey_algo; - rec.r.key.fingerprint_len = fingerlen; - memcpy(rec.r.key.fingerprint, fingerprint, fingerlen ); - rec.r.key.ownertrust = 0; - if( tdbio_write_record( knum, &rec ) ) { - log_error("wrinting key record failed\n"); - return G10ERR_TRUSTDB; + /* delete all invalid marked userids and their preferences and sigs */ + /* (ugly code - I know) */ + while( (rec=uidlist_head) && !(rec->mark & 1) ) { + uidlist_head = rec->next; + rel_mem_uidnode(keyid, 1, rec); + } + for( ; rec; rec = rec->next ) { + if( rec->next && !(rec->next->mark & 1) ) { + TRUSTREC *r = rec->next; + rec->next = r->next; + rel_mem_uidnode(keyid, 1, r); + } } + /* check that we have at least one userid */ + if( !uidlist_head ) { + log_error("key %08lX: no user ids - rejected\n", (ulong)keyid[1] ); + rc = G10ERR_BAD_CERT, + goto leave; + } + + /* insert the record numbers to build the real (on disk) list */ + /* fixme: should start a transaction here */ + dirrec.recnum = tdbio_new_recnum(); + dirrec.r.dir.lid = dirrec.recnum; + /* fixme: how do we set sigflag???*/ + /* (list of keys) */ + for(rec=keylist_head; rec; rec = rec->next ) { + rec->r.key.lid = dirrec.recnum; + rec->recnum = tdbio_new_recnum(); + } + for(rec=keylist_head; rec; rec = rec->next ) + rec->r.key.next = rec->next? rec->next->recnum : 0; + dirrec.r.dir.keylist = keylist_head->recnum; + /* (list of user ids) */ + for(rec=uidlist_head; rec; rec = rec->next ) { + rec->r.uid.lid = dirrec.recnum; + rec->recnum = tdbio_new_recnum(); + /* (preference records) */ + for( rec2 = rec->help_pref; rec2; rec2 = rec2->next ) { + rec2->r.pref.lid = dirrec.recnum; + rec2->recnum = tdbio_new_recnum(); + } + for( rec2 = rec->help_pref; rec2; rec2 = rec2->next ) + rec2->r.pref.next = rec2->next? rec2->next->recnum : 0; + rec->r.uid.prefrec = rec->help_pref->recnum; + /* (signature list) */ + for( rec2 = rec->help_sig; rec2; rec2 = rec2->next ) { + rec2->r.sig.lid = dirrec.recnum; + rec2->recnum = tdbio_new_recnum(); + } + for( rec2 = rec->help_sig; rec2; rec2 = rec2->next ) + rec2->r.sig.next = rec2->next? rec2->next->recnum : 0; + rec->r.uid.siglist = rec->help_sig->recnum; + } + for(rec=uidlist_head; rec; rec = rec->next ) + rec->r.uid.next = rec->next? rec->next->recnum : 0; + dirrec.r.dir.uidlist = uidlist_head->recnum; + + /* write all records */ + for(rec=keylist_head; rec; rec = rec->next ) { + assert( rec->rectype == RECTYPE_KEY ); + if( tdbio_write_record( rec ) ) { + log_error("writing key record failed\n"); + rc = G10ERR_TRUSTDB; + goto leave; + } + } + for(rec=uidlist_head; rec; rec = rec->next ) { + assert( rec->rectype == RECTYPE_UID ); + if( tdbio_write_record( rec ) ) { + log_error("writing uid record failed\n"); + rc = G10ERR_TRUSTDB; + goto leave; + } + for( rec2=rec->help_pref; rec2; rec2 = rec2->next ); { + assert( rec2->rectype == RECTYPE_PREF ); + if( tdbio_write_record( rec2 ) ) { + log_error("writing pref record failed\n"); + rc = G10ERR_TRUSTDB; + goto leave; + } + } + for( rec2=rec->help_sig; rec2; rec2 = rec2->next ); { + assert( rec2->rectype == RECTYPE_SIG ); + if( tdbio_write_record( rec2 ) ) { + log_error("writing sig record failed\n"); + rc = G10ERR_TRUSTDB; + goto leave; + } + } + } if( tdbio_write_record( dirrec.r.dir.lid, &dirrec ) ) { log_error("writing dir record failed\n"); return G10ERR_TRUSTDB; } /* and store the LID */ - orig_pk->local_id = dnum; + orig_pk->local_id = dirrec.r.dir.lid; - return 0; + leave: + for( rec=dirrec.r.dir.uidlist; rec; rec = rec2 ) { + rec2 = rec->next; + rel_mem_uidnode(rec); + } + for( rec=dirrec.r.dir.keylist; rec; rec = rec2 ) { + rec2 = rec->next; + m_free(rec); + } + + return rc; } diff --git a/tools/mk-tdata b/tools/mk-tdata index 3b9e1c016..7fcfb389f 100755 Binary files a/tools/mk-tdata and b/tools/mk-tdata differ