diff options
Diffstat (limited to '')
-rw-r--r-- | g10/ChangeLog | 12 | ||||
-rw-r--r-- | g10/Makefile.am | 1 | ||||
-rw-r--r-- | g10/armor.c | 1 | ||||
-rw-r--r-- | g10/g10.c | 6 | ||||
-rw-r--r-- | g10/getkey.c | 162 | ||||
-rw-r--r-- | g10/kbnode.c | 36 | ||||
-rw-r--r-- | g10/keyedit.c | 6 | ||||
-rw-r--r-- | g10/keyid.c | 2 | ||||
-rw-r--r-- | g10/mainproc.c | 10 | ||||
-rw-r--r-- | g10/revoke.c | 26 | ||||
-rw-r--r-- | g10/ringedit.c | 5 | ||||
-rw-r--r-- | g10/tdbio.c | 549 | ||||
-rw-r--r-- | g10/tdbio.h | 31 | ||||
-rw-r--r-- | g10/trustdb.c | 1335 | ||||
-rw-r--r-- | g10/trustdb.h | 1 |
15 files changed, 1304 insertions, 879 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog index 6e420e5e1..93a21e241 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,15 @@ +Thu Oct 8 11:35:51 1998 Werner Koch ([email protected]) + + * armor.c (is_armored): Ad test on old comment packets. + + * tdbio.c (tdbio_search_dir_bypk): fixed memory leak. + + * getkey.c: Changed the caching algorithms. + +Wed Oct 7 19:33:28 1998 Werner Koch ([email protected]) + + * kbnodes.c (unused_nodes): New. + Wed Oct 7 11:15:36 1998 Werner Koch ([email protected]) * keyedit.c (sign_uids): Fixed a problem with SK which could caused diff --git a/g10/Makefile.am b/g10/Makefile.am index b38037d28..da04fece3 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -86,3 +86,4 @@ g10maint.o: $(srcdir)/g10.c $(PROGRAMS): $(needed_libs) + diff --git a/g10/armor.c b/g10/armor.c index c72526a98..df67712a4 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -165,6 +165,7 @@ is_armored( byte *buf ) case PKT_PUBKEY_ENC: case PKT_SIGNATURE: case PKT_COMMENT: + case PKT_OLD_COMMENT: case PKT_PLAINTEXT: case PKT_COMPRESSED: case PKT_ENCRYPTED: @@ -41,6 +41,7 @@ #include "ttyio.h" #include "i18n.h" #include "status.h" +#include "g10defs.h" #ifndef IS_G10MAINT #define IS_G10 1 @@ -503,8 +504,8 @@ main( int argc, char **argv ) log_set_name("gpg"); secure_random_alloc(); /* put random number into secure memory */ disable_core_dumps(); - init_signals(); #endif + init_signals(); i18n_init(); opt.compress = -1; /* defaults to standard compress level */ /* fixme: set the next two to zero and decide where used */ @@ -571,7 +572,6 @@ main( int argc, char **argv ) /* Okay, we are now working under our real uid */ #endif - if( default_config ) configname = make_filename(opt.homedir, "options", NULL ); @@ -1232,6 +1232,8 @@ main( int argc, char **argv ) void g10_exit( int rc ) { + if( opt.debug & DBG_MEMSTAT_VALUE ) + m_print_stats("on exit"); if( opt.debug ) secmem_dump_stats(); secmem_term(); diff --git a/g10/getkey.c b/g10/getkey.c index 886d53cc9..6e4c34592 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -33,27 +33,11 @@ #include "main.h" #include "i18n.h" -#define MAX_PK_CACHE_ENTRIES 500 +#define MAX_UNK_CACHE_ENTRIES 1000 +#define MAX_PK_CACHE_ENTRIES 50 +#define MAX_UID_CACHE_ENTRIES 50 -typedef struct keyid_list { - struct keyid_list *next; - u32 keyid[2]; -} *keyid_list_t; - -typedef struct user_id_db { - struct user_id_db *next; - u32 keyid[2]; - int len; - char name[1]; -} *user_id_db_t; - -typedef struct pk_cache_entry { - struct pk_cache_entry *next; - u32 keyid[2]; - PKT_public_key *pk; -} *pk_cache_entry_t; - typedef struct enum_seckey_context { int eof; STRLIST sl; @@ -64,10 +48,39 @@ typedef struct enum_seckey_context { static STRLIST keyrings; static STRLIST secret_keyrings; -static keyid_list_t unknown_keyids; +#if MAX_UNK_CACHE_ENTRIES + typedef struct keyid_list { + struct keyid_list *next; + u32 keyid[2]; + } *keyid_list_t; + static keyid_list_t unknown_keyids; + static int unk_cache_entries; /* number of entries in unknown keys cache */ + static int unk_cache_disabled; +#endif + +#if MAX_PK_CACHE_ENTRIES + typedef struct pk_cache_entry { + struct pk_cache_entry *next; + u32 keyid[2]; + PKT_public_key *pk; + } *pk_cache_entry_t; + static pk_cache_entry_t pk_cache; + static int pk_cache_entries; /* number of entries in pk cache */ + static int pk_cache_disabled; +#endif + +#if MAX_UID_CACHE_ENTRIES < 5 + #error we really need the userid cache +#endif +typedef struct user_id_db { + struct user_id_db *next; + u32 keyid[2]; + int len; + char name[1]; +} *user_id_db_t; static user_id_db_t user_id_db; -static pk_cache_entry_t pk_cache; -static int pk_cache_entries; /* number of entries in pk cache */ +static int uid_cache_entries; /* number of entries in uid cache */ + static int lookup( PKT_public_key *pk, @@ -166,9 +179,13 @@ add_secret_keyring( const char *name ) static void cache_public_key( PKT_public_key *pk ) { + #if MAX_PK_CACHE_ENTRIES pk_cache_entry_t ce; u32 keyid[2]; + if( pk_cache_disabled ) + return; + if( is_ELGAMAL(pk->pubkey_algo) || pk->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(pk->pubkey_algo) ) { @@ -184,24 +201,20 @@ cache_public_key( PKT_public_key *pk ) return; } - if( pk_cache_entries > MAX_PK_CACHE_ENTRIES ) { - /* FIMXE: use another algorithm to free some cache slots */ - if( pk_cache_entries == MAX_PK_CACHE_ENTRIES ) { - pk_cache_entries++; - log_info("too many entries in pk cache - disabled\n"); - } - ce = pk_cache; - free_public_key( ce->pk ); - } - else { - pk_cache_entries++; - ce = m_alloc( sizeof *ce ); - ce->next = pk_cache; - pk_cache = ce; + if( pk_cache_entries >= MAX_PK_CACHE_ENTRIES ) { + /* fixme: use another algorithm to free some cache slots */ + pk_cache_disabled=1; + log_info("too many entries in pk cache - disabled\n"); + return; } + pk_cache_entries++; + ce = m_alloc( sizeof *ce ); + ce->next = pk_cache; + pk_cache = ce; ce->pk = copy_public_key( NULL, pk ); ce->keyid[0] = keyid[0]; ce->keyid[1] = keyid[1]; + #endif } @@ -220,6 +233,13 @@ cache_user_id( PKT_user_id *uid, u32 *keyid ) return; } + if( uid_cache_entries >= MAX_UID_CACHE_ENTRIES ) { + /* fixme: use another algorithm to free some cache slots */ + r = user_id_db; + user_id_db = r->next; + m_free(r); + uid_cache_entries--; + } r = m_alloc( sizeof *r + uid->len-1 ); r->keyid[0] = keyid[0]; r->keyid[1] = keyid[1]; @@ -227,6 +247,7 @@ cache_user_id( PKT_user_id *uid, u32 *keyid ) memcpy(r->name, uid->name, r->len); r->next = user_id_db; user_id_db = r; + uid_cache_entries++; } @@ -239,23 +260,30 @@ cache_user_id( PKT_user_id *uid, u32 *keyid ) int get_pubkey( PKT_public_key *pk, u32 *keyid ) { - keyid_list_t kl; int internal = 0; int rc = 0; - pk_cache_entry_t ce; - /* let's see whether we checked the keyid already */ - for( kl = unknown_keyids; kl; kl = kl->next ) - if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] ) - return G10ERR_NO_PUBKEY; /* already checked and not found */ - - /* Try to get it from our cache */ - for( ce = pk_cache; ce; ce = ce->next ) - if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) { - if( pk ) - copy_public_key( pk, ce->pk ); - return 0; + #if MAX_UNK_CACHE_ENTRIES + { /* let's see whether we checked the keyid already */ + keyid_list_t kl; + for( kl = unknown_keyids; kl; kl = kl->next ) + if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] ) + return G10ERR_NO_PUBKEY; /* already checked and not found */ + } + #endif + + #if MAX_PK_CACHE_ENTRIES + { /* Try to get it from the cache */ + pk_cache_entry_t ce; + for( ce = pk_cache; ce; ce = ce->next ) { + if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) { + if( pk ) + copy_public_key( pk, ce->pk ); + return 0; + } } + } + #endif /* more init stuff */ if( !pk ) { pk = m_alloc_clear( sizeof *pk ); @@ -268,12 +296,24 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) if( !rc ) goto leave; + #if MAX_UNK_CACHE_ENTRIES /* not found: store it for future reference */ - kl = m_alloc( sizeof *kl ); - kl->keyid[0] = keyid[0]; - kl->keyid[1] = keyid[1]; - kl->next = unknown_keyids; - unknown_keyids = kl; + if( unk_cache_disabled ) + ; + else if( ++unk_cache_entries > MAX_UNK_CACHE_ENTRIES ) { + unk_cache_disabled = 1; + log_info("too many entries in unk cache - disabled\n"); + } + else { + keyid_list_t kl; + + kl = m_alloc( sizeof *kl ); + kl->keyid[0] = keyid[0]; + kl->keyid[1] = keyid[1]; + kl->next = unknown_keyids; + unknown_keyids = kl; + } + #endif rc = G10ERR_NO_PUBKEY; leave: @@ -829,9 +869,10 @@ lookup( PKT_public_key *pk, int mode, u32 *keyid, break; } else if( mode == 16 || mode == 20 ) { + byte afp[MAX_FINGERPRINT_LEN]; size_t an; - byte *afp = fingerprint_from_pk( - k->pkt->pkt.public_key, NULL, &an ); + + fingerprint_from_pk(k->pkt->pkt.public_key, afp, &an ); if( DBG_CACHE ) { u32 aki[2]; @@ -845,10 +886,8 @@ lookup( PKT_public_key *pk, int mode, u32 *keyid, && ( !pk->pubkey_algo || pk->pubkey_algo == k->pkt->pkt.public_key->pubkey_algo) ) { - m_free(afp); break; } - m_free(afp); } else BUG(); @@ -1008,16 +1047,15 @@ lookup_sk( PKT_secret_key *sk, int mode, u32 *keyid, const char *name, } else if( mode == 16 || mode == 20 ) { size_t an; - byte *afp = fingerprint_from_sk( - k->pkt->pkt.secret_key, NULL, &an ); + byte afp[MAX_FINGERPRINT_LEN]; + + fingerprint_from_sk(k->pkt->pkt.secret_key, afp, &an ); if( an == mode && !memcmp( afp, name, an) && ( !sk->pubkey_algo || sk->pubkey_algo == k->pkt->pkt.secret_key->pubkey_algo) ) { - m_free(afp); break; } - m_free(afp); } else BUG(); diff --git a/g10/kbnode.c b/g10/kbnode.c index 428768105..18b16d0bd 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -28,12 +28,20 @@ #include "packet.h" #include "keydb.h" +#define USE_UNUSED_NODES 1 +static KBNODE unused_nodes; KBNODE new_kbnode( PACKET *pkt ) { - KBNODE n = m_alloc( sizeof *n ); + KBNODE n; + + n = unused_nodes; + if( n ) + unused_nodes = n->next; + else + n = m_alloc( sizeof *n ); n->next = NULL; n->pkt = pkt; n->flag = 0; @@ -45,7 +53,13 @@ new_kbnode( PACKET *pkt ) KBNODE clone_kbnode( KBNODE node ) { - KBNODE n = m_alloc( sizeof *n ); + KBNODE n; + + n = unused_nodes; + if( n ) + unused_nodes = n->next; + else + n = m_alloc( sizeof *n ); n->next = NULL; n->pkt = node->pkt; n->flag = 0; @@ -61,9 +75,16 @@ release_kbnode( KBNODE n ) while( n ) { n2 = n->next; - if( !(n->private_flag & 2) ) + if( !(n->private_flag & 2) ) { free_packet( n->pkt ); + m_free( n->pkt ); + } + #if USE_UNUSED_NODES + n->next = unused_nodes; + unused_nodes = n; + #else m_free( n ); + #endif n = n2; } } @@ -230,9 +251,16 @@ commit_kbnode( KBNODE *root ) *root = nl = n->next; else nl->next = n->next; - if( !(n->private_flag & 2) ) + if( !(n->private_flag & 2) ) { free_packet( n->pkt ); + m_free( n->pkt ); + } + #if USE_UNUSED_NODES + n->next = unused_nodes; + unused_nodes = n; + #else m_free( n ); + #endif changed = 1; } else diff --git a/g10/keyedit.c b/g10/keyedit.c index 6f9c1c7c1..ea64d3870 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -902,10 +902,11 @@ show_key_and_fingerprint( KBNODE keyblock ) static void show_fingerprint( PKT_public_key *pk ) { - byte *array, *p; + byte array[MAX_FINGERPRINT_LEN], *p; size_t i, n; - p = array = fingerprint_from_pk( pk, NULL, &n ); + fingerprint_from_pk( pk, array, &n ); + p = array; tty_printf(" Fingerprint:"); if( n == 20 ) { for(i=0; i < n ; i++, i++, p += 2 ) { @@ -922,7 +923,6 @@ show_fingerprint( PKT_public_key *pk ) } } tty_printf("\n"); - m_free(array); } diff --git a/g10/keyid.c b/g10/keyid.c index 2de67ccf2..387c3fa9a 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -330,7 +330,7 @@ expirestr_from_sk( PKT_secret_key *sk ) /**************** . * Return a byte array with the fingerprint for the given PK/SK * The length of the array is returned in ret_len. Caller must free - * the array or provide array as buffer of length MAX_FINGERPRINT_LEN + * the array or provide an array of length MAX_FINGERPRINT_LEN. */ byte * diff --git a/g10/mainproc.c b/g10/mainproc.c index e5c83287a..a47885b74 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -413,11 +413,14 @@ print_userid( PACKET *pkt ) static void print_fingerprint( PKT_public_key *pk, PKT_secret_key *sk ) { - byte *array, *p; + byte array[MAX_FINGERPRINT_LEN], *p; size_t i, n; - p = array = sk? fingerprint_from_sk( sk, NULL, &n ) - : fingerprint_from_pk( pk, NULL, &n ); + if( sk ) + fingerprint_from_sk( sk, array, &n ); + else + fingerprint_from_pk( pk, array, &n ); + p = array; if( opt.with_colons ) { printf("fpr:::::::::"); for(i=0; i < n ; i++, p++ ) @@ -442,7 +445,6 @@ print_fingerprint( PKT_public_key *pk, PKT_secret_key *sk ) } } putchar('\n'); - m_free(array); } diff --git a/g10/revoke.c b/g10/revoke.c index 61d1e4c5c..74c11cb01 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -57,7 +57,7 @@ gen_revoke( const char *uname ) KBPOS kbpos; if( opt.batch ) { - log_error("sorry, can't do this in batch mode\n"); + log_error(_("sorry, can't do this in batch mode\n")); return G10ERR_GENERAL; } @@ -70,21 +70,21 @@ gen_revoke( const char *uname ) /* search the userid */ rc = find_secret_keyblock_byname( &kbpos, uname ); if( rc ) { - log_error("secret key for user '%s' not found\n", uname ); + log_error(_("secret key for user '%s' not found\n"), uname ); goto leave; } /* read the keyblock */ rc = read_keyblock( &kbpos, &keyblock ); if( rc ) { - log_error("error reading the certificate: %s\n", g10_errstr(rc) ); + log_error(_("error reading the certificate: %s\n"), g10_errstr(rc) ); goto leave; } /* get the keyid from the keyblock */ node = find_kbnode( keyblock, PKT_SECRET_KEY ); if( !node ) { /* maybe better to use log_bug ? */ - log_error("Oops; secret key not found anymore!\n"); + log_error(_("Oops; secret key not found anymore!\n")); rc = G10ERR_GENERAL; goto leave; } @@ -107,11 +107,11 @@ gen_revoke( const char *uname ) pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, sk_keyid ); if( rc ) { - log_error("no corresponding public key: %s\n", g10_errstr(rc) ); + log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) ); goto leave; } if( cmp_public_secret_key( pk, sk ) ) { - log_error("public key does not match secret key!\n" ); + log_error(_("public key does not match secret key!\n") ); rc = G10ERR_GENERAL; goto leave; } @@ -125,11 +125,11 @@ gen_revoke( const char *uname ) switch( is_secret_key_protected( sk ) ) { case -1: - log_error("unknown protection algorithm\n"); + log_error(_("unknown protection algorithm\n")); rc = G10ERR_PUBKEY_ALGO; break; case 0: - tty_printf("Warning: This key is not protected!\n"); + tty_printf(_("note: This key is not protected!\n")); break; default: rc = check_secret_key( sk, 0 ); @@ -140,7 +140,7 @@ gen_revoke( const char *uname ) if( !opt.armor ) - tty_printf("ASCII armored output forced.\n"); + tty_printf(_("ASCII armored output forced.\n")); if( (rc = open_outfile( NULL, 0, &out )) ) goto leave; @@ -155,7 +155,7 @@ gen_revoke( const char *uname ) /* create it */ rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0, NULL, NULL); if( rc ) { - log_error("make_keysig_packet failed: %s\n", g10_errstr(rc)); + log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc)); goto leave; } init_packet( &pkt ); @@ -164,17 +164,17 @@ gen_revoke( const char *uname ) rc = build_packet( out, &pkt ); if( rc ) { - log_error("build_packet failed: %s\n", g10_errstr(rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); goto leave; } /* and issue a usage notice */ - tty_printf("Revocation certificate created.\n\n" + tty_printf(_("Revocation certificate created.\n\n" "Please move it to a medium which you can hide away; if Mallory gets\n" "access to this certificate he can use it to make your key unusable.\n" "It is smart to print this certificate and store it away, just in case\n" "your media become unreadable. But have some caution: The print system of\n" -"your machine might store the data and make it available to others!\n"); +"your machine might store the data and make it available to others!\n")); diff --git a/g10/ringedit.c b/g10/ringedit.c index ace7adc5f..677420e25 100644 --- a/g10/ringedit.c +++ b/g10/ringedit.c @@ -679,9 +679,11 @@ keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs ) /* skip pakets at the beginning of a keyring, until we find * a start packet; issue a warning if it is not a comment */ if( !root && pkt->pkttype != PKT_COMMENT - && pkt->pkttype != PKT_OLD_COMMENT ) + && pkt->pkttype != PKT_OLD_COMMENT ) { log_info("keyring_enum: skipped packet of type %d\n", pkt->pkttype ); + break; + } if( !root || (skipsigs && ( pkt->pkttype == PKT_SIGNATURE ||pkt->pkttype == PKT_COMMENT ||pkt->pkttype == PKT_OLD_COMMENT )) ) { @@ -704,6 +706,7 @@ keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs ) *ret_root = root; free_packet( pkt ); m_free( pkt ); + return rc; } diff --git a/g10/tdbio.c b/g10/tdbio.c index 70cf4a1ba..82b22b43a 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -39,6 +39,18 @@ #include "trustdb.h" #include "tdbio.h" +/* a type used to pass infomation to cmp_krec_fpr */ +struct cmp_krec_fpr_struct { + int pubkey_algo; + const char *fpr; + int fprlen; +}; + +/* a type used to pass infomation to cmp_sdir */ +struct cmp_sdir_struct { + int pubkey_algo; + u32 keyid[2]; +}; static char *db_name; @@ -149,6 +161,47 @@ open_db() /**************** + * Make a hashtable: type 0 = key hash, 1 = sdir hash + */ +static void +create_hashtable( TRUSTREC *vr, int type ) +{ + TRUSTREC rec; + off_t offset; + ulong recnum; + int i, n, rc; + + offset = lseek( db_fd, 0, SEEK_END ); + if( offset == -1 ) + log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) ); + recnum = offset / TRUST_RECORD_LEN; + assert(recnum); /* this is will never be the first record */ + + if( !type ) + vr->r.ver.keyhashtbl = recnum; + else + vr->r.ver.sdirhashtbl = recnum; + /* Now write the records */ + n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD; + for(i=0; i < n; i++, recnum++ ) { + memset( &rec, 0, sizeof rec ); + rec.rectype = RECTYPE_HTBL; /* free record */ + rec.recnum = recnum; + rc = tdbio_write_record( &rec ); + if( rc ) + log_fatal_f(db_name,_("failed to create hashtable: %s\n"), + g10_errstr(rc)); + } + /* update the version record */ + rc = tdbio_write_record( vr ); + if( rc ) + log_fatal_f( db_name, _("error updating version record: %s\n"), + g10_errstr(rc)); +} + + + +/**************** * Return the record number of the keyhash tbl or create a new one. */ static ulong @@ -165,48 +218,45 @@ get_keyhashrec() if( rc ) log_fatal_f( db_name, _("error reading version record: %s\n"), g10_errstr(rc) ); - if( vr.r.ver.keyhashtbl ) - keyhashtbl = vr.r.ver.keyhashtbl; - else { - TRUSTREC rec; - off_t offset; - ulong recnum; - int i, n; + if( !vr.r.ver.keyhashtbl ) + create_hashtable( &vr, 0 ); - offset = lseek( db_fd, 0, SEEK_END ); - if( offset == -1 ) - log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) ); - recnum = offset / TRUST_RECORD_LEN; - assert(recnum); /* this is will never be the first record */ - keyhashtbl = recnum; - /* Now write the records */ - n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD; - for(i=0; i < n; i++, recnum++ ) { - memset( &rec, 0, sizeof rec ); - rec.rectype = RECTYPE_HTBL; /* free record */ - rec.recnum = recnum; - rc = tdbio_write_record( &rec ); - if( rc ) - log_fatal_f(db_name,_("failed to create hashtable: %s\n"), - g10_errstr(rc)); - } - /* update the version record */ - vr.r.ver.keyhashtbl = keyhashtbl; - rc = tdbio_write_record( &vr ); - if( rc ) - log_fatal_f( db_name, _("error updating version record: %s\n"), - g10_errstr(rc)); - } - return keyhashtbl; + return vr.r.ver.keyhashtbl; +} + +/**************** + * Return the record number of the shadow direcory hash table + * or create a new one. + */ +static ulong +get_sdirhashrec() +{ + static ulong sdirhashtbl; /* record number of the hashtable */ + TRUSTREC vr; + int rc; + + if( sdirhashtbl ) + return sdirhashtbl; + + rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); + if( rc ) + log_fatal_f( db_name, _("error reading version record: %s\n"), + g10_errstr(rc) ); + if( !vr.r.ver.sdirhashtbl ) + create_hashtable( &vr, 1 ); + + return vr.r.ver.sdirhashtbl; } /**************** - * Update the key hashtbl or create the table if it does not exist + * Update a hashtable. + * table gives the start of the table, key and keylen is the key, + * newrecnum is the record number to insert. */ static int -update_keyhashtbl( TRUSTREC *kr ) +upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) { TRUSTREC lastrec, rec; ulong hashrec, item; @@ -214,40 +264,40 @@ update_keyhashtbl( TRUSTREC *kr ) int level=0; int rc, i; - hashrec = get_keyhashrec(); + hashrec = table; next_level: - msb = kr->r.key.fingerprint[level]; + msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { - log_error( db_name, "update_keyhashtbl read failed: %s\n", + log_error( db_name, "upd_hashtable: read failed: %s\n", g10_errstr(rc) ); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; - if( !item ) { /* insert new one */ - rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = kr->recnum; + if( !item ) { /* insert a new item into the hash table */ + rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) { - log_error( db_name, "update_keyhashtbl write htbl failed: %s\n", + log_error( db_name, "upd_hashtable: write htbl failed: %s\n", g10_errstr(rc) ); return rc; } } - else if( item != kr->recnum ) { /* must do an update */ + else if( item != newrecnum ) { /* must do an update */ lastrec = rec; rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { - log_error( db_name, "update_keyhashtbl read item failed: %s\n", + log_error( db_name, "upd_hashtable: read item failed: %s\n", g10_errstr(rc) ); return rc; } if( rec.rectype == RECTYPE_HTBL ) { hashrec = item; level++; - if( level >= kr->r.key.fingerprint_len ) { - log_error( db_name, "keyhashtbl has invalid indirections\n"); + if( level >= keylen ) { + log_error( db_name, "hashtable has invalid indirections.\n"); return G10ERR_TRUSTDB; } goto next_level; @@ -256,15 +306,13 @@ update_keyhashtbl( TRUSTREC *kr ) /* see whether the key is already in this list */ for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { - if( rec.r.hlst.rnum[i] == kr->recnum ) { - log_debug("HTBL: no update needed for keyrec %lu\n", - kr->recnum ); - return 0; + if( rec.r.hlst.rnum[i] == newrecnum ) { + return 0; /* okay, already in the list */ } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, - &rec, RECTYPE_HLST); + &rec, RECTYPE_HLST); if( rc ) { log_error( db_name, "scan keyhashtbl read hlst failed: %s\n", @@ -279,13 +327,13 @@ update_keyhashtbl( TRUSTREC *kr ) for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( !rec.r.hlst.rnum[i] ) { - rec.r.hlst.rnum[i] = kr->recnum; + rec.r.hlst.rnum[i] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) log_error( db_name, - "update_keyhashtbl write hlst failed: %s\n", + "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); - return rc; /* ready */ + return rc; /* done */ } } if( rec.r.hlst.next ) { @@ -293,7 +341,7 @@ update_keyhashtbl( TRUSTREC *kr ) &rec, RECTYPE_HLST ); if( rc ) { log_error( db_name, - "update_keyhashtbl read hlst failed: %s\n", + "upd_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } @@ -303,26 +351,26 @@ update_keyhashtbl( TRUSTREC *kr ) rc = tdbio_write_record( &rec ); if( rc ) { log_error( db_name, - "update_keyhashtbl write hlst failed: %s\n", + "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); return rc; } memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HLST; rec.recnum = item; - rec.r.hlst.rnum[0] = kr->recnum; + rec.r.hlst.rnum[0] = newrecnum; + rc = tdbio_write_record( &rec ); if( rc ) log_error( db_name, - "update_keyhashtbl write ext hlst failed: %s\n", + "upd_hashtable: write ext hlst failed: %s\n", g10_errstr(rc) ); - return rc; /* ready */ + return rc; /* done */ } - } + } /* end loop over hlst slots */ } - else if( rec.rectype == RECTYPE_KEY ) { /* insert a list record */ - if( rec.recnum == kr->recnum ) { - log_debug("HTBL: no update needed for keyrec %lu\n", - kr->recnum ); + else if( rec.rectype == RECTYPE_KEY + || rec.rectype == RECTYPE_SDIR ) { /* insert a list record */ + if( rec.recnum == newrecnum ) { return 0; } item = rec.recnum; /* save number of key record */ @@ -330,11 +378,11 @@ update_keyhashtbl( TRUSTREC *kr ) rec.rectype = RECTYPE_HLST; rec.recnum = tdbio_new_recnum(); rec.r.hlst.rnum[0] = item; /* old keyrecord */ - rec.r.hlst.rnum[1] = kr->recnum; /* and new one */ + rec.r.hlst.rnum[1] = newrecnum; /* and new one */ rc = tdbio_write_record( &rec ); if( rc ) { log_error( db_name, - "update_keyhashtbl write new hlst failed: %s\n", + "upd_hashtable: write new hlst failed: %s\n", g10_errstr(rc) ); return rc; } @@ -342,13 +390,12 @@ update_keyhashtbl( TRUSTREC *kr ) lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum; rc = tdbio_write_record( &lastrec ); if( rc ) - log_error( db_name, - "update_keyhashtbl update htbl failed: %s\n", - g10_errstr(rc) ); + log_error( db_name, "upd_hashtable: update htbl failed: %s\n", + g10_errstr(rc) ); return rc; /* ready */ } else { - log_error( db_name, "keyhashtbl %lu points to an invalid record\n", + log_error( db_name, "hashtbl %lu points to an invalid record\n", item); return G10ERR_TRUSTDB; } @@ -359,6 +406,119 @@ update_keyhashtbl( TRUSTREC *kr ) +/**************** + * Lookup a record via the hashtable tablewith key/keylen and return the + * result in rec. cmp() should return if the record is the desired one. + * Returns -1 if not found, 0 if found or another errocode + */ +static int +lookup_hashtable( ulong table, const byte *key, size_t keylen, + int (*cmpfnc)(void*, const TRUSTREC *), void *cmpdata, + TRUSTREC *rec ) +{ + int rc; + ulong hashrec, item; + int msb; + int level=0; + + hashrec = table; + next_level: + msb = key[level]; + hashrec += msb / ITEMS_PER_HTBL_RECORD; + rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL ); + if( rc ) { + log_error( db_name, "lookup_hashtable failed: %s\n", g10_errstr(rc) ); + return rc; + } + + item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; + if( !item ) + return -1; /* not found */ + + rc = tdbio_read_record( item, rec, 0 ); + if( rc ) { + log_error( db_name, "hashtable read failed: %s\n", g10_errstr(rc) ); + return rc; + } + if( rec->rectype == RECTYPE_HTBL ) { + hashrec = item; + level++; + if( level >= keylen ) { + log_error( db_name, "hashtable has invalid indirections\n"); + return G10ERR_TRUSTDB; + } + goto next_level; + } + else if( rec->rectype == RECTYPE_HLST ) { + for(;;) { + int i; + + for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { + if( rec->r.hlst.rnum[i] ) { + TRUSTREC tmp; + + rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 ); + if( rc ) { + log_error( "lookup_hashtable: read item failed: %s\n", + g10_errstr(rc) ); + return rc; + } + if( (*cmpfnc)( cmpdata, &tmp ) ) { + *rec = tmp; + return 0; + } + } + } + if( rec->r.hlst.next ) { + rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST ); + if( rc ) { + log_error( "lookup_hashtable: read hlst failed: %s\n", + g10_errstr(rc) ); + return rc; + } + } + else + return -1; /* not found */ + } + } + + + if( (*cmpfnc)( cmpdata, rec ) ) + return 0; /* really found */ + + return -1; /* no: not found */ +} + + + + +/**************** + * Update the key hashtbl or create the table if it does not exist + */ +static int +update_keyhashtbl( TRUSTREC *kr ) +{ + return upd_hashtable( get_keyhashrec(), + kr->r.key.fingerprint, + kr->r.key.fingerprint_len, kr->recnum ); +} + +/**************** + * Update the shadow dir hashtbl or create the table if it does not exist + */ +static int +update_sdirhashtbl( TRUSTREC *sr ) +{ + byte key[8]; + + u32tobuf( key , sr->r.sdir.keyid[0] ); + u32tobuf( key+4 , sr->r.sdir.keyid[1] ); + return upd_hashtable( get_sdirhashrec(), key, 8, sr->recnum ); +} + + + + void tdbio_dump_record( TRUSTREC *rec, FILE *fp ) { @@ -371,8 +531,9 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) switch( rec->rectype ) { case 0: fprintf(fp, "blank\n"); break; - case RECTYPE_VER: fprintf(fp, "version, keyhashtbl=%lu, firstfree=%lu\n", - rec->r.ver.keyhashtbl, rec->r.ver.firstfree ); + case RECTYPE_VER: fprintf(fp, "version, kd=%lu, sd=%lu, free=%lu\n", + rec->r.ver.keyhashtbl, rec->r.ver.sdirhashtbl, + rec->r.ver.firstfree ); break; case RECTYPE_FREE: fprintf(fp, "free, next=%lu\n", rec->r.free.next ); break; @@ -411,6 +572,10 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) rec->r.uid.prefrec, rec->r.uid.siglist, rec->r.uid.namehash[18], rec->r.uid.namehash[19]); + if( rec->r.uid.uidflags & UIDF_CHECKED ) + fputs(", checked", fp ); + if( rec->r.uid.uidflags & UIDF_VALID ) + fputs(", valid", fp ); if( rec->r.uid.uidflags & UIDF_REVOKED ) fputs(", revoked", fp ); putc('\n', fp); @@ -436,6 +601,14 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) } putc('\n', fp); break; + case RECTYPE_SDIR: + fprintf(fp, "sdir %lu, keyid=%08lX%08lX, algo=%d, hint=%lu\n", + rec->r.sdir.lid, + (ulong)rec->r.sdir.keyid[0], + (ulong)rec->r.sdir.keyid[1], + rec->r.sdir.pubkey_algo, + (ulong)rec->r.sdir.hintlist ); + break; case RECTYPE_CACH: fprintf(fp, "cach\n"); break; @@ -483,6 +656,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) return G10ERR_READ_FILE; } rec->recnum = recnum; + rec->dirty = 0; p = buf; rec->rectype = *p++; if( expected && rec->rectype != expected ) { @@ -508,6 +682,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) rec->r.ver.validated= buftoulong(p); p += 4; rec->r.ver.keyhashtbl=buftoulong(p); p += 4; rec->r.ver.firstfree =buftoulong(p); p += 4; + rec->r.ver.sdirhashtbl =buftoulong(p); p += 4; if( recnum ) { log_error_f( db_name, "version record with recnum %lu\n", (ulong)recnum ); @@ -568,6 +743,19 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) rec->r.sig.sig[i].flag = *p++; } break; + case RECTYPE_SDIR: /* shadow directory record */ + rec->r.sdir.lid = buftoulong(p); p += 4; + rec->r.sdir.keyid[0]= buftou32(p); p += 4; + rec->r.sdir.keyid[1]= buftou32(p); p += 4; + rec->r.sdir.pubkey_algo = *p++; + p += 3; + rec->r.sdir.hintlist = buftoulong(p); + if( rec->r.sdir.lid != recnum ) { + log_error_f( db_name, "sdir LID != recnum (%lu,%lu)\n", + rec->r.sdir.lid, (ulong)recnum ); + rc = G10ERR_TRUSTDB; + } + break; case RECTYPE_CACH: /* cache record (FIXME)*/ rec->r.cache.lid = buftoulong(p); p += 4; memcpy(rec->r.cache.blockhash, p, 20); p += 20; @@ -609,6 +797,8 @@ tdbio_write_record( TRUSTREC *rec ) if( db_fd == -1 ) open_db(); +tdbio_dump_record( rec, stdout ); + memset(buf, 0, TRUST_RECORD_LEN); p = buf; *p++ = rec->rectype; p++; @@ -626,6 +816,7 @@ tdbio_write_record( TRUSTREC *rec ) ulongtobuf(p, rec->r.ver.validated); p += 4; ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4; ulongtobuf(p, rec->r.ver.firstfree ); p += 4; + ulongtobuf(p, rec->r.ver.sdirhashtbl ); p += 4; break; case RECTYPE_FREE: @@ -677,6 +868,15 @@ tdbio_write_record( TRUSTREC *rec ) } break; + case RECTYPE_SDIR: + ulongtobuf( p, rec->r.sdir.lid); p += 4; + u32tobuf( p, rec->r.sdir.keyid[0] ); p += 4; + u32tobuf( p, rec->r.sdir.keyid[1] ); p += 4; + *p++ = rec->r.sdir.pubkey_algo; + p += 3; + ulongtobuf( p, rec->r.sdir.hintlist ); + break; + case RECTYPE_CACH: /* FIXME*/ ulongtobuf(p, rec->r.cache.lid); p += 4; memcpy(p, rec->r.cache.blockhash, 20); p += 20; @@ -711,6 +911,8 @@ tdbio_write_record( TRUSTREC *rec ) } else if( rec->rectype == RECTYPE_KEY ) rc = update_keyhashtbl( rec ); + else if( rec->rectype == RECTYPE_SDIR ) + rc = update_sdirhashtbl( rec ); return rc; } @@ -805,13 +1007,13 @@ tdbio_new_recnum() int tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec ) { - byte *fingerprint; + byte fingerprint[MAX_FINGERPRINT_LEN]; size_t fingerlen; u32 keyid[2]; int rc; keyid_from_pk( pk, keyid ); - fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen ); + fingerprint_from_pk( pk, fingerprint, &fingerlen ); rc = tdbio_search_dir_byfpr( fingerprint, fingerlen, pk->pubkey_algo, rec ); @@ -827,185 +1029,74 @@ tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec ) } +static int +cmp_krec_fpr( void *dataptr, const TRUSTREC *rec ) +{ + const struct cmp_krec_fpr_struct *d = dataptr; + + return rec->rectype == RECTYPE_KEY + && ( !d->pubkey_algo || rec->r.key.pubkey_algo == d->pubkey_algo ) + && rec->r.key.fingerprint_len == d->fprlen + && !memcmp( rec->r.key.fingerprint, d->fpr, d->fprlen ); +} + int tdbio_search_dir_byfpr( const byte *fingerprint, size_t fingerlen, int pubkey_algo, TRUSTREC *rec ) { + struct cmp_krec_fpr_struct cmpdata; ulong recnum; int rc; - ulong hashrec, item; - int msb; - int level=0; assert( fingerlen == 20 || fingerlen == 16 ); /* locate the key using the hash table */ - hashrec = get_keyhashrec(); - next_level: - msb = fingerprint[level]; - hashrec += msb / ITEMS_PER_HTBL_RECORD; - rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL ); - if( rc ) { - log_error( db_name, "scan keyhashtbl failed: %s\n", g10_errstr(rc) ); - return rc; - } - - item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; - if( !item ) - return -1; /* not found */ - - rc = tdbio_read_record( item, rec, 0 ); - if( rc ) { - log_error( db_name, "keyhashtbl read failed: %s\n", g10_errstr(rc) ); - return rc; - } - if( rec->rectype == RECTYPE_HTBL ) { - hashrec = item; - level++; - if( level >= fingerlen ) { - log_error( db_name, "keyhashtbl has invalid indirections\n"); - return G10ERR_TRUSTDB; - } - goto next_level; - } - else if( rec->rectype == RECTYPE_HLST ) { - for(;;) { - int i; - - for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { - if( rec->r.hlst.rnum[i] ) { - TRUSTREC tmp; - - rc = tdbio_read_record( rec->r.hlst.rnum[i], - &tmp, RECTYPE_KEY ); - if( rc ) { - log_error( db_name, - "scan keyhashtbl read key failed: %s\n", - g10_errstr(rc) ); - return rc; - } - if( (!pubkey_algo || tmp.r.key.pubkey_algo == pubkey_algo) - && tmp.r.key.fingerprint_len == fingerlen - && !memcmp(tmp.r.key.fingerprint, - fingerprint, fingerlen) ) { - *rec = tmp; - goto found; - } - } - } - if( rec->r.hlst.next ) { - rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST ); - if( rc ) { - log_error( db_name, - "scan keyhashtbl read hlst failed: %s\n", - g10_errstr(rc) ); - return rc; - } - } - else - return -1; /* not found */ - } - found: - ; - } - else if( rec->rectype == RECTYPE_KEY ) { - /* must check that it is the requested key */ - if( (pubkey_algo && rec->r.key.pubkey_algo != pubkey_algo) - || rec->r.key.fingerprint_len != fingerlen - || memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) ) - return -1; /* no: not found */ - } - else { - log_error( db_name, "keyhashtbl %lu points to an invalid record\n", - item); - return G10ERR_TRUSTDB; + cmpdata.pubkey_algo = pubkey_algo; + cmpdata.fpr = fingerprint; + cmpdata.fprlen = fingerlen; + rc = lookup_hashtable( get_keyhashrec(), fingerprint, fingerlen, + cmp_krec_fpr, &cmpdata, rec ); + if( !rc ) { + recnum = rec->r.key.lid; + /* Now read the dir record */ + rc = tdbio_read_record( recnum, rec, RECTYPE_DIR); + if( rc ) + log_error_f(db_name, "can't read dirrec %lu: %s\n", + recnum, g10_errstr(rc) ); } - - recnum = rec->r.key.lid; - /* Now read the dir record */ - rc = tdbio_read_record( recnum, rec, RECTYPE_DIR); - if( rc ) - log_error_f(db_name, "can't read dirrec %lu: %s\n", - recnum, g10_errstr(rc) ); - return rc; } + + static int -del_reclist( ulong recno, int type ) +cmp_sdir( void *dataptr, const TRUSTREC *rec ) { - TRUSTREC rec; - int rc; + const struct cmp_sdir_struct *d = dataptr; - while( recno ) { - rc = tdbio_read_record( recno, &rec, type); - if( rc ) { - log_error_f(db_name, "can't read record %lu: %s\n", - recno, g10_errstr(rc)); - return rc; - } - switch( type ) { - case RECTYPE_PREF: recno = rec.r.pref.next; break; - case RECTYPE_UID: recno = rec.r.uid.next; break; - default: BUG(); - } - rc = tdbio_delete_record( rec.recnum ); - if( rc ) { - log_error_f(db_name, "can't delete record %lu: %s\n", - rec.recnum, g10_errstr(rc)); - return rc; - } - } - return 0; + return rec->rectype == RECTYPE_SDIR + && ( !d->pubkey_algo || rec->r.sdir.pubkey_algo == d->pubkey_algo ) + && rec->r.sdir.keyid[0] == d->keyid[0] + && rec->r.sdir.keyid[1] == d->keyid[1]; } -/**************** - * Delete the Userid UIDLID from DIRLID - */ + int -tdbio_delete_uidrec( ulong dirlid, ulong uidlid ) +tdbio_search_sdir( u32 *keyid, int pubkey_algo, TRUSTREC *rec ) { - TRUSTREC dirrec, rec; - ulong recno; + struct cmp_sdir_struct cmpdata; int rc; - - rc = tdbio_read_record( dirlid, &dirrec, RECTYPE_DIR); - if( rc ) { - log_error_f(db_name, "can't read dirrec %lu: %s\n", dirlid, g10_errstr(rc)); - return rc; - } - recno = dirrec.r.dir.uidlist; - for( ; recno; recno = rec.r.uid.next ) { - rc = tdbio_read_record( recno, &rec, RECTYPE_UID); - if( rc ) { - log_error_f(db_name, "can't read uidrec %lu: %s\n", - recno, g10_errstr(rc)); - return rc; - } - if( recno == uidlid ) { - rc = del_reclist( rec.r.uid.prefrec, RECTYPE_PREF ); - if( rc ) - return rc; - rc = del_reclist( rec.r.uid.siglist, RECTYPE_SIG ); - if( rc ) - return rc; - rc = tdbio_delete_record( recno ); - if( rc ) { - log_error_f(db_name, "can't delete uidrec %lu: %s\n", - recno, g10_errstr(rc)); - return rc; - } - dirrec.r.dir.uidlist = 0; - rc = tdbio_write_record( &dirrec ); - if( rc ) { - log_error_f(db_name, "can't update dirrec %lu: %s\n", - dirrec.recnum, g10_errstr(rc)); - return rc; - } - return 0; - } - } - return -1; /* not found */ + byte key[8]; + + /* locate the shadow dir record using the hash table */ + u32tobuf( key , keyid[0] ); + u32tobuf( key+4 , keyid[1] ); + cmpdata.pubkey_algo = pubkey_algo; + cmpdata.keyid[0] = keyid[0]; + cmpdata.keyid[1] = keyid[1]; + rc = lookup_hashtable( get_sdirhashrec(), key, 8, + cmp_sdir, &cmpdata, rec ); + return rc; } diff --git a/g10/tdbio.h b/g10/tdbio.h index b59b4e40e..66579ad14 100644 --- a/g10/tdbio.h +++ b/g10/tdbio.h @@ -39,6 +39,7 @@ #define RECTYPE_UID 4 #define RECTYPE_PREF 5 #define RECTYPE_SIG 6 +#define RECTYPE_SDIR 8 #define RECTYPE_CACH 9 #define RECTYPE_HTBL 10 #define RECTYPE_HLST 11 @@ -47,19 +48,28 @@ #define DIRF_CHECKED 1 /* everything has been checked, the other bits are valid */ -#define DIRF_MISKEY 2 /* some keys are missing, so they could not be checked*/ +#define DIRF_MISKEY 2 /* not all signatures are checked */ + /* this flag is used as a quick hint, that we */ + /* do not need to look at the sig records */ #define DIRF_ERROR 4 /* severe errors: the key is not valid for some reasons but we mark it to avoid duplicate checks */ #define DIRF_REVOKED 8 /* the complete key has been revoked */ -#define KEYF_REVOKED DIRF_REVOKED /* this key has been revoked - (only useful on subkeys)*/ -#define UIDF_REVOKED DIRF_REVOKED /* this user id has been revoked */ +#define KEYF_REVOKED 8 /* this key has been revoked (only useful on subkeys)*/ +#define UIDF_CHECKED 1 /* user id has been checked - other bits are valid */ +#define UIDF_VALID 2 /* this is a valid user id */ +#define UIDF_REVOKED 8 /* this user id has been revoked */ + +#define SIGF_CHECKED 1 /* signature has been checked - bits 0..6 are valid */ +#define SIGF_VALID 2 /* the signature is valid */ +#define SIGF_REVOKED 8 /* this signature has been revoked */ +#define SIGF_NOPUBKEY 128 /* there is no pubkey for this sig */ struct trust_record { int rectype; int mark; + int dirty; /* for now only used internal by functions */ struct trust_record *next; /* help pointer to build lists in memory */ ulong recnum; union { @@ -70,6 +80,7 @@ struct trust_record { ulong validated; /* timestamp of last validation */ ulong keyhashtbl; ulong firstfree; + ulong sdirhashtbl; } ver; struct { /* free record */ ulong next; @@ -109,9 +120,15 @@ struct trust_record { ulong next; /* recnno of next record or NULL for last one */ struct { ulong lid; /* of pubkey record of signator (0=unused) */ - byte flag; /* SIGRF_xxxxx */ + byte flag; /* SIGF_xxxxx */ } sig[SIGS_PER_RECORD]; } sig; + struct { + ulong lid; + u32 keyid[2]; + byte pubkey_algo; + u32 hintlist; + } sdir; struct { /* cache record */ ulong lid; byte blockhash[20]; @@ -122,7 +139,7 @@ struct trust_record { } htbl; struct { ulong next; - ulong rnum[ITEMS_PER_HLST_RECORD]; /* of a key record */ + ulong rnum[ITEMS_PER_HLST_RECORD]; /* of another record */ } hlst; } r; }; @@ -154,7 +171,7 @@ ulong tdbio_new_recnum(void); int tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec ); int tdbio_search_dir_byfpr( const byte *fingerprint, size_t fingerlen, int pubkey_algo, TRUSTREC *rec ); -int tdbio_delete_uidrec( ulong dirlid, ulong uidlid ); +int tdbio_search_sdir( u32 *keyid, int pubkey_algo, TRUSTREC *rec ); #define buftoulong( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ diff --git a/g10/trustdb.c b/g10/trustdb.c index 78b7b0aa0..18726f151 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -85,7 +85,7 @@ struct recno_list_struct { typedef struct recno_list_struct *RECNO_LIST; -static int walk_sigrecs( SIGREC_CONTEXT *c, int create ); +static int walk_sigrecs( SIGREC_CONTEXT *c ); static LOCAL_ID_INFO *new_lid_table(void); static void release_lid_table( LOCAL_ID_INFO *tbl ); @@ -96,11 +96,11 @@ static void upd_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag ); static void print_user_id( const char *text, u32 *keyid ); static int do_list_path( TRUST_INFO *stack, int depth, int max_depth, LOCAL_ID_INFO *lids, TRUST_SEG_LIST *tslist ); -static int update_sigs_by_lid( ulong lid ); static int list_sigs( ulong pubkey_id ); static int propagate_trust( TRUST_SEG_LIST tslist ); static int do_check( TRUSTREC *drec, unsigned *trustlevel ); +static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); /* a table used to keep track of ultimately trusted keys @@ -282,34 +282,6 @@ upd_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag ) BUG(); } -/**************** - * If we do not have a local_id in a signature packet, find the owner of - * the signature packet in our trustdb or insert them into the trustdb - */ -static int -set_signature_packets_lid( PKT_signature *sig ) -{ - PKT_public_key *pk = m_alloc_clear( sizeof *pk ); - TRUSTREC rec; - int rc; - - rc = get_pubkey( pk, sig->keyid ); - if( rc) - goto leave; - if( !pk->local_id ) { - rc = tdbio_search_dir_bypk( pk, &rec ); - if( rc == -1 ) - rc = insert_trust_record( pk ); - if( rc ) - goto leave; - } - sig->local_id = pk->local_id; - - leave: - free_public_key( pk ); - return rc; -} - /**************** @@ -343,6 +315,24 @@ keyid_from_lid( ulong lid, u32 *keyid ) return 0; } + +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; + + get_dir_record( pk, &rec ); + } + return pk->local_id; +} + + /**************** @@ -356,9 +346,8 @@ keyid_from_lid( ulong lid, u32 *keyid ) * Returns: 0 - okay, -1 for eof (no more sigs) or any other errorcode */ static int -walk_sigrecs( SIGREC_CONTEXT *c, int create ) +walk_sigrecs( SIGREC_CONTEXT *c ) { - int rc=0; TRUSTREC *r; ulong rnum; @@ -380,20 +369,6 @@ walk_sigrecs( SIGREC_CONTEXT *c, int create ) rnum = r->r.sig.next; if( !rnum && c->ctl.nextuid ) { /* read next uid record */ read_record( c->ctl.nextuid, r, RECTYPE_UID ); - if( !r->r.uid.siglist && create ) { - rc = update_sigs_by_lid( c->lid ); - if( rc ) { - if( rc == G10ERR_BAD_CERT ) - rc = -1; /* maybe no selfsignature */ - if( rc != -1 ) - log_info("LID %lu: " - "error building sigs on the fly: %s\n", - c->lid, g10_errstr(rc) ); - c->ctl.eof = 1; - return rc; - } - read_record( c->ctl.nextuid, r, RECTYPE_UID ); - } c->ctl.nextuid = r->r.uid.next; rnum = r->r.uid.siglist; } @@ -557,7 +532,7 @@ do_list_sigs( ulong root, ulong pubkey, int depth, memset( &sx, 0, sizeof sx ); sx.lid = pubkey; for(;;) { - rc = walk_sigrecs( &sx, 0 ); + rc = walk_sigrecs( &sx ); if( rc ) break; rc = keyid_from_lid( sx.sig_lid, keyid ); @@ -716,7 +691,7 @@ do_list_path( TRUST_INFO *stack, int depth, int max_depth, memset( &sx, 0, sizeof sx ); sx.lid = stack[depth-1].lid; /* loop over all signatures. If we do not have any, try to create them */ - while( !(rc = walk_sigrecs( &sx, 1 )) ) { + while( !(rc = walk_sigrecs( &sx )) ) { TRUST_SEG_LIST tsl, t2, tl; int i; @@ -767,291 +742,6 @@ do_list_path( TRUST_INFO *stack, int depth, int max_depth, /**************** - * find the uid record given the uid packet and the dir-record. - * Returns: 0 = found - * -1 = No such uid-record - * or other error - */ -static int -find_urec( TRUSTREC *dir, PKT_user_id *uid, TRUSTREC *urec ) -{ - byte nhash[20]; - ulong recno; - - assert(dir->rectype == RECTYPE_DIR ); - rmd160_hash_buffer( nhash, uid->name, uid->len ); - for( recno=dir->r.dir.uidlist; recno; recno = urec->r.uid.next ) { - read_record( recno, urec, RECTYPE_UID ); - if( !memcmp( nhash, urec->r.uid.namehash, 20 ) ) - return 0; - } - - return -1; -} - - -/**************** - * Test whether zthe signature lid is already in the (in mem) list. - * Returns: True if it is a duplicate - */ -static int -test_dupsig( TRUSTREC *rec, ulong lid ) -{ - int i; - ulong alid; - - for( ; rec; rec = rec->next ) { - for(i=0; i < SIGS_PER_RECORD && (alid = rec->r.sig.sig[i].lid); i++ ) - if( alid == lid ) - return 1; - } - return 0; -} - - -/**************** - * release the sigrec from the uidlist - */ -static void -rel_uidsigs( TRUSTREC *urec ) -{ - TRUSTREC *r2, *rec; - assert( urec->rectype == RECTYPE_UID ); - - for(rec=urec->next ; rec; rec = r2 ) { - assert( rec->rectype == RECTYPE_SIG ); - r2 = rec->next; - m_free( rec ); - } - urec->next = NULL; -} - -static int -no_selfsig_del( ulong lid, u32 *keyid, TRUSTREC *urec ) -{ - int rc; - - log_error("key %08lX.%lu, uid %02X%02X: " - "no self-signature - user id removed\n", - (ulong)keyid[1], lid, urec->r.uid.namehash[18], - urec->r.uid.namehash[19] ); - rel_uidsigs( urec ); - rc = tdbio_delete_uidrec( lid, urec->recnum ); - if( rc ) - log_error("no_selfsig_del: delete_uid %lu failed: %s\n", - lid, g10_errstr(rc) ); - return rc; -} - -/**************** - * Write the signature records from the in-mem list at urec - * (The sequence of signatures does not matter) - */ -static int -write_sigs_from_urec( ulong lid, u32 *keyid, TRUSTREC *urec ) -{ - TRUSTREC *rec, srec; - ulong nextrecno; - ulong recno; - - nextrecno = urec->r.uid.siglist; - urec->r.uid.siglist = 0; /* end of list marker */ - for( rec = urec->next; rec; rec = rec->next ) { - assert( rec->rectype == RECTYPE_SIG ); - if( nextrecno ) { /* read the sig record, so it can be reused */ - read_record( nextrecno, &srec, RECTYPE_SIG ); - recno = nextrecno; - nextrecno = srec.r.sig.next; - } - else - recno = tdbio_new_recnum(); - - /* link together (the sequence of signatures does not matter) */ - rec->r.sig.next = urec->r.uid.siglist; - urec->r.uid.siglist = recno; - rec->r.sig.lid = lid; - /* and write */ - rec->recnum = recno; - write_record( rec ); - } - - /* write the urec back */ - write_record( urec ); - - /* delete remaining old sigrecords */ - while( nextrecno ) { - read_record( nextrecno, &srec, RECTYPE_SIG ); - delete_record( nextrecno ); - nextrecno = srec.r.sig.next; - } - - return 0; -} - -/**************** - * If we do not have sigrecs for the given key, build them and write them - * to the trustdb - */ -static int -update_sigs( TRUSTREC *dir ) -{ - TRUSTREC *rec, krec; - TRUSTREC urec; - TRUSTREC *sigrec_list; - KBNODE keyblock = NULL; - KBNODE node; - int i, sigidx, have_urec ; - ulong lid = dir->r.dir.lid; - u32 keyid[2]; - int miskey=0; - int rc=0; - - if( DBG_TRUST ) - log_debug("update_sigs for %lu\n", lid ); - - read_record( dir->r.dir.keylist, &krec, RECTYPE_KEY ); - rc = get_keyblock_byfprint( &keyblock, krec.r.key.fingerprint, - krec.r.key.fingerprint_len ); - if( rc ) { - log_error( "update_sigs: keyblock for %lu not found: %s\n", - lid, g10_errstr(rc) ); - goto leave; - } - - /* check all key signatures */ - assert( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ); - have_urec = 0; - sigrec_list = NULL; - sigidx = 0; - for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_PUBLIC_KEY ) - keyid_from_pk( node->pkt->pkt.public_key, keyid ); - else if( node->pkt->pkttype == PKT_USER_ID ) { - if( have_urec && !(urec.mark & 1) ) { - if( (rc = no_selfsig_del(lid, keyid, &urec )) ) - goto leave; - have_urec = 0; - } - if( have_urec ) { - rc = write_sigs_from_urec( lid, keyid, &urec ); - if( rc ) - goto leave; - rel_uidsigs( &urec ); - } - rc = find_urec( dir, node->pkt->pkt.user_id, &urec ); - urec.next = NULL; - urec.mark = 0; - have_urec = sigidx = 0; - if( rc == -1 ) { - log_info("update_sigs: new user id for %lu\n", lid ); - /* fixme: we should add the new user id here */ - } - else if( rc ) { - log_error("update_sigs: find_urec %lu failed: %s\n", - lid, g10_errstr(rc) ); - goto leave; - } - else - have_urec = 1; - } - else if( have_urec && node->pkt->pkttype == PKT_SIGNATURE ) { - PKT_signature *sig = node->pkt->pkt.signature; - - if( (sig->sig_class&~3) == 0x10 ) { - rc = check_key_signature( keyblock, node, &i ); - if( rc == G10ERR_NO_PUBKEY ) { - if( opt.verbose ) - log_info(_("key %08lX.%lu, uid %02X%02X: " - "no public key for signature %08lX\n"), - (ulong)keyid[1], lid, urec.r.uid.namehash[18], - urec.r.uid.namehash[19], (ulong)sig->keyid[1] ); - miskey = 1; - } - else if( rc ) - log_info(_("key %08lX.%lu, uid %02X%02X: " - "invalid %ssignature: %s\n"), - (ulong)keyid[1], lid, urec.r.uid.namehash[18], - urec.r.uid.namehash[19], - i?"self-":"",g10_errstr(rc)); - else if( i ) /* mark that we have a valid selfsignature */ - urec.mark |= 1; - else if( (rc = set_signature_packets_lid( sig )) ) - log_error("key %08lX.%lu, uid %02X%02X: " - "can't get LID of signer: %s\n", - (ulong)keyid[1], lid, urec.r.uid.namehash[18], - urec.r.uid.namehash[19], g10_errstr(rc)); - else if( !test_dupsig( urec.next, sig->local_id ) ) { - /* put the valid signature into a list */ - rec = urec.next; - if( !rec || sigidx == SIGS_PER_RECORD ) { - rec = m_alloc_clear( sizeof *rec ); - rec->rectype = RECTYPE_SIG; - rec->next = urec.next; - urec.next = rec; - sigidx = 0; - } - rec->r.sig.sig[sigidx].lid = sig->local_id; - rec->r.sig.sig[sigidx].flag = 0; - sigidx++; - if( DBG_TRUST ) - log_debug("key %08lX.%lu, uid %02X%02X: " - "signed by LID %lu\n", - (ulong)keyid[1], lid, urec.r.uid.namehash[18], - urec.r.uid.namehash[19], sig->local_id); - } - else if( DBG_TRUST ) - log_debug("key %08lX.%lu, uid %02X%02X: " - "duplicate signature by LID %lu\n", - (ulong)keyid[1], lid, urec.r.uid.namehash[18], - urec.r.uid.namehash[19], sig->local_id ); - rc = 0; - } - else { - /* fixme: handle other sig classes here */ - /* FIXME: Revocations!!! */ - } - } - } - if( have_urec && !(urec.mark & 1) ) { - if( (rc = no_selfsig_del(lid, keyid, &urec )) ) - goto leave; - have_urec = 0; - } - if( have_urec ) { - rc = write_sigs_from_urec( lid, keyid, &urec ); - if( rc ) - goto leave; - rel_uidsigs( &urec ); - } - dir->r.dir.dirflags |= DIRF_CHECKED; - if( miskey ) - dir->r.dir.dirflags |= DIRF_MISKEY; - else - dir->r.dir.dirflags &= ~DIRF_MISKEY; - write_record( dir ); - - leave: - /* fixme: need more cleanup in case of an error */ - release_kbnode( keyblock ); - if( DBG_TRUST ) - log_debug("update_sigs for %lu: %s\n", lid, g10_errstr(rc) ); - return rc; -} - - -static int -update_sigs_by_lid( ulong lid ) -{ - int rc; - TRUSTREC rec; - - read_record( lid, &rec, RECTYPE_DIR ); - if( !(rec.r.dir.dirflags & DIRF_CHECKED) ) - rc = update_sigs( &rec ); - return rc; -} - -/**************** * Make a list of trust paths */ static int @@ -1170,8 +860,7 @@ do_check( TRUSTREC *dr, unsigned *trustlevel ) } /* did we already check the signatures */ - if( !(dr->r.dir.dirflags & DIRF_CHECKED) ) /* no - do it now */ - rc = update_sigs( dr ); + /* fixme:.... */ if( dr->r.dir.dirflags & DIRF_REVOKED ) tflags |= TRUST_FLAG_REVOKED; @@ -1557,55 +1246,111 @@ list_trust_path( int max_depth, const char *username ) /**************** - * Check the complete trustdb or only the entries for the given username - * FIXME: We need a mode which only looks at keys with the MISKEY flag set. + * Check the complete trustdb or only the entries for the given username. + * Special hack: A username "REBUILD" inserts all records from the public + * key rings into the trustdb. */ void check_trustdb( const char *username ) { TRUSTREC rec; + KBNODE keyblock = NULL; + KBPOS kbpos; int rc; + int rebuild = username && !strcmp(username, "REBUILD"); - if( username && *username == '#' ) { - int rc; - ulong lid = atoi(username+1); + if( username && !rebuild ) { + rc = find_keyblock_byname( &kbpos, username ); + if( !rc ) + rc = read_keyblock( &kbpos, &keyblock ); + if( rc ) { + log_error("%s: keyblock read problem: %s\n", + username, g10_errstr(rc)); + } + else { + rc = update_trust_record( keyblock ); + if( rc == -1 ) { /* not yet in trustdb: insert */ + rc = insert_trust_record( + find_kbnode( keyblock, PKT_PUBLIC_KEY + ) ->pkt->pkt.public_key ); - if( (rc = update_sigs_by_lid( lid )) ) - log_error("lid %lu: check failed: %s\n", - lid, g10_errstr(rc)); - else - log_info("lid %lu: checked: %s\n", lid, g10_errstr(rc)); - } - else if( username ) { - PKT_public_key *pk = m_alloc_clear( sizeof *pk ); + } + if( rc ) + log_error("%s: update failed: %s\n", + username, g10_errstr(rc) ); + else + log_info("%s: updated\n", username ); - if( (rc = get_pubkey_byname( pk, username )) ) - log_error("user '%s' not found: %s\n", username, g10_errstr(rc) ); - else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) - log_error("problem finding '%s' in trustdb: %s\n", - username, g10_errstr(rc)); - else if( rc == -1 ) - log_error("user '%s' not in trustdb\n", username); - else if( (rc = update_sigs( &rec )) ) - log_error("lid %lu: check failed: %s\n", - rec.recnum, g10_errstr(rc)); - else - log_info("lid %lu: checked: %s\n", rec.recnum, g10_errstr(rc)); - free_public_key( pk ); + } + release_kbnode( keyblock ); keyblock = NULL; } else { ulong recnum; for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { if( rec.rectype == RECTYPE_DIR ) { - rc = update_sigs( &rec ); + TRUSTREC tmp; + if( !rec.r.dir.keylist ) { + log_info("lid %lu: dir record w/o key - skipped\n", recnum); + continue; + } + + read_record( rec.r.dir.keylist, &tmp, RECTYPE_KEY ); + + rc = get_keyblock_byfprint( &keyblock, + tmp.r.key.fingerprint, + tmp.r.key.fingerprint_len ); + if( rc ) { + log_error("lid %lu: keyblock not found: %s\n", + recnum, g10_errstr(rc) ); + continue; + } + rc = update_trust_record( keyblock ); if( rc ) - log_error("lid %lu: check failed: %s\n", + log_error("lid %lu: update failed: %s\n", recnum, g10_errstr(rc) ); else - log_info("lid %lu: checked\n", recnum ); + log_info("lid %lu: updated\n", recnum ); + + release_kbnode( keyblock ); keyblock = NULL; } } + if( rebuild ) { + log_info("adding new entries.\n"); + + rc = enum_keyblocks( 0, &kbpos, &keyblock ); + if( !rc ) { + while( !(rc = enum_keyblocks( 1, &kbpos, &keyblock )) ) { + rc = update_trust_record( keyblock ); + if( rc == -1 ) { /* not yet in trustdb: insert */ + PKT_public_key *pk = + find_kbnode( keyblock, PKT_PUBLIC_KEY + ) ->pkt->pkt.public_key; + rc = insert_trust_record( pk ); + if( rc && !pk->local_id ) + log_error("lid ?: insert failed: %s\n", + g10_errstr(rc) ); + else if( rc ) + log_error("lid %lu: insert failed: %s\n", + pk->local_id, g10_errstr(rc) ); + else + log_info("lid %lu: inserted\n", pk->local_id ); + } + else if( rc ) + log_error("lid %lu: update failed: %s\n", + lid_from_keyblock(keyblock), g10_errstr(rc) ); + else + log_info("lid %lu: updated\n", + lid_from_keyblock(keyblock) ); + } + } + if( rc && rc != -1 ) + log_error("enum_keyblocks failed: %s\n", g10_errstr(rc)); + + enum_keyblocks( 2, &kbpos, &keyblock ); /* close */ + release_kbnode( keyblock ); + } + } } @@ -1929,11 +1674,601 @@ clear_trust_checked_flag( PKT_public_key *pk ) + +static void +check_hint_sig( ulong lid, KBNODE keyblock, u32 *keyid, byte *uidrec_hash, + TRUSTREC *sigrec, int sigidx, ulong hint_owner ) +{ + KBNODE node; + int rc, state=0; + byte uhash[20]; + int is_selfsig; + PKT_signature *sigpkt = NULL; + + if( sigrec->r.sig.sig[sigidx].flag & SIGF_CHECKED ) + log_info(_("note: sig rec %lu[%d] in hintlist " + "of %lu but marked as checked\n"), + sigrec->recnum, sigidx, hint_owner ); + if( !(sigrec->r.sig.sig[sigidx].flag & SIGF_NOPUBKEY) ) + log_info(_("note: sig rec %lu[%d] in hintlist " + "of %lu but not marked\n"), + sigrec->recnum, sigidx, hint_owner ); + + /* find the correct signature packet */ + for( node=keyblock; node; node = node->next ) { + if( node->pkt->pkttype == PKT_USER_ID ) { + PKT_user_id *uidpkt = node->pkt->pkt.user_id; + + if( state ) + break; + rmd160_hash_buffer( uhash, uidpkt->name, uidpkt->len ); + if( !memcmp( uhash, uidrec_hash, 20 ) ) + state = 1; + } + else if( state && node->pkt->pkttype == PKT_SIGNATURE ) { + sigpkt = node->pkt->pkt.signature; + if( sigpkt->keyid[0] == keyid[0] + && sigpkt->keyid[1] == keyid[1] + && (sigpkt->sig_class&~3) == 0x10 ) + break; /* found */ + } + } + + if( !node ) { + log_error(_("lid %lu: user id not found in keyblock\n"), lid ); + return ; + } + + /* and check the sig */ + rc = check_key_signature( keyblock, node, &is_selfsig ); + if( is_selfsig ) { + log_error(_("lid %lu: self-signature in hintlist\n"), lid ); + return ; + } + if( !rc ) { /* valid signature */ + sigrec->r.sig.sig[sigidx].flag = SIGF_CHECKED | SIGF_VALID; + } + else if( rc == G10ERR_NO_PUBKEY ) { + log_info(_("key %08lX.%lu, uid %02X%02X: " + "very strange: no public key for signature %08lX\n"), + (ulong)keyid[1], lid, uhash[18], uhash[19], + (ulong)sigpkt->keyid[1] ); + sigrec->r.sig.sig[sigidx].flag = SIGF_NOPUBKEY; + } + else { + log_info(_("key %08lX.%lu, uid %02X%02X: " + "invalid signature: %s\n"), + (ulong)keyid[1], lid, + uhash[18], uhash[19], g10_errstr(rc)); + sigrec->r.sig.sig[sigidx].flag = SIGF_CHECKED; + } + sigrec->dirty = 1; +} + + +/**************** + * Process a hintlist. + * Fixme: this list is not anymore anchored to another + * record, so it should be put elsewehere in case of an error + */ +static void +process_hintlist( ulong hintlist, ulong hint_owner ) +{ + ulong hlst_rn; + int rc; + + for( hlst_rn = hintlist; hlst_rn; ) { + TRUSTREC hlstrec; + int hlst_idx; + + read_record( hlst_rn, &hlstrec, RECTYPE_HLST ); + + for( hlst_idx=0; hlst_idx < ITEMS_PER_HLST_RECORD; hlst_idx++ ) { + TRUSTREC dirrec; + TRUSTREC uidrec; + TRUSTREC tmprec; + KBNODE keyblock = NULL; + u32 keyid[2]; + ulong lid; + ulong r1, r2; + + lid = hlstrec.r.hlst.rnum[hlst_idx]; + if( !lid ) + continue; + + read_record( lid, &dirrec, 0 ); + /* make sure it points to a dir record: + * this should be true because it only makes sense to + * call this function if the dir record is available */ + if( dirrec.rectype != RECTYPE_DIR ) { + log_error(_("hintlist %lu[%d] of %lu " + "does not point to a dir record\n"), + hlst_rn, hlst_idx, hint_owner ); + continue; + } + if( !dirrec.r.dir.keylist ) { + log_error(_("lid %lu does not have a key\n"), lid ); + continue; + } + + /* get the keyblock */ + read_record( dirrec.r.dir.keylist, &tmprec, RECTYPE_KEY ); + rc = get_keyblock_byfprint( &keyblock, + tmprec.r.key.fingerprint, + tmprec.r.key.fingerprint_len ); + if( rc ) { + log_error(_("lid %lu: can't get keyblock: %s\n"), + lid, g10_errstr(rc) ); + continue; + } + keyid_from_fingerprint( tmprec.r.key.fingerprint, + tmprec.r.key.fingerprint_len, keyid ); + + /* Walk over all user ids and their signatures and check all + * the signature which are created by hint_owner */ + for( r1 = dirrec.r.dir.uidlist; r1; r1 = uidrec.r.uid.next ) { + TRUSTREC sigrec; + + read_record( r1, &uidrec, RECTYPE_UID ); + for( r2 = uidrec.r.uid.siglist; r2; r2 = sigrec.r.sig.next ) { + int i; + + read_record( r2, &sigrec, RECTYPE_SIG ); + sigrec.dirty = 0; + 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].lid != hint_owner ) + continue; /* not for us */ + /* some diagnostic messages */ + /* and do the signature check */ + check_hint_sig( lid, keyblock, keyid, + uidrec.r.uid.namehash, + &sigrec, i, hint_owner ); + } + if( sigrec.dirty ) + write_record( &sigrec ); + } + } + release_kbnode( keyblock ); + } /* loop over hlst entries */ + + /* delete this hlst record */ + hlst_rn = hlstrec.r.hlst.next; + delete_record( hlstrec.recnum ); + } /* loop over hintlist */ +} + + + +static void +upd_key_record( PKT_public_key *pk, TRUSTREC *drec, RECNO_LIST *recno_list ) +{ + TRUSTREC krec; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + ulong recno, newrecno; + + fingerprint_from_pk( pk, fpr, &fprlen ); + /* do we already have this key? */ + for( recno=drec->r.dir.keylist; recno; recno = krec.r.key.next ) { + read_record( recno, &krec, RECTYPE_KEY ); + if( krec.r.key.fingerprint_len == fprlen + && !memcmp( krec.r.key.fingerprint, fpr, fprlen ) ) + break; + } + if( recno ) { /* yes */ + ins_recno_list( recno_list, recno, RECTYPE_KEY ); + /* here we would compare/update the keyflags */ + } + else { /* no: insert this new key */ + memset( &krec, 0, sizeof(krec) ); + krec.rectype = RECTYPE_KEY; + krec.r.key.lid = drec->recnum; + krec.r.key.pubkey_algo = pk->pubkey_algo; + krec.r.key.fingerprint_len = fprlen; + memcpy(krec.r.key.fingerprint, fpr, fprlen ); + krec.recnum = newrecno = tdbio_new_recnum(); + write_record( &krec ); + ins_recno_list( recno_list, newrecno, RECTYPE_KEY ); + /* and put this new record at the end of the keylist */ + if( !(recno=drec->r.dir.keylist) ) { + /* this is the first key */ + drec->r.dir.keylist = newrecno; + drec->dirty = 1; + } + else { /* we already have a key, append it to the list */ + for( ; recno; recno = krec.r.key.next ) + read_record( recno, &krec, RECTYPE_KEY ); + krec.r.key.next = newrecno; + write_record( &krec ); + } + } +} + + +static void +upd_uid_record( PKT_user_id *uid, TRUSTREC *drec, RECNO_LIST *recno_list, + u32 *keyid, ulong *uidrecno, byte *uidhash ) +{ + TRUSTREC urec; + ulong recno, newrecno; + + rmd160_hash_buffer( uidhash, uid->name, uid->len ); + for( recno=drec->r.dir.uidlist; recno; recno = urec.r.uid.next ) { + read_record( recno, &urec, RECTYPE_UID ); + if( !memcmp( uidhash, urec.r.uid.namehash, 20 ) ) + break; + } + if( recno ) { + ins_recno_list( recno_list, recno, RECTYPE_UID ); + *uidrecno = recno; + } + else { /* new user id */ + memset( &urec, 0 , sizeof(urec) ); + urec.rectype = RECTYPE_UID; + urec.r.uid.lid = drec->recnum; + memcpy(urec.r.uid.namehash, uidhash, 20 ); + urec.recnum = newrecno = tdbio_new_recnum(); + write_record( &urec ); + ins_recno_list( recno_list, newrecno, RECTYPE_UID ); + /* and put this new record at the end of the uidlist */ + if( !(recno=drec->r.dir.uidlist) ) { /* this is the first uid */ + drec->r.dir.uidlist = newrecno; + drec->dirty = 1; + } + else { /* we already have an uid, append it to the list */ + for( ; recno; recno = urec.r.key.next ) + read_record( recno, &urec, RECTYPE_UID ); + urec.r.uid.next = newrecno; + write_record( &urec ); + } + *uidrecno = newrecno; + } +} + + +static void +upd_pref_record( PKT_signature *sig, TRUSTREC *drec, + u32 *keyid, ulong *uidrecno, byte *uidhash ) +{ + static struct { + sigsubpkttype_t subpkttype; + int preftype; + } prefs[] = { + { SIGSUBPKT_PREF_SYM, PREFTYPE_SYM }, + { SIGSUBPKT_PREF_HASH, PREFTYPE_HASH }, + { SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR }, + { 0, 0 } + }; + TRUSTREC urec, prec; + const byte *s; + size_t n; + int k, i; + ulong recno_tbl[10]; + int recno_idx = 0; + ulong recno; + + /* we need the uid record */ + read_record( *uidrecno, &urec, RECTYPE_UID ); + + /* First delete all pref records + * This is much simpler than checking whether we have to + * do update the record at all - the record cache may care about it */ + for( recno=urec.r.uid.prefrec; recno; recno = prec.r.pref.next ) { + read_record( recno, &prec, RECTYPE_PREF ); + delete_record( recno ); + } + + /* and write the new ones */ + i = 0; + for(k=0; prefs[k].subpkttype; k++ ) { + s = parse_sig_subpkt2( sig, prefs[k].subpkttype, &n ); + if( s ) { + while( n ) { + if( !i || i >= ITEMS_PER_PREF_RECORD ) { + if( recno_idx >= DIM(recno_tbl)-1 ) { + log_info("too many preferences\n"); + break; + } + if( i ) { + recno_tbl[recno_idx]=tdbio_new_recnum(); + prec.recnum = recno_tbl[recno_idx++]; + write_record( &prec ); + } + memset( &prec, 0, sizeof prec ); + prec.rectype = RECTYPE_PREF; + prec.r.pref.lid = drec->recnum; + i = 0; + } + prec.r.pref.data[i++] = prefs[k].preftype; + prec.r.pref.data[i++] = *s++; + n--; + } + } + } + if( i ) { /* write the last one */ + recno_tbl[recno_idx]=tdbio_new_recnum(); + prec.recnum = recno_tbl[recno_idx++]; + write_record( &prec ); + } + /* now link them together */ + for(i=0; i < recno_idx-1; i++ ) { + read_record( recno_tbl[i], &prec, RECTYPE_PREF ); + prec.r.pref.next = recno_tbl[i+1]; + write_record( &prec ); + } + /* don't need to write the last one, but update the uid */ + urec.r.uid.prefrec = recno_idx? recno_tbl[0] : 0; + write_record( &urec ); +} + + + /**************** - * Update all the info from the public keyblock, the signatures-checked - * flag is reset. The key must already exist in the keydb. - * - * Implementation of this function needs a cache for tdbio record updates + * Note: A signature made with a secondayr key is not considered a + * self-signature. + */ +static void +upd_sig_record( PKT_signature *sig, TRUSTREC *drec, + u32 *keyid, ulong *uidrecno, byte *uidhash, + KBNODE keyblock, KBNODE signode ) +{ + int rc; + ulong lid = drec->recnum; + + if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { + if( (sig->sig_class&~3) == 0x10 ) { + /* must verify this selfsignature here, so that we can + * build the preference record and validate the uid record + */ + if( !*uidrecno ) { + log_error("key %08lX: self-signature without user id\n", + (ulong)keyid[1] ); + } + else if( (rc = check_key_signature( keyblock, signode, NULL ))) { + log_error("key %08lX, uid %02X%02X: " + "invalid self-signature: %s\n", (ulong)keyid[1], + uidhash[18], uidhash[19], g10_errstr(rc) ); + } + else { /* valid sig (may be revoked, but that doesn't matter here) */ + upd_pref_record( sig, drec, keyid, uidrecno, uidhash ); + } + } + else {/* is revocation sig etc */ + /* FIXME */ + } + } + else if( !*uidrecno ) + ; /* skip record with direct key signatures here */ + else if( (sig->sig_class&~3) == 0x10 ) { + /* We simply insert the signature into the sig records but + * avoid duplicate ones. We do not check them here because + * there is a big chance, that we import required public keys + * later. The problem with this is that we must somewhere store + * the information about this signature (we need a record id). + * We do this by using the record type shadow dir, which will + * be converted to a dir record as soon as a new public key is + * inserted into the trustdb. + */ + TRUSTREC urec, rec; + ulong recno; + TRUSTREC delrec; + int delrecidx; + int newflag = 0; + ulong newlid = 0; + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); + + delrec.recnum = 0; + /* we need the uid record */ + read_record( *uidrecno, &urec, RECTYPE_UID ); + + for( recno = urec.r.uid.siglist; recno; recno = rec.r.sig.next ) { + int i; + + read_record( recno, &rec, RECTYPE_SIG ); + for(i=0; i < SIGS_PER_RECORD; i++ ) { + TRUSTREC tmp; + if( !rec.r.sig.sig[i].lid ) { + if( !delrec.recnum ) { + delrec = rec; + delrecidx = i; + } + continue; /* skip deleted sigs */ + } + if( rec.r.sig.sig[i].flag & SIGF_CHECKED ) + continue; /* we already checked this signature */ + if( rec.r.sig.sig[i].flag & SIGF_NOPUBKEY ) + continue; /* we do not have the public key */ + + read_record( rec.r.sig.sig[i].lid, &tmp, 0 ); + if( tmp.rectype == RECTYPE_DIR ) { + /* In this case we should now be able to check + * the signature: */ + rc = check_key_signature( keyblock, signode, NULL ); + if( !rc ) { /* valid signature */ + rec.r.sig.sig[i].flag = SIGF_CHECKED | SIGF_VALID; + } + else if( rc == G10ERR_NO_PUBKEY ) { + log_info(_("key %08lX.%lu, uid %02X%02X: " + "weird: no public key for signature %08lX\n"), + (ulong)keyid[1], lid, uidhash[18], + uidhash[19], (ulong)sig->keyid[1] ); + rec.r.sig.sig[i].flag = SIGF_NOPUBKEY; + } + else { + log_info(_("key %08lX.%lu, uid %02X%02X: " + "invalid signature: %s\n"), + (ulong)keyid[1], lid, + uidhash[18], uidhash[19], g10_errstr(rc)); + rec.r.sig.sig[i].flag = SIGF_CHECKED; + } + write_record( &rec ); + goto ready; + } + else if( tmp.rectype == RECTYPE_SDIR ) { + /* must 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 )) { + log_info(_("key %08lX.%lu, uid %02X%02X: " + "has shadow dir %lu but not yet marked.\n"), + (ulong)keyid[1], lid, + uidhash[18], uidhash[19], tmp.recnum ); + rec.r.sig.sig[i].flag = SIGF_NOPUBKEY; + write_record( &rec ); + /* fixme: should we verify that the record is + * in the hintlist? - This case here should anyway + * never occur */ + goto ready; + } + } + else { + log_error("sig record %lu[%d] points to wrong record.\n", + rec.r.sig.sig[i].lid, i ); + die_invalid_db(); + } + } + } + /* at this point, we have verified, that the signature is not in + * our list of signatures. Add a new record with that signature + * and if the public key is there, check the signature. */ + rc = get_pubkey( pk, sig->keyid ); + if( !rc ) { + /* check that we already have the record in the trustdb; + * if not we should not check the signature, because we do + * not have the lid of the signature and therefore can't add + * a signature record. We will fake a no_pubkey error, so + * that this is handled, like we do not have the publick key at + * at all. The alternative would be a recursive insert of + * records - but that is not good. + */ + rc = query_trust_record( pk ); + if( rc == -1 ) + rc = G10ERR_NO_PUBKEY; + else + rc = check_key_signature( keyblock, signode, NULL ); + } + if( !rc ) { /* valid signature */ + newlid = pk->local_id; /* this is the pk of the signature */ + if( !newlid ) + BUG(); + newflag = SIGF_CHECKED | SIGF_VALID; + } + else if( rc == G10ERR_NO_PUBKEY ) { + if( opt.verbose ) + log_info(_("key %08lX.%lu, uid %02X%02X: " + "no public key for signature %08lX\n"), + (ulong)keyid[1], lid, uidhash[18], + uidhash[19], (ulong)sig->keyid[1] ); + newflag = SIGF_NOPUBKEY; + } + else { + log_info(_("key %08lX.%lu, uid %02X%02X: " + "invalid signature: %s\n"), + (ulong)keyid[1], lid, uidhash[18], uidhash[19], + g10_errstr(rc)); + newflag = SIGF_CHECKED; + } + free_public_key( pk ); + + if( !newlid ) { /* create a shadow dir record */ + TRUSTREC sdir, hlst, tmphlst; + int tmpidx; + /* 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_dir failed: %s\n", g10_errstr(rc)); + die_invalid_db(); + } + 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; + sdir.r.sdir.hintlist = 0; + write_record( &sdir ); + } + newlid = sdir.recnum; + /* Put the record number into the hintlist. + * (It is easier to use the lid and not the record number of the + * key to save some space (assuming that a signator has + * signed more than one user id - and it is easier to implement.) + */ + tmphlst.recnum = 0; + for( recno=sdir.r.sdir.hintlist; recno; recno = hlst.r.hlst.next) { + int i; + read_record( recno, &hlst, RECTYPE_HLST ); + for( i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { + if( !hlst.r.hlst.rnum[i] ) { + if( !tmphlst.recnum ) { + tmphlst = hlst; + tmpidx = i; + } + } + else if( hlst.r.hlst.rnum[i] == lid ) + goto have_hint; + } + } + /* not yet in the hint list, write it */ + if( tmphlst.recnum ) { /* we have an empty slot */ + tmphlst.r.hlst.rnum[tmpidx] = lid; + write_record( &tmphlst ); + } + else { /* must append a new hlst record */ + memset( &hlst, 0, sizeof hlst ); + hlst.recnum = tdbio_new_recnum(); + hlst.rectype = RECTYPE_HLST; + hlst.r.hlst.next = sdir.r.sdir.hintlist; + hlst.r.hlst.rnum[0] = lid; + write_record( &hlst ); + sdir.r.sdir.hintlist = hlst.recnum; + write_record( &sdir ); + } + have_hint: /* "goto considered useful" (don't tell Dijkstra) */ + ; + } + + if( delrec.recnum ) { /* we can reuse a deleted slot */ + delrec.r.sig.sig[delrecidx].lid = newlid; + delrec.r.sig.sig[delrecidx].flag= newflag; + write_record( &delrec ); + } + else { /* must insert a new sig record */ + TRUSTREC tmp; + + memset( &tmp, 0, sizeof tmp ); + tmp.recnum = tdbio_new_recnum(); + tmp.rectype = RECTYPE_SIG; + tmp.r.sig.lid = lid; + tmp.r.sig.next = urec.r.uid.siglist; + tmp.r.sig.sig[0].lid = newlid; + tmp.r.sig.sig[0].flag= newflag; + write_record( &tmp ); + urec.r.uid.siglist = tmp.recnum; + write_record( &urec ); + } + + ready: + ; + } + else { + /* handle other sig classes */ + } + +} + + +/**************** + * Update all the info from the public keyblock. + * The key must already exist in the keydb. + * This function is responsible for checking the signatures in cases + * where the public key is already available. If we no not have the public + * key, the check is done by some special code in insert_trust_record(). */ int update_trust_record( KBNODE keyblock ) @@ -1942,13 +2277,12 @@ update_trust_record( KBNODE keyblock ) KBNODE node; TRUSTREC drec; TRUSTREC krec; - TRUSTREC prec; TRUSTREC urec; + TRUSTREC prec; TRUSTREC helprec; - int modified = 0; int rc = 0; u32 keyid[2]; /* keyid of primary key */ - ulong recno, newrecno, lastrecno; + ulong recno, lastrecno; ulong uidrecno = 0; byte uidhash[20]; RECNO_LIST recno_list = NULL; /* list of verified records */ @@ -1958,189 +2292,46 @@ update_trust_record( KBNODE keyblock ) rc = get_dir_record( primary_pk, &drec ); if( rc ) return rc; + if( !primary_pk->local_id ) + primary_pk->local_id = drec.recnum; keyid_from_pk( primary_pk, keyid ); /* fixme: start a transaction */ /* now update keys and user ids */ 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; - byte fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - + switch( node->pkt->pkttype ) { + case PKT_PUBLIC_KEY: + case PKT_PUBLIC_SUBKEY: uidrecno = 0; + upd_key_record( node->pkt->pkt.public_key, &drec, &recno_list ); + break; - fingerprint_from_pk( pk, fpr, &fprlen ); - /* do we already have this key? */ - for( recno=drec.r.dir.keylist; recno; recno = krec.r.key.next ) { - read_record( recno, &krec, RECTYPE_KEY ); - if( krec.r.key.fingerprint_len == fprlen - && !memcmp( krec.r.key.fingerprint, fpr, fprlen ) ) - break; + case PKT_USER_ID: + if( drec.dirty ) { /* upd_pref_record may read the drec */ + write_record( &drec ); + drec.dirty = 0; } - if( recno ) { /* yes */ - ins_recno_list( &recno_list, recno, RECTYPE_KEY ); - /* here we would compare/update the keyflags */ - } - else { /* no: insert this new key */ - memset( &krec, 0, sizeof(krec) ); - krec.rectype = RECTYPE_KEY; - krec.r.key.lid = drec.recnum; - krec.r.key.pubkey_algo = pk->pubkey_algo; - krec.r.key.fingerprint_len = fprlen; - memcpy(krec.r.key.fingerprint, fpr, fprlen ); - krec.recnum = newrecno = tdbio_new_recnum(); - write_record( &krec ); - ins_recno_list( &recno_list, newrecno, RECTYPE_KEY ); - /* and put this new record at the end of the keylist */ - if( !(recno=drec.r.dir.keylist) ) { - /* this is the first key */ - drec.r.dir.keylist = newrecno; - modified = 1; - } - else { /* we already have key, append it to the list */ - for( ; recno; recno = krec.r.key.next ) - read_record( recno, &krec, RECTYPE_KEY ); - krec.r.key.next = newrecno; - write_record( &krec ); - } - } /* end insert new key */ - } /* end packet type public key packet */ - else if( node->pkt->pkttype == PKT_USER_ID ) { - PKT_user_id *uid = node->pkt->pkt.user_id; - TRUSTREC urec; - - rmd160_hash_buffer( uidhash, uid->name, uid->len ); - for( recno=drec.r.dir.uidlist; recno; recno = urec.r.uid.next ) { - read_record( recno, &urec, RECTYPE_UID ); - if( !memcmp( uidhash, urec.r.uid.namehash, 20 ) ) - break; - } - if( recno ) { - ins_recno_list( &recno_list, recno, RECTYPE_UID ); - uidrecno = recno; - } - else { /* new user id */ - memset( &urec, 0 , sizeof(urec) ); - urec.rectype = RECTYPE_UID; - urec.r.uid.lid = drec.recnum; - memcpy(urec.r.uid.namehash, uidhash, 20 ); - urec.recnum = newrecno = tdbio_new_recnum(); - write_record( &urec ); - ins_recno_list( &recno_list, newrecno, RECTYPE_UID ); - /* and put this new record at the end of the uidlist */ - if( !(recno=drec.r.dir.uidlist) ) { - /* this is the first uid */ - drec.r.dir.uidlist = newrecno; - modified = 1; - } - else { /* we already have an uid, append it to the list */ - for( ; recno; recno = urec.r.key.next ) - read_record( recno, &urec, RECTYPE_UID ); - urec.r.uid.next = newrecno; - write_record( &urec ); - } - uidrecno = newrecno; - } - } - else if( node->pkt->pkttype == PKT_SIGNATURE ) { - PKT_signature *sig = node->pkt->pkt.signature; - - 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( !uidrecno ) { - 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], - uidhash[18], uidhash[19], g10_errstr(rc) ); - rc = 0; - } - else { /* build the prefrecord */ - static struct { - sigsubpkttype_t subpkttype; - int preftype; - } prefs[] = { - { SIGSUBPKT_PREF_SYM, PREFTYPE_SYM }, - { SIGSUBPKT_PREF_HASH, PREFTYPE_HASH }, - { SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR }, - { 0, 0 } - }; - const byte *s; - size_t n; - int k, i; - ulong recno_tbl[10]; - int recno_idx = 0; - - read_record( uidrecno, &urec, RECTYPE_UID ); - - /* first delete all pref records */ - for(recno=urec.r.uid.prefrec ; recno; - recno = prec.r.pref.next ) { - read_record( recno, &prec, RECTYPE_PREF ); - delete_record( recno ); - } + upd_uid_record( node->pkt->pkt.user_id, &drec, &recno_list, + keyid, &uidrecno, uidhash ); + break; - /* and write the new ones */ - i = 0; - for(k=0; prefs[k].subpkttype; k++ ) { - s = parse_sig_subpkt2( sig, prefs[k].subpkttype, &n ); - if( s ) { - while( n ) { - if( !i || i >= ITEMS_PER_PREF_RECORD ) { - if( recno_idx >= DIM(recno_tbl)-1 ) { - log_info("too many preferences\n"); - break; - } - if( i ) { - recno_tbl[recno_idx]=tdbio_new_recnum(); - prec.recnum = recno_tbl[recno_idx++]; - write_record( &prec ); - } - memset( &prec, 0, sizeof prec ); - prec.rectype = RECTYPE_PREF; - prec.r.pref.lid = drec.recnum; - i = 0; - } - prec.r.pref.data[i++] = prefs[k].preftype; - prec.r.pref.data[i++] = *s++; - n--; - } - } - } - if( i ) { /* write the last one */ - recno_tbl[recno_idx]=tdbio_new_recnum(); - prec.recnum = recno_tbl[recno_idx++]; - write_record( &prec ); - } - /* now link them together */ - for(i=0; i < recno_idx-1; i++ ) { - read_record( recno_tbl[i], &prec, RECTYPE_PREF ); - prec.r.pref.next = recno_tbl[i+1]; - write_record( &prec ); - } - /* don't need to write the last one, but update the uid */ - urec.r.uid.prefrec = recno_idx? recno_tbl[0] : 0; - write_record( &urec ); - } - } - else if( 0 /* is revocation sig etc */ ) { - /* handle it here */ - } - else { /* not a selfsignature */ + case PKT_SIGNATURE: + if( drec.dirty ) { /* upd_sig_recrod may read the drec */ + write_record( &drec ); + drec.dirty = 0; } + upd_sig_record( node->pkt->pkt.signature, &drec, + keyid, &uidrecno, uidhash, keyblock, node ); + break; + + default: + break; } } /* end loop over all nodes */ - /* now delete keyrecords from the trustdb which are not anymore used */ + /* delete keyrecords from the trustdb which are not anymore used */ lastrecno = 0; for( recno=drec.r.dir.keylist; recno; recno = krec.r.key.next ) { read_record( recno, &krec, RECTYPE_KEY ); @@ -2148,7 +2339,7 @@ update_trust_record( KBNODE keyblock ) /* delete this one */ if( !lastrecno ) { drec.r.dir.keylist = krec.r.key.next; - modified = 1; + drec.dirty = 1; } else { read_record( lastrecno, &helprec, RECTYPE_KEY ); @@ -2160,7 +2351,7 @@ update_trust_record( KBNODE keyblock ) else lastrecno = recno; } - /* now delete uid records and their pref records from the + /* delete uid records and sig and their pref records from the * trustdb which are not anymore used */ lastrecno = 0; for( recno=drec.r.dir.uidlist; recno; recno = urec.r.uid.next ) { @@ -2170,7 +2361,7 @@ update_trust_record( KBNODE keyblock ) /* delete this one */ if( !lastrecno ) { drec.r.dir.uidlist = urec.r.uid.next; - modified = 1; + drec.dirty = 1; } else { read_record( lastrecno, &helprec, RECTYPE_UID ); @@ -2181,6 +2372,10 @@ update_trust_record( KBNODE keyblock ) 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 ); } else @@ -2191,7 +2386,7 @@ update_trust_record( KBNODE keyblock ) if( rc ) ; /* fixme: cancel transaction */ - else if( modified ) { + else if( drec.dirty ) { drec.r.dir.dirflags &= ~DIRF_CHECKED; /* reset flag */ write_record( &drec ); /* fixme: commit_transaction */ @@ -2203,29 +2398,33 @@ update_trust_record( KBNODE keyblock ) /**************** * Insert a trust record into the TrustDB - * This function fails if this record already exists. - * - * We build everything we can do at this point. We cannot build - * the sig records, because their LIDs are needed and we may not have them. + * This function assumes that the record does not yet exist. */ int insert_trust_record( PKT_public_key *pk ) { TRUSTREC dirrec; + TRUSTREC shadow; KBNODE keyblock = NULL; KBNODE node; - byte *fingerprint; + byte fingerprint[MAX_FINGERPRINT_LEN]; size_t fingerlen; int rc = 0; - + ulong hintlist = 0; if( pk->local_id ) log_bug("pk->local_id=%lu\n", pk->local_id ); - fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen ); + fingerprint_from_pk( pk, fingerprint, &fingerlen ); /* fixme: assert that we do not have this record. * we can do this by searching for the primary keyid + * + * fixme: If there is no such key we should look whether one + * of the subkeys has been used to sign another key and in this case + * we got the key anyway. Because a secondary key can't be used + * without a primary key (it is needed to bind the secondary one + * to the primary one which has the user ids etc.) */ /* get the keyblock which has the key */ @@ -2236,9 +2435,37 @@ insert_trust_record( PKT_public_key *pk ) goto leave; } + /* check that we used the primary key (we are little bit paranoid) */ + { PKT_public_key *a_pk; + node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); + a_pk = node->pkt->pkt.public_key; + if( cmp_public_keys( a_pk, pk ) ) { + log_error("did not use primary key for insert_trust_record()\n"); + rc = G10ERR_GENERAL; + goto leave; + } + } + + /* We have to look for a shadow dir record which must be reused + * as the dir record. And: check all signatures which are listed + * in the hintlist of the shadow dir record. + */ + rc = tdbio_search_sdir( pk->keyid, pk->pubkey_algo, &shadow ); + if( rc && rc != -1 ) { + log_error("tdbio_search_dir failed: %s\n", g10_errstr(rc)); + die_invalid_db(); + } memset( &dirrec, 0, sizeof dirrec ); dirrec.rectype = RECTYPE_DIR; - dirrec.recnum = tdbio_new_recnum(); + if( !rc ) { + /* hey, great: this key has already signed other keys + * convert this to a real directory entry */ + hintlist = shadow.r.sdir.hintlist; + dirrec.recnum = shadow.recnum; + } + else { + dirrec.recnum = tdbio_new_recnum(); + } dirrec.r.dir.lid = dirrec.recnum; write_record( &dirrec ); @@ -2258,10 +2485,12 @@ insert_trust_record( PKT_public_key *pk ) /* and put all the other stuff into the keydb */ rc = update_trust_record( keyblock ); - + if( !rc ) + process_hintlist( hintlist, dirrec.r.dir.lid ); leave: - m_free(fingerprint); + if( rc && hintlist ) + ; /* fixme: the hintlist is not anymore anchored */ release_kbnode( keyblock ); return rc; } diff --git a/g10/trustdb.h b/g10/trustdb.h index db2743fbf..b0f39770f 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -55,6 +55,7 @@ int get_ownertrust_info( ulong lid ); byte *get_pref_data( ulong lid, const byte *namehash, size_t *ret_n ); int is_algo_in_prefs( ulong lid, int preftype, int algo ); int keyid_from_lid( ulong lid, u32 *keyid ); +ulong lid_from_keyblock( KBNODE keyblock ); int query_trust_record( PKT_public_key *pk ); int clear_trust_checked_flag( PKT_public_key *pk ); int insert_trust_record( PKT_public_key *pk ); |