diff options
Diffstat (limited to '')
-rw-r--r-- | g10/trustdb.c | 1393 |
1 files changed, 689 insertions, 704 deletions
diff --git a/g10/trustdb.c b/g10/trustdb.c index 5f391d028..7a6ec3bd1 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -65,7 +65,7 @@ struct trust_info { typedef struct trust_seg_list *TRUST_SEG_LIST; struct trust_seg_list { TRUST_SEG_LIST next; - int nseg; /* number of segmens */ + int nseg; /* number of segments */ int dup; TRUST_INFO seg[1]; /* segment list */ }; @@ -86,14 +86,9 @@ static LOCAL_ID_INFO *new_lid_table(void); static void release_lid_table( LOCAL_ID_INFO *tbl ); static int ins_lid_table_item( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag ); static int qry_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned *flag ); -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 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 ); @@ -102,8 +97,6 @@ static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); * which are the ones from our secrings */ static LOCAL_ID_INFO *ultikey_table; -static ulong last_trust_web_key; -static TRUST_SEG_LIST last_trust_web_tslist; #define HEXTOBIN(a) ( (a) >= '0' && (a) <= '9' ? ((a)-'0') : \ @@ -277,19 +270,6 @@ qry_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned *flag ) return -1; } -static void -upd_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag ) -{ - LOCAL_ID_INFO a; - - for( a = tbl[lid & 0x0f]; a; a = a->next ) - if( a->lid == lid ) { - a->flag = flag; - return; - } - BUG(); -} - /**************** @@ -301,12 +281,19 @@ keyid_from_lid( ulong lid, u32 *keyid ) TRUSTREC rec; int rc; - rc = tdbio_read_record( lid, &rec, RECTYPE_DIR ); + rc = tdbio_read_record( lid, &rec, 0 ); if( rc ) { log_error("error reading dir record for LID %lu: %s\n", lid, g10_errstr(rc)); return G10ERR_TRUSTDB; } + if( rec.rectype == RECTYPE_SDIR ) + return 0; + if( rec.rectype != RECTYPE_DIR ) { + log_error("lid %lu: expected dir record, got type %d\n", + lid, rec.rectype ); + return G10ERR_TRUSTDB; + } if( !rec.r.dir.keylist ) { log_error("no primary key for LID %lu\n", lid ); return G10ERR_TRUSTDB; @@ -342,7 +329,6 @@ lid_from_keyblock( KBNODE keyblock ) - /**************** * Walk through the signatures of a public key. * The caller must provide a context structure, with all fields set @@ -364,9 +350,13 @@ walk_sigrecs( SIGREC_CONTEXT *c ) r = &c->ctl.rec; if( !c->ctl.init_done ) { c->ctl.init_done = 1; - read_record( c->lid, r, RECTYPE_DIR ); + read_record( c->lid, r, 0 ); + if( r->rectype != RECTYPE_DIR ) { + c->ctl.eof = 1; + return -1; /* return eof */ + } c->ctl.nextuid = r->r.dir.uidlist; - /* force a read (what a bad bad hack) */ + /* force a read */ c->ctl.index = SIGS_PER_RECORD; r->r.sig.next = 0; } @@ -503,17 +493,18 @@ print_user_id( const char *text, u32 *keyid ) m_free(p); } -static void + +static int print_keyid( FILE *fp, ulong lid ) { u32 ki[2]; if( keyid_from_lid( lid, ki ) ) - fprintf(fp, "????????.%lu", lid ); + return fprintf(fp, "????????.%lu", lid ); else - fprintf(fp, "%08lX.%lu", (ulong)ki[1], lid ); + return fprintf(fp, "%08lX.%lu", (ulong)ki[1], lid ); } -static void +static int print_trust( FILE *fp, unsigned trust ) { int c; @@ -525,14 +516,32 @@ print_trust( FILE *fp, unsigned trust ) case TRUST_MARGINAL: c = 'm'; break; case TRUST_FULLY: c = 'f'; break; case TRUST_ULTIMATE: c = 'u'; break; - default: fprintf(fp, "%02x", trust ); return; + default: fprintf(fp, "%02x", trust ); return 2; } putc(c, fp); + return 1; +} + + +static int +print_sigflags( FILE *fp, unsigned flags ) +{ + if( flags & SIGF_CHECKED ) { + fprintf(fp,"%c%c%c", + (flags & SIGF_VALID) ? 'V':'-', + (flags & SIGF_EXPIRED) ? 'E':'-', + (flags & SIGF_REVOKED) ? 'R':'-'); + } + else if( flags & SIGF_NOPUBKEY) + fputs("?--", fp); + else + fputs("---", fp); + return 3; } /* (a non-recursive algorithm would be easier) */ static int -do_list_sigs( ulong root, ulong pubkey, int depth, +do_list_sigs( ulong root, ulong pk_lid, int depth, LOCAL_ID_INFO *lids, unsigned *lineno ) { SIGREC_CONTEXT sx; @@ -540,26 +549,29 @@ do_list_sigs( ulong root, ulong pubkey, int depth, u32 keyid[2]; memset( &sx, 0, sizeof sx ); - sx.lid = pubkey; + sx.lid = pk_lid; for(;;) { rc = walk_sigrecs( &sx ); if( rc ) break; rc = keyid_from_lid( sx.sig_lid, keyid ); if( rc ) { - printf("%6u: %*s????????.%lu:%02x\n", *lineno, depth*4, "", - sx.sig_lid, sx.sig_flag ); + printf("%6u: %*s????????.%lu:", *lineno, depth*4, "", sx.sig_lid ); + print_sigflags( stdout, sx.sig_flag ); + putchar('\n'); ++*lineno; } else { - printf("%6u: %*s%08lX.%lu:%02x ", *lineno, depth*4, "", - (ulong)keyid[1], sx.sig_lid, sx.sig_flag ); - /* check whether we already checked this pubkey */ + printf("%6u: %*s%08lX.%lu:", *lineno, depth*4, "", + (ulong)keyid[1], sx.sig_lid ); + print_sigflags( stdout, sx.sig_flag ); + putchar(' '); + /* check whether we already checked this pk_lid */ if( !qry_lid_table_flag( ultikey_table, sx.sig_lid, NULL ) ) { print_user_id("[ultimately trusted]", keyid); ++*lineno; } - else if( sx.sig_lid == pubkey ) { + else if( sx.sig_lid == pk_lid ) { printf("[self-signature]\n"); ++*lineno; } @@ -632,7 +644,7 @@ list_records( ulong lid ) tdbio_dump_record( &dr, stdout ); for( recno=dr.r.dir.keylist; recno; recno = rec.r.key.next ) { - rc = tdbio_read_record( recno, &rec, RECTYPE_KEY ); + rc = tdbio_read_record( recno, &rec, 0 ); if( rc ) { log_error("lid %lu: read key record failed: %s\n", lid, g10_errstr(rc)); @@ -681,197 +693,241 @@ list_records( ulong lid ) /**************** - * Function to collect all trustpaths + * Given the directory record of a key, check whether we can + * find a path to an ultimately trusted key. We do this by + * checking all key signatures up to a some depth. */ static int -do_list_path( TRUST_INFO *stack, int depth, int max_depth, - LOCAL_ID_INFO *lids, TRUST_SEG_LIST *tslist ) +verify_key( int depth, int max_depth, TRUSTREC *drec ) { - SIGREC_CONTEXT sx; - unsigned last_depth; - int rc; + ulong rn, uidrn; + int marginal=0; + int fully=0; + size_t dbglen; + + /* Note: If used stack size is an issue, we could reuse the + * trustrec vars and reread them as needed */ + + dbglen = printf("verify_key: depth=%d %*s", depth, depth*3,"" ); + dbglen += print_keyid( stdout, drec->recnum ); + dbglen += printf(" ot="); + dbglen += print_trust(stdout, drec->r.dir.ownertrust ); + dbglen += printf(" -> "); + + if( depth >= max_depth ) { + /* max cert_depth reached */ + puts("undefined (too deep)"); + return TRUST_UNDEFINED; + } + if( !qry_lid_table_flag( ultikey_table, drec->r.dir.lid, NULL ) ) { + /* we are at the end of a path */ + puts("ultimate"); + return TRUST_ULTIMATE; + } + + /* loop over all user-ids */ + for( rn = drec->r.dir.uidlist; rn; rn = uidrn ) { + TRUSTREC rec; /* used for uids and sigs */ + ulong sigrn; + + read_record( rn, &rec, RECTYPE_UID ); + uidrn = rec.r.uid.next; + /* fixme: continue if the uidrec is not marked valid */ + + /* loop over all signature records */ + for( rn = rec.r.uid.siglist; rn; rn = sigrn ) { + int i; - assert(depth); + read_record( rn, &rec, RECTYPE_SIG ); + sigrn = rec.r.sig.next; - /*printf("%2lu/%d: scrutinizig\n", stack[depth-1], depth);*/ - if( depth >= max_depth || depth >= MAX_LIST_SIGS_DEPTH-1 ) { - /*printf("%2lu/%d: too deeply nested\n", stack[depth-1], depth);*/ - return 0; - } - 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 )) ) { - TRUST_SEG_LIST tsl, t2, tl; - int i; + for(i=0; i < SIGS_PER_RECORD; i++ ) { + TRUSTREC tmp; + int ot, nt; - if( !(sx.sig_flag & SIGF_CHECKED) ) - continue; /* only checked sigs */ - if( !(sx.sig_flag & SIGF_VALID) ) - continue; /* and, of course, only valid sigs */ - if( (sx.sig_flag & SIGF_REVOKED) ) - continue; /* and skip revoked sigs */ - - stack[depth].lid = sx.sig_lid; - stack[depth].trust = 0; - if( qry_lid_table_flag( lids, sx.sig_lid, &last_depth) ) { - /*printf("%2lu/%d: marked\n", sx.sig_lid, depth );*/ - ins_lid_table_item( lids, sx.sig_lid, depth); - last_depth = depth; - } - else if( depth < last_depth ) { - /*printf("%2lu/%d: last_depth=%u - updated\n", sx.sig_lid, depth, last_depth);*/ - last_depth = depth; - upd_lid_table_flag( lids, sx.sig_lid, depth); - } + if( !rec.r.sig.sig[i].lid ) + continue; /* skip deleted sigs */ + if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) ) + continue; /* skip unchecked signatures */ + if( !(rec.r.sig.sig[i].flag & SIGF_VALID) ) + continue; /* skip invalid signatures */ + if( (rec.r.sig.sig[i].flag & SIGF_EXPIRED) ) + continue; /* skip expired signatures */ + if( (rec.r.sig.sig[i].flag & SIGF_REVOKED) ) + continue; /* skip revoked signatures */ + /* fixme: skip duplicates */ + + read_record( rec.r.sig.sig[i].lid, &tmp, RECTYPE_DIR ); + ot = tmp.r.dir.ownertrust & TRUST_MASK; + #if 0 /* Does not work, because the owner trust of our + * own keys is not always set + * -- fix this in verify_own_keys() ? */ + if( ot < TRUST_MARGINAL ) { + printf(". "); + continue; /* ownertrust is too low; don't need to check */ + } + #endif + if( ot >= TRUST_FULLY ) + ot = TRUST_FULLY; /* just in case */ + + puts(""); + nt = verify_key( depth+1, max_depth, &tmp ) & TRUST_MASK; + if( nt < TRUST_MARGINAL ) { + printf("%*s* ", dbglen, ""); + dbglen += 2; + continue; + } - if( last_depth < depth ) - /*printf("%2lu/%d: already visited\n", sx.sig_lid, depth)*/; - else if( !qry_lid_table_flag( ultikey_table, sx.sig_lid, NULL ) ) { - /* found end of path; store it, ordered by path length */ - tsl = m_alloc( sizeof *tsl + depth*sizeof(TRUST_INFO) ); - tsl->nseg = depth+1; - tsl->dup = 0; - for(i=0; i <= depth; i++ ) - tsl->seg[i] = stack[i]; - for(t2=*tslist,tl=NULL; t2; tl=t2, t2 = t2->next ) - if( depth < t2->nseg ) - break; - if( !tl ) { - tsl->next = t2; - *tslist = tsl; - } - else { - tsl->next = t2; - tl->next = tsl; + if( nt == TRUST_ULTIMATE ) { + /* we have signed this key and only in this special case + * we assume a completes-needed or marginals-needed of 1 */ + printf("%*s", dbglen, ""); + if( ot == TRUST_MARGINAL ) + puts("marginal (1st level)"); + else if( ot == TRUST_FULLY ) + puts("fully (1st level)"); + else + puts("????? (1st level)"); + return ot; + } + + if( nt >= TRUST_FULLY ) + fully++; + if( nt >= TRUST_MARGINAL ) + marginal++; + + if( fully >= opt.completes_needed + || marginal >= opt.marginals_needed ) { + printf("%*s", dbglen, ""); + puts("fully"); + return TRUST_FULLY; + } } - /*putchar('.'); fflush(stdout);*/ - /*printf("%2lu/%d: found\n", sx.sig_lid, depth);*/ - } - else { - rc = do_list_path( stack, depth+1, max_depth, lids, tslist); - if( rc && rc != -1 ) - break; } } - return rc==-1? 0 : rc; + printf("%*s", dbglen, ""); + if( marginal ) { + puts("marginal"); + return TRUST_MARGINAL; + } + puts("undefined"); + return TRUST_UNDEFINED; } -/**************** - * Make a list of trust paths - */ static int -make_tsl( ulong lid, TRUST_SEG_LIST *ret_tslist ) +list_paths( int depth, int max_depth, TRUSTREC *drec ) { - int i, rc; - LOCAL_ID_INFO *lids = new_lid_table(); - TRUST_INFO stack[MAX_LIST_SIGS_DEPTH]; - TRUST_SEG_LIST tsl, tslist; - int max_depth = 4; - - tslist = *ret_tslist = NULL; - - if( !qry_lid_table_flag( ultikey_table, lid, NULL ) ) { - tslist = m_alloc( sizeof *tslist ); - tslist->nseg = 1; - tslist->dup = 0; - tslist->seg[0].lid = lid; - tslist->seg[0].trust = 0; - tslist->next = NULL; - rc = 0; + ulong rn, uidrn; + int marginal=0; + int fully=0; + size_t dbglen; + + if( depth >= max_depth ) { + /* max cert_depth reached */ + puts("undefined (too deep)"); + return TRUST_UNDEFINED; } - else { - stack[0].lid = lid; - stack[0].trust = 0; - rc = do_list_path( stack, 1, max_depth, lids, &tslist ); - } - if( !rc ) { /* wipe out duplicates */ - LOCAL_ID_INFO *work = new_lid_table(); - for( tsl=tslist; tsl; tsl = tsl->next ) { - for(i=1; i < tsl->nseg-1; i++ ) { - if( ins_lid_table_item( work, tsl->seg[i].lid, 0 ) ) { - tsl->dup = 1; /* mark as duplicate */ - break; - } - } - } - release_lid_table(work); - *ret_tslist = tslist; - } - else { /* error: release tslist */ - while( tslist ) { - tsl = tslist->next; - m_free(tslist); - tslist = tsl; - } + if( !qry_lid_table_flag( ultikey_table, drec->r.dir.lid, NULL ) ) { + /* we are at the end of a path */ + puts("ultimate"); + return TRUST_ULTIMATE; } - release_lid_table(lids); - return rc; -} + /* loop over all user-ids */ + for( rn = drec->r.dir.uidlist; rn; rn = uidrn ) { + TRUSTREC rec; /* used for uids and sigs */ + ulong sigrn; -/**************** - * Given a trust segment list tslist, walk over all paths and fill in - * the trust information for each segment. What this function does is - * to assign a trustvalue to the first segment (which is the requested key) - * of each path. - * - * FIXME: We have to do more thinking here. e.g. we should never increase - * the trust value. - * - * Do not do it for duplicates. - */ -static int -propagate_trust( TRUST_SEG_LIST tslist ) -{ - int i; - unsigned trust, tr; - TRUST_SEG_LIST tsl; + read_record( rn, &rec, RECTYPE_UID ); + uidrn = rec.r.uid.next; + /* fixme: continue if the uidrec is not marked valid */ - for(tsl = tslist; tsl; tsl = tsl->next ) { - if( tsl->dup ) - continue; - assert( tsl->nseg ); - /* the last segment is always an ultimately trusted one, so we can - * assign a fully trust to the next one */ - i = tsl->nseg-1; - tsl->seg[i].trust = TRUST_ULTIMATE; - trust = TRUST_FULLY; - for(i-- ; i >= 0; i-- ) { - tsl->seg[i].trust = trust; - if( i > 0 ) { - /* get the trust of this pubkey */ - tr = get_ownertrust( tsl->seg[i].lid ); - if( tr < trust ) - trust = tr; + /* loop over all signature records */ + for( rn = rec.r.uid.siglist; rn; rn = sigrn ) { + int i; + + read_record( rn, &rec, RECTYPE_SIG ); + sigrn = rec.r.sig.next; + + for(i=0; i < SIGS_PER_RECORD; i++ ) { + TRUSTREC tmp; + int ot, nt; + + if( !rec.r.sig.sig[i].lid ) + continue; /* skip deleted sigs */ + if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) ) + continue; /* skip unchecked signatures */ + if( !(rec.r.sig.sig[i].flag & SIGF_VALID) ) + continue; /* skip invalid signatures */ + if( (rec.r.sig.sig[i].flag & SIGF_EXPIRED) ) + continue; /* skip expired signatures */ + if( (rec.r.sig.sig[i].flag & SIGF_REVOKED) ) + continue; /* skip revoked signatures */ + /* fixme: skip duplicates */ + + read_record( rec.r.sig.sig[i].lid, &tmp, RECTYPE_DIR ); + ot = tmp.r.dir.ownertrust & TRUST_MASK; + if( ot < TRUST_MARGINAL ) { + printf(". "); + continue; /* ownertrust is too low; don't need to check */ + } + + if( ot >= TRUST_FULLY ) + ot = TRUST_FULLY; /* just in case */ + + puts(""); + nt = verify_key( depth+1, max_depth, &tmp ) & TRUST_MASK; + if( nt < TRUST_MARGINAL ) { + printf("%*s* ", dbglen, ""); + dbglen += 2; + continue; + } + + if( nt == TRUST_ULTIMATE ) { + /* we have signed this key and only in this special case + * we assume a completes-needed or marginals-needed of 1 */ + printf("%*s", dbglen, ""); + if( ot == TRUST_MARGINAL ) + puts("marginal (1st level)"); + else if( ot == TRUST_FULLY ) + puts("fully (1st level)"); + else + puts("????? (1st level)"); + return ot; + } + + if( nt >= TRUST_FULLY ) + fully++; + if( nt >= TRUST_MARGINAL ) + marginal++; + + if( fully >= opt.completes_needed + || marginal >= opt.marginals_needed ) { + printf("%*s", dbglen, ""); + puts("fully"); + return TRUST_FULLY; + } } } } - return 0; + printf("%*s", dbglen, ""); + if( marginal ) { + puts("marginal"); + return TRUST_MARGINAL; + } + puts("undefined"); + return TRUST_UNDEFINED; } /**************** * we have the pubkey record and all needed informations are in the trustdb * but nothing more is known. - * (this function may re-read the dir record dr) */ static int do_check( TRUSTREC *dr, unsigned *trustlevel ) { - int i, rc=0; - TRUST_SEG_LIST tsl, tsl2, tslist; - int marginal, fully; - int fully_needed = opt.completes_needed; - int marginal_needed = opt.marginals_needed; - unsigned tflags = 0; - - assert( fully_needed > 0 && marginal_needed > 1 ); - - - *trustlevel = TRUST_UNDEFINED; - if( !dr->r.dir.keylist ) { log_error("Ooops, no keys\n"); return G10ERR_TRUSTDB; @@ -881,92 +937,15 @@ do_check( TRUSTREC *dr, unsigned *trustlevel ) return G10ERR_TRUSTDB; } - /* did we already check the signatures */ - /* fixme:.... */ + *trustlevel = verify_key( 1, 5, dr ); if( dr->r.dir.dirflags & DIRF_REVOKED ) - tflags |= TRUST_FLAG_REVOKED; - - #if 0 /* Do we still need this?? */ - if( !rc && !dr->r.dir.siglist ) { - /* We do not have any signatures; check whether it is one of our - * secret keys */ - if( !qry_lid_table_flag( ultikey_table, dr->r.dir.lid, NULL ) ) - *trustlevel = tflags | TRUST_ULTIMATE; - return 0; - } - #endif - if( rc ) - return rc; /* error while looking for sigrec or building sigrecs */ - - /* fixme: take it from the cache if it is valid */ + *trustlevel |= TRUST_FLAG_REVOKED; - /* Make a list of all possible trust-paths */ - rc = make_tsl( dr->r.dir.lid, &tslist ); - if( rc ) - return rc; - rc = propagate_trust( tslist ); - if( rc ) - return rc; - for(tsl = tslist; tsl; tsl = tsl->next ) { - if( tsl->dup ) - continue; - - if( opt.verbose ) { - log_info("trust path:" ); - for(i=0; i < tsl->nseg; i++ ) { - putc(' ',stderr); - print_keyid( stderr, tsl->seg[i].lid ); - putc(':',stderr); - print_trust( stderr, tsl->seg[i].trust ); - } - putc('\n',stderr); - } - } - - /* and see whether there is a trusted path. - * We only have to look at the first segment, because - * propagate_trust has investigated all other segments */ - marginal = fully = 0; - for(tsl = tslist; tsl; tsl = tsl->next ) { - if( tsl->dup ) - continue; - if( tsl->seg[0].trust == TRUST_ULTIMATE ) { - *trustlevel = tflags | TRUST_ULTIMATE; /* our own key */ - break; - } - if( tsl->seg[0].trust == TRUST_FULLY ) { - marginal++; - fully++; - } - else if( tsl->seg[0].trust == TRUST_MARGINAL ) - marginal++; - - if( fully >= fully_needed ) { - *trustlevel = tflags | TRUST_FULLY; - break; - } - } - if( !tsl && marginal >= marginal_needed ) - *trustlevel = tflags | TRUST_MARGINAL; - - /* cache the tslist */ - if( last_trust_web_key ) { - for( tsl = last_trust_web_tslist; tsl; tsl = tsl2 ) { - tsl2 = tsl->next; - m_free(tsl); - } - } - last_trust_web_key = dr->r.dir.lid; - last_trust_web_tslist = tslist; return 0; } -/*********************************************** - **************** API ************************ - ***********************************************/ - /**************** * Perform some checks over the trustdb * level 0: only open the db @@ -1059,6 +1038,9 @@ export_ownertrust() byte *p; int rc; + printf("# List of assigned trustvalues, created %s\n" + "# (Use \"gpgm --import-ownertrust\" to restore them)\n", + asctimestamp( make_timestamp() ) ); for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { if( rec.rectype == RECTYPE_DIR ) { if( !rec.r.dir.keylist ) { @@ -1184,7 +1166,6 @@ list_trust_path( int max_depth, const char *username ) { int rc; int wipe=0; - int i; TRUSTREC rec; PKT_public_key *pk = m_alloc_clear( sizeof *pk ); @@ -1192,6 +1173,8 @@ list_trust_path( int max_depth, const char *username ) wipe = 1; max_depth = -max_depth; } + if( max_depth < 1 ) + max_depth = 1; if( (rc = get_pubkey_byname( pk, username )) ) log_error("user '%s' not found: %s\n", username, g10_errstr(rc) ); @@ -1208,61 +1191,6 @@ list_trust_path( int max_depth, const char *username ) } } - if( !rc ) { - TRUST_SEG_LIST tsl, tslist = NULL; - - if( !qry_lid_table_flag( ultikey_table, pk->local_id, NULL ) ) { - tslist = m_alloc( sizeof *tslist ); - tslist->nseg = 1; - tslist->dup = 0; - tslist->seg[0].lid = pk->local_id; - tslist->seg[0].trust = 0; - tslist->next = NULL; - rc = 0; - } - else { - LOCAL_ID_INFO *lids = new_lid_table(); - TRUST_INFO stack[MAX_LIST_SIGS_DEPTH]; - - stack[0].lid = pk->local_id; - stack[0].trust = 0; - rc = do_list_path( stack, 1, max_depth, lids, &tslist ); - if( wipe ) { /* wipe out duplicates */ - LOCAL_ID_INFO *work; - - work = new_lid_table(); - for( tsl=tslist; tsl; tsl = tsl->next ) { - for(i=1; i < tsl->nseg-1; i++ ) { - if( ins_lid_table_item( work, tsl->seg[i].lid, 0 ) ) { - tsl->dup = 1; /* mark as duplicate */ - break; - } - } - } - release_lid_table(work); - } - release_lid_table(lids); - } - if( rc ) - log_error("user '%s' list problem: %s\n", username, g10_errstr(rc)); - rc = propagate_trust( tslist ); - if( rc ) - log_error("user '%s' trust problem: %s\n", username, g10_errstr(rc)); - for(tsl = tslist; tsl; tsl = tsl->next ) { - int i; - - if( tsl->dup ) - continue; - printf("trust path:" ); - for(i=0; i < tsl->nseg; i++ ) { - putc(' ',stdout); - print_keyid( stdout, tsl->seg[i].lid ); - putc(':',stdout); - print_trust( stdout, tsl->seg[i].trust ); - } - putchar('\n'); - } - } free_public_key( pk ); } @@ -1288,7 +1216,9 @@ check_trustdb( const char *username ) username, g10_errstr(rc)); } else { - rc = update_trust_record( keyblock ); + int modified; + + rc = update_trust_record( keyblock, &modified ); if( rc == -1 ) { /* not yet in trustdb: insert */ rc = insert_trust_record( find_kbnode( keyblock, PKT_PUBLIC_KEY @@ -1298,20 +1228,27 @@ check_trustdb( const char *username ) if( rc ) log_error("%s: update failed: %s\n", username, g10_errstr(rc) ); - else + else if( modified ) log_info("%s: updated\n", username ); + else + log_info("%s: okay\n", username ); } release_kbnode( keyblock ); keyblock = NULL; } else { ulong recnum; + ulong count=0, upd_count=0, err_count=0, skip_count=0; for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { if( rec.rectype == RECTYPE_DIR ) { TRUSTREC tmp; + int modified; + if( !rec.r.dir.keylist ) { log_info("lid %lu: dir record w/o key - skipped\n", recnum); + count++; + skip_count++; continue; } @@ -1323,18 +1260,37 @@ check_trustdb( const char *username ) if( rc ) { log_error("lid %lu: keyblock not found: %s\n", recnum, g10_errstr(rc) ); + count++; + skip_count++; continue; } - rc = update_trust_record( keyblock ); - if( rc ) + + rc = update_trust_record( keyblock, &modified ); + if( rc ) { log_error("lid %lu: update failed: %s\n", recnum, g10_errstr(rc) ); - else - log_info("lid %lu: updated\n", recnum ); + err_count++; + } + else if( modified ) { + if( opt.verbose ) + log_info("lid %lu: updated\n", recnum ); + upd_count++; + } + else if( opt.verbose > 1 ) + log_info("lid %lu: okay\n", recnum ); release_kbnode( keyblock ); keyblock = NULL; + if( !(++count % 100) ) + log_info(_("%lu keys so far processed\n"), count); } } + log_info(_("%lu keys processed\n"), count); + if( skip_count ) + log_info(_("\t%lu keys skipped\n"), skip_count); + if( err_count ) + log_info(_("\t%lu keys with errors\n"), err_count); + if( upd_count ) + log_info(_("\t%lu keys updated\n"), upd_count); } } @@ -1348,31 +1304,57 @@ update_trustdb( ) rc = enum_keyblocks( 0, &kbpos, &keyblock ); if( !rc ) { + ulong count=0, upd_count=0, err_count=0, new_count=0; + while( !(rc = enum_keyblocks( 1, &kbpos, &keyblock )) ) { - rc = update_trust_record( keyblock ); + int modified; + + rc = update_trust_record( keyblock, &modified ); 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 ) + if( rc && !pk->local_id ) { log_error("lid ?: insert failed: %s\n", g10_errstr(rc) ); - else if( rc ) + err_count++; + } + 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 ); + err_count++; + } + else { + if( opt.verbose ) + log_info("lid %lu: inserted\n", pk->local_id ); + new_count++; + } } - else if( rc ) + 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) ); + err_count++; + } + else if( modified ) { + if( opt.verbose ) + log_info("lid %lu: updated\n", lid_from_keyblock(keyblock)); + upd_count++; + } + else if( opt.verbose > 1 ) + log_info("lid %lu: okay\n", lid_from_keyblock(keyblock) ); release_kbnode( keyblock ); keyblock = NULL; + if( !(++count % 100) ) + log_info(_("%lu keys so far processed\n"), count); } + log_info(_("%lu keys processed\n"), count); + if( err_count ) + log_info(_("\t%lu keys with errors\n"), err_count); + if( upd_count ) + log_info(_("\t%lu keys updated\n"), upd_count); + if( new_count ) + log_info(_("\t%lu keys inserted\n"), new_count); } if( rc && rc != -1 ) log_error("enum_keyblocks failed: %s\n", g10_errstr(rc)); @@ -1510,54 +1492,8 @@ query_trust_info( PKT_public_key *pk ) int enum_trust_web( void **context, ulong *lid ) { - struct { - TRUST_SEG_LIST tsl; - int index; - } *c = *context; - - if( !c ) { /* make a new context */ - c = m_alloc_clear( sizeof *c ); - *context = c; - if( *lid == last_trust_web_key && last_trust_web_tslist ) - c->tsl = last_trust_web_tslist; - else { - TRUST_SEG_LIST tsl, tsl2, tslist; - int rc; + /* REPLACE THIS with a BETTER ONE */ - rc = make_tsl( *lid, &tslist ); - if( rc ) { - log_error("failed to build the TSL\n"); - return rc; - } - /* cache the tslist, so that we do not need to free it */ - if( last_trust_web_key ) { - for( tsl = last_trust_web_tslist; tsl; tsl = tsl2 ) { - tsl2 = tsl->next; - m_free(tsl); - } - } - last_trust_web_key = *lid; - last_trust_web_tslist = tslist; - c->tsl = last_trust_web_tslist; - } - c->index = 1; - } - - if( !lid ) { /* free the context */ - m_free( c ); - *context = NULL; - return 0; - } - - while( c->tsl ) { - if( !c->tsl->dup && c->index < c->tsl->nseg-1 ) { - *lid = c->tsl->seg[c->index].lid; - c->index++; - return 0; - } - c->index = 1; - c->tsl = c->tsl->next; - } return -1; /* eof */ } @@ -1709,7 +1645,7 @@ 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; + int rc, state; byte uhash[20]; int is_selfsig; PKT_signature *sigpkt = NULL; @@ -1743,6 +1679,7 @@ check_hint_sig( ulong lid, KBNODE keyblock, u32 *keyid, byte *uidrec_hash, /* find the correct signature packet */ + state = 0; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uidpkt = node->pkt->pkt.user_id; @@ -1757,13 +1694,19 @@ check_hint_sig( ulong lid, KBNODE keyblock, u32 *keyid, byte *uidrec_hash, sigpkt = node->pkt->pkt.signature; if( sigpkt->keyid[0] == sigkid[0] && sigpkt->keyid[1] == sigkid[1] - && (sigpkt->sig_class&~3) == 0x10 ) + && (sigpkt->sig_class&~3) == 0x10 ) { + state = 2; break; /* found */ + } } } if( !node ) { - log_error(_("lid %lu: user id not found in keyblock\n"), lid ); + log_info(_("lid %lu: user id not found in keyblock\n"), lid ); + return ; + } + if( state != 2 ) { + log_info(_("lid %lu: user id without signature\n"), lid ); return ; } @@ -1776,7 +1719,7 @@ check_hint_sig( ulong lid, KBNODE keyblock, u32 *keyid, byte *uidrec_hash, if( !rc ) { /* valid signature */ if( opt.verbose ) log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " - "good signature (3)\n"), + "Good signature (3)\n"), (ulong)keyid[1], lid, uhash[18], uhash[19], (ulong)sigpkt->keyid[1] ); sigrec->r.sig.sig[sigidx].flag = SIGF_CHECKED | SIGF_VALID; @@ -1789,8 +1732,7 @@ check_hint_sig( ulong lid, KBNODE keyblock, u32 *keyid, byte *uidrec_hash, sigrec->r.sig.sig[sigidx].flag = SIGF_NOPUBKEY; } else { - log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " - "invalid signature: %s\n"), + log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: %s\n"), (ulong)keyid[1], lid, uhash[18], uhash[19], (ulong)sigpkt->keyid[1], g10_errstr(rc) ); sigrec->r.sig.sig[sigidx].flag = SIGF_CHECKED; @@ -1893,6 +1835,74 @@ process_hintlist( ulong hintlist, ulong hint_owner ) } +/**************** + * Create or update shadow dir record and return the LID of the record + */ +static ulong +create_shadow_dir( PKT_signature *sig, ulong lid ) +{ + TRUSTREC sdir, hlst, tmphlst; + ulong recno, newlid; + int tmpidx; + int rc; + + /* first see whether we already have such a record */ + rc = tdbio_search_sdir( sig->keyid, sig->pubkey_algo, &sdir ); + if( rc && rc != -1 ) { + log_error("tdbio_search_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 ) + return newlid; /* the signature is already in the hintlist */ + } + } + /* 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 ); + } + + return newlid; +} + static void upd_key_record( PKT_public_key *pk, TRUSTREC *drec, RECNO_LIST *recno_list ) @@ -1941,7 +1951,7 @@ upd_key_record( PKT_public_key *pk, TRUSTREC *drec, RECNO_LIST *recno_list ) static void upd_uid_record( PKT_user_id *uid, TRUSTREC *drec, RECNO_LIST *recno_list, - u32 *keyid, ulong *uidrecno, byte *uidhash ) + u32 *keyid, ulong *uidrecno, byte *uidhash ) { TRUSTREC urec; ulong recno, newrecno; @@ -1981,8 +1991,8 @@ upd_uid_record( PKT_user_id *uid, TRUSTREC *drec, RECNO_LIST *recno_list, static void -upd_pref_record( PKT_signature *sig, TRUSTREC *drec, - u32 *keyid, TRUSTREC *urec, byte *uidhash ) +upd_pref_record( PKT_signature *sig, ulong lid, const u32 *keyid, + TRUSTREC *urec, const byte *uidhash ) { static struct { sigsubpkttype_t subpkttype; @@ -2003,7 +2013,8 @@ upd_pref_record( PKT_signature *sig, TRUSTREC *drec, /* 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 */ + * do update the record at all - the record cache may care about it + * FIXME: We never get correct statistics if we di it like this */ for( recno=urec->r.uid.prefrec; recno; recno = prec.r.pref.next ) { read_record( recno, &prec, RECTYPE_PREF ); delete_record( recno ); @@ -2027,7 +2038,7 @@ upd_pref_record( PKT_signature *sig, TRUSTREC *drec, } memset( &prec, 0, sizeof prec ); prec.rectype = RECTYPE_PREF; - prec.r.pref.lid = drec->recnum; + prec.r.pref.lid = lid; i = 0; } prec.r.pref.data[i++] = prefs[k].preftype; @@ -2053,6 +2064,248 @@ upd_pref_record( PKT_signature *sig, TRUSTREC *drec, } +/**************** + * update self key signatures (class 0x10..0x13) + */ +static void +upd_self_key_sigs( PKT_signature *sig, TRUSTREC *urec, + ulong lid, const u32 *keyid, const byte *uidhash, + KBNODE keyblock, KBNODE signode) +{ + int rc; + + /* must verify this selfsignature here, so that we can + * build the preference record and validate the uid record + */ + if( !(urec->r.uid.uidflags & UIDF_CHECKED) ) { + rc = check_key_signature( keyblock, signode, NULL ); + if( !rc ) { + if( opt.verbose ) + log_info(_( + "key %08lX.%lu, uid %02X%02X: Good self-signature\n"), + (ulong)keyid[1], lid, uidhash[18], uidhash[19] ); + upd_pref_record( sig, lid, keyid, urec, uidhash ); + urec->r.uid.uidflags = UIDF_CHECKED | UIDF_VALID; + } + else { + log_info(_("key %08lX, uid %02X%02X: Invalid self-signature: %s\n"), + (ulong)keyid[1], uidhash[18], uidhash[19], g10_errstr(rc) ); + urec->r.uid.uidflags = UIDF_CHECKED; + } + urec->dirty = 1; + } +} + + +/**************** + * update non-self key signatures (class 0x10..0x13) + */ +static void +upd_nonself_key_sigs( PKT_signature *sig, TRUSTREC *urec, + ulong lid, const u32 *keyid, const byte *uidhash, + KBNODE keyblock, KBNODE signode) +{ + /* 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 rec; + ulong recno; + TRUSTREC delrec; + int delrecidx; + int newflag = 0; + ulong newlid = 0; + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); + ulong pk_lid = 0; + int found_sig = 0; + int found_delrec = 0; + int rc; + + delrec.recnum = 0; + + /* get the LID of the pubkey of the signature under verification */ + rc = get_pubkey( pk, sig->keyid ); + if( !rc ) { + if( pk->local_id ) + pk_lid = pk->local_id; + else { + rc = tdbio_search_dir_bypk( pk, &rec ); + if( !rc ) + pk_lid = rec.recnum; + else if( rc == -1 ) { /* see whether there is a sdir instead */ + u32 akid[2]; + + keyid_from_pk( pk, akid ); + rc = tdbio_search_sdir( akid, pk->pubkey_algo, &rec ); + if( !rc ) + pk_lid = rec.recnum; + } + } + } + free_public_key( pk ); pk = NULL; + + /* Loop over all signatures just in case one is not correctly + * marked. If we see the correct signature, set a flag. + * delete duplicate signatures (should not happen but...) */ + 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( !found_delrec && !delrec.recnum ) { + delrec = rec; + delrecidx = i; + found_delrec=1; + } + continue; /* skip deleted sigs */ + } + if( rec.r.sig.sig[i].lid == pk_lid ) { + if( found_sig ) { + log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " + "duplicated signature - deleted\n"), + (ulong)keyid[1], lid, uidhash[18], + uidhash[19], (ulong)sig->keyid[1] ); + rec.r.sig.sig[i].lid = 0; + rec.dirty = 1; + continue; + } + found_sig = 1; + } + 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 */ + if( opt.verbose ) + log_info(_( + "key %08lX.%lu, uid %02X%02X, sig %08lX: " + "Good signature (1)\n"), + (ulong)keyid[1], lid, uidhash[18], + uidhash[19], (ulong)sig->keyid[1] ); + rec.r.sig.sig[i].flag = SIGF_CHECKED | SIGF_VALID; + } + else if( rc == G10ERR_NO_PUBKEY ) { + log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " + "weird: no public key\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, sig %08lX:" + " %s\n"), + (ulong)keyid[1], lid, uidhash[18], + uidhash[19], (ulong)sig->keyid[1], + g10_errstr(rc)); + rec.r.sig.sig[i].flag = SIGF_CHECKED; + } + rec.dirty = 1; + } + 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; + rec.dirty = 1; + /* fixme: should we verify that the record is + * in the hintlist? - This case here should anyway + * never occur */ + } + } + else { + log_error("sig record %lu[%d] points to wrong record.\n", + rec.r.sig.sig[i].lid, i ); + die_invalid_db(); + } + } + if( found_delrec && delrec.recnum ) { + delrec = rec; + found_delrec = 0; /* we only want the first one */ + } + if( rec.dirty ) { + write_record( &rec ); + rec.dirty = 0; + } + } + + if( found_sig ) + return; + + /* 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. */ + + if( !pk_lid ) /* we have already seen that there is no pubkey */ + rc = G10ERR_NO_PUBKEY; + else + rc = check_key_signature( keyblock, signode, NULL ); + + if( !rc ) { /* valid signature */ + if( opt.verbose ) + log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " + "Good signature (2)\n"), + (ulong)keyid[1], lid, uidhash[18], + uidhash[19], (ulong)sig->keyid[1] ); + newlid = pk_lid; /* this is the pk of the signature */ + newflag = SIGF_CHECKED | SIGF_VALID; + } + else if( rc == G10ERR_NO_PUBKEY ) { + if( opt.verbose > 1 ) + log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " + "no public key\n"), + (ulong)keyid[1], lid, uidhash[18], + uidhash[19], (ulong)sig->keyid[1] ); + newlid = create_shadow_dir( sig, lid ); + newflag = SIGF_NOPUBKEY; + } + else { + log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: %s\n"), + (ulong)keyid[1], lid, uidhash[18], uidhash[19], + (ulong)sig->keyid[1], g10_errstr(rc)); + newlid = create_shadow_dir( sig, lid ); + newflag = SIGF_CHECKED; + } + + 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; + urec->dirty = 1; + } +} + + /**************** * Note: A signature made with a secondary key is not considered a @@ -2061,10 +2314,9 @@ upd_pref_record( PKT_signature *sig, TRUSTREC *drec, static void upd_sig_record( PKT_signature *sig, TRUSTREC *drec, u32 *keyid, ulong *uidrecno, byte *uidhash, - KBNODE keyblock, KBNODE signode ) + KBNODE keyblock, KBNODE signode) { TRUSTREC urec; - int rc; ulong lid = drec->recnum; if( !*uidrecno ) { @@ -2086,29 +2338,8 @@ upd_sig_record( PKT_signature *sig, TRUSTREC *drec, 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( !(urec.r.uid.uidflags & UIDF_CHECKED) ) { - rc = check_key_signature( keyblock, signode, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info(_( - "key %08lX.%lu, uid %02X%02X: " - "good self-signature\n"), - (ulong)keyid[1], lid, uidhash[18], - uidhash[19] ); - upd_pref_record( sig, drec, keyid, &urec, uidhash ); - urec.r.uid.uidflags = UIDF_CHECKED | UIDF_VALID; - } - else { - log_error("key %08lX, uid %02X%02X: " - "invalid self-signature: %s\n", (ulong)keyid[1], - uidhash[18], uidhash[19], g10_errstr(rc) ); - urec.r.uid.uidflags = UIDF_CHECKED; - } - urec.dirty = 1; - } + upd_self_key_sigs( sig, &urec, lid, keyid, uidhash, + keyblock, signode ); } else if( sig->sig_class == 0x18 ) { /* key binding */ /* get the corresponding key */ @@ -2126,264 +2357,8 @@ upd_sig_record( PKT_signature *sig, TRUSTREC *drec, } } 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 rec; - ulong recno; - TRUSTREC delrec; - int delrecidx; - int newflag = 0; - ulong newlid = 0; - PKT_public_key *pk = m_alloc_clear( sizeof *pk ); - ulong pk_lid = 0; - int found_sig = 0; - int found_delrec = 0; - - delrec.recnum = 0; - - rc = get_pubkey( pk, sig->keyid ); - if( !rc ) { - if( pk->local_id ) - pk_lid = pk->local_id; - else { - rc = tdbio_search_dir_bypk( pk, &rec ); - if( !rc ) - pk_lid = rec.recnum; - else if( rc == -1 ) { /* see whether there is a sdir instead */ - u32 akid[2]; - - keyid_from_pk( pk, akid ); - rc = tdbio_search_sdir( akid, pk->pubkey_algo, &rec ); - if( !rc ) - pk_lid = rec.recnum; - } - } - } - free_public_key( pk ); pk = NULL; - - /* Loop over all signatures just in case one is not correctly - * marked. If we see the correct signature, set a flag. - * delete duplicate signatures (should not happen but...) - */ - 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( !found_delrec && !delrec.recnum ) { - delrec = rec; - delrecidx = i; - found_delrec=1; - } - continue; /* skip deleted sigs */ - } - if( rec.r.sig.sig[i].lid == pk_lid ) { - if( found_sig ) { - log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " - "duplicated signature - deleted\n"), - (ulong)keyid[1], lid, uidhash[18], - uidhash[19], (ulong)sig->keyid[1] ); - rec.r.sig.sig[i].lid = 0; - rec.dirty = 1; - } - else - found_sig = 1; - } - 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 */ - if( opt.verbose ) - log_info(_( - "key %08lX.%lu, uid %02X%02X, sig %08lX: " - "good signature (1)\n"), - (ulong)keyid[1], lid, uidhash[18], - uidhash[19], (ulong)sig->keyid[1] ); - rec.r.sig.sig[i].flag = SIGF_CHECKED | SIGF_VALID; - } - else if( rc == G10ERR_NO_PUBKEY ) { - log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " - "weird: no public key\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, sig %08lX: " - "invalid signature: %s\n"), - (ulong)keyid[1], lid, uidhash[18], - uidhash[19], (ulong)sig->keyid[1], - g10_errstr(rc)); - rec.r.sig.sig[i].flag = SIGF_CHECKED; - } - rec.dirty = 1; - } - 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; - rec.dirty = 1; - /* fixme: should we verify that the record is - * in the hintlist? - This case here should anyway - * never occur */ - } - } - else { - log_error("sig record %lu[%d] points to wrong record.\n", - rec.r.sig.sig[i].lid, i ); - die_invalid_db(); - } - } - if( found_delrec && delrec.recnum ) { - delrec = rec; - found_delrec = 0; /* we onyl want the first one */ - } - if( rec.dirty ) - write_record( &rec ); - } - - if( found_sig ) - goto leave; - - /* 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. */ - - if( !pk_lid ) - rc = G10ERR_NO_PUBKEY; - else - rc = check_key_signature( keyblock, signode, NULL ); - - if( !rc ) { /* valid signature */ - if( opt.verbose ) - log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " - "good signature (2)\n"), - (ulong)keyid[1], lid, uidhash[18], - uidhash[19], (ulong)sig->keyid[1] ); - newlid = pk_lid; /* this is the pk of the signature */ - if( !newlid ) - BUG(); - newflag = SIGF_CHECKED | SIGF_VALID; - } - else if( rc == G10ERR_NO_PUBKEY ) { - if( opt.verbose > 1 ) - log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: " - "no public key\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, sig %08lX: " - "invalid signature: %s\n"), - (ulong)keyid[1], lid, uidhash[18], uidhash[19], - (ulong)sig->keyid[1], g10_errstr(rc)); - newflag = SIGF_CHECKED; - } - - 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; - urec.dirty = 1; - } - + upd_nonself_key_sigs( sig, &urec, lid, keyid, uidhash, + keyblock, signode ); } else if( sig->sig_class == 0x18 ) { /* key binding */ log_info(_("key %08lX: bogus key binding by %08lX\n"), @@ -2398,12 +2373,13 @@ upd_sig_record( PKT_signature *sig, TRUSTREC *drec, (ulong)keyid[1], (ulong)sig->keyid[1] ); } else if( sig->sig_class == 0x30 ) { /* cert revocation */ - /* FIXME: a signator wants to revoke his certification signature */ + /* fixme: a signator wants to revoke his certification signature */ } - leave: - if( urec.dirty ) + if( urec.dirty ) { write_record( &urec ); + urec.dirty = 0; + } } @@ -2415,7 +2391,7 @@ upd_sig_record( PKT_signature *sig, TRUSTREC *drec, * key, the check is done by some special code in insert_trust_record(). */ int -update_trust_record( KBNODE keyblock ) +update_trust_record( KBNODE keyblock, int *modified ) { PKT_public_key *primary_pk; KBNODE node; @@ -2432,6 +2408,9 @@ update_trust_record( KBNODE keyblock ) RECNO_LIST recno_list = NULL; /* list of verified records */ /* fixme: replace recno_list by a lookup on node->recno */ + if( modified ) + *modified = 0; + node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); primary_pk = node->pkt->pkt.public_key; rc = get_dir_record( primary_pk, &drec ); @@ -2442,7 +2421,10 @@ update_trust_record( KBNODE keyblock ) keyid_from_pk( primary_pk, keyid ); - /* fixme: start a transaction */ + rc = tdbio_begin_transaction(); + if( rc ) + return rc; + /* now update keys and user ids */ for( node=keyblock; node; node = node->next ) { switch( node->pkt->pkttype ) { @@ -2458,17 +2440,16 @@ update_trust_record( KBNODE keyblock ) drec.dirty = 0; } upd_uid_record( node->pkt->pkt.user_id, &drec, &recno_list, - keyid, &uidrecno, uidhash ); + keyid, &uidrecno, uidhash ); break; case PKT_SIGNATURE: - if( drec.dirty ) { /* upd_sig_recrod may read the drec */ - + if( drec.dirty ) { /* upd_sig_recory may read the drec */ write_record( &drec ); drec.dirty = 0; } - upd_sig_record( node->pkt->pkt.signature, &drec, - keyid, &uidrecno, uidhash, keyblock, node ); + upd_sig_record( node->pkt->pkt.signature, &drec, keyid, + &uidrecno, uidhash, keyblock, node ); break; default: @@ -2531,11 +2512,15 @@ update_trust_record( KBNODE keyblock ) if( rc ) - ; /* fixme: cancel transaction */ - else if( drec.dirty ) { - drec.r.dir.dirflags &= ~DIRF_CHECKED; /* reset flag */ - write_record( &drec ); - do_sync(); + rc = tdbio_cancel_transaction(); + else { + if( drec.dirty ) { + drec.r.dir.dirflags &= ~DIRF_CHECKED; /* reset flag */ + write_record( &drec ); + } + if( modified && tdbio_is_dirty() ) + *modified = 0; + rc = tdbio_end_transaction(); } rel_recno_list( &recno_list ); return rc; @@ -2638,7 +2623,7 @@ insert_trust_record( PKT_public_key *pk ) } /* and put all the other stuff into the keydb */ - rc = update_trust_record( keyblock ); + rc = update_trust_record( keyblock, NULL ); if( !rc ) process_hintlist( hintlist, dirrec.r.dir.lid ); |