diff options
Diffstat (limited to 'g10/tdbio.c')
-rw-r--r-- | g10/tdbio.c | 520 |
1 files changed, 400 insertions, 120 deletions
diff --git a/g10/tdbio.c b/g10/tdbio.c index ac1266147..36f0ac503 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -45,46 +45,8 @@ static char *db_name; static int db_fd = -1; - -static void create_db( const char *fname ); static void open_db(void); -/************************************************** - ************** read and write helpers ************ - **************************************************/ - -static void -fwrite_8(FILE *fp, byte a) -{ - if( putc( a & 0xff, fp ) == EOF ) - log_fatal("error writing byte to trustdb: %s\n", strerror(errno) ); -} - - -static void -fwrite_32( FILE*fp, ulong a) -{ - putc( (a>>24) & 0xff, fp ); - putc( (a>>16) & 0xff, fp ); - putc( (a>> 8) & 0xff, fp ); - if( putc( a & 0xff, fp ) == EOF ) - log_fatal("error writing ulong to trustdb: %s\n", strerror(errno) ); -} - -static void -fwrite_zeros( FILE *fp, size_t n) -{ - while( n-- ) - if( putc( 0, fp ) == EOF ) - log_fatal("error writing zeros to trustdb: %s\n", strerror(errno) ); -} - - - - -/************************************************** - ************** read and write stuff ************** - **************************************************/ int tdbio_set_dbname( const char *new_dbname, int create ) @@ -101,7 +63,11 @@ tdbio_set_dbname( const char *new_dbname, int create ) return G10ERR_TRUSTDB; } if( create ) { + FILE *fp; + TRUSTREC rec; + int rc; char *p = strrchr( fname, '/' ); + assert(p); *p = 0; if( access( fname, F_OK ) ) { @@ -119,7 +85,30 @@ tdbio_set_dbname( const char *new_dbname, int create ) log_fatal_f(fname, _("directory does not exist!\n") ); } *p = '/'; - create_db( fname ); + + fp =fopen( fname, "w" ); + if( !fp ) + log_fatal_f( fname, _("can't create: %s\n"), strerror(errno) ); + fclose(fp); + m_free(db_name); + db_name = fname; + db_fd = open( db_name, O_RDWR ); + if( db_fd == -1 ) + log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) ); + + memset( &rec, 0, sizeof rec ); + rec.r.ver.version = 2; + rec.r.ver.created = make_timestamp(); + rec.rectype = RECTYPE_VER; + rec.recnum = 0; + rc = tdbio_write_record( &rec ); + if( rc ) + log_fatal_f( fname, _("failed to create version record: %s"), + g10_errstr(rc)); + /* and read again to check that we are okay */ + if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) + log_fatal_f( db_name, "invalid trust-db created\n" ); + return 0; } } m_free(db_name); @@ -136,37 +125,6 @@ tdbio_get_dbname() -/**************** - * Create a new trustdb - */ -static void -create_db( const char *fname ) -{ - FILE *fp; - - fp =fopen( fname, "w" ); - if( !fp ) - log_fatal_f( fname, _("can't create %s: %s\n"), strerror(errno) ); - fwrite_8( fp, 1 ); /* record type */ - fwrite_8( fp, 'g' ); - fwrite_8( fp, 'p' ); - fwrite_8( fp, 'g' ); - fwrite_8( fp, 2 ); /* version */ - fwrite_zeros( fp, 3 ); /* reserved */ - fwrite_32( fp, 0 ); /* not locked */ - fwrite_32( fp, make_timestamp() ); /* created */ - fwrite_32( fp, 0 ); /* not yet modified */ - fwrite_32( fp, 0 ); /* not yet validated*/ - fwrite_32( fp, 0 ); /* reserved */ - fwrite_8( fp, 3 ); /* marginals needed */ - fwrite_8( fp, 1 ); /* completes needed */ - fwrite_8( fp, 4 ); /* max_cet_depth */ - fwrite_zeros( fp, 9 ); /* filler */ - fclose(fp); -} - - - static void open_db() { @@ -182,10 +140,221 @@ open_db() } +/**************** + * Return the record number of the keyhash tbl or create a new one. + */ +static ulong +get_keyhashrec() +{ + static ulong keyhashtbl; /* record number of the key hashtable */ + TRUSTREC vr; + int rc; + + if( keyhashtbl ) + return keyhashtbl; + + 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.keyhashtbl ) + keyhashtbl = vr.r.ver.keyhashtbl; + else { + TRUSTREC rec; + off_t offset; + ulong recnum; + int i, n; + + 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; +} + + +/**************** + * Update the key hashtbl or create the table if it does not exist + */ +static int +update_keyhashtbl( TRUSTREC *kr ) +{ + TRUSTREC lastrec, rec; + ulong hashrec, item; + int msb; + int level=0; + int rc, i; + + hashrec = get_keyhashrec(); + next_level: + msb = kr->r.key.fingerprint[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", + 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; + rc = tdbio_write_record( &rec ); + if( rc ) { + log_error( db_name, "update_keyhashtbl write htbl failed: %s\n", + g10_errstr(rc) ); + return rc; + } + } + else if( item != kr->recnum ) { /* 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", + 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"); + return G10ERR_TRUSTDB; + } + goto next_level; + } + else if( rec.rectype == RECTYPE_HLST ) { /* extend list */ + /* 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.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 + break; /* not there */ + } + /* find the next free entry and put it in */ + for(;;) { + for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { + if( !rec.r.hlst.rnum[i] ) { + rec.r.hlst.rnum[i] = kr->recnum; + rc = tdbio_write_record( &rec ); + if( rc ) + log_error( db_name, + "update_keyhashtbl write hlst failed: %s\n", + g10_errstr(rc) ); + return rc; /* ready */ + } + } + if( rec.r.hlst.next ) { + rc = tdbio_read_record( rec.r.hlst.next, + &rec, RECTYPE_HLST ); + if( rc ) { + log_error( db_name, + "update_keyhashtbl read hlst failed: %s\n", + g10_errstr(rc) ); + return rc; + } + } + else { /* add a new list record */ + rec.r.hlst.next = item = tdbio_new_recnum(); + rc = tdbio_write_record( &rec ); + if( rc ) { + log_error( db_name, + "update_keyhashtbl 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; + if( rc ) + log_error( db_name, + "update_keyhashtbl write ext hlst failed: %s\n", + g10_errstr(rc) ); + return rc; /* ready */ + } + } + } + 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 ); + return 0; + } + item = rec.recnum; /* save number of key record */ + memset( &rec, 0, sizeof rec ); + 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 */ + rc = tdbio_write_record( &rec ); + if( rc ) { + log_error( db_name, + "update_keyhashtbl write new hlst failed: %s\n", + g10_errstr(rc) ); + return rc; + } + /* update the hashtable record */ + 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) ); + return rc; /* ready */ + } + else { + log_error( db_name, "keyhashtbl %lu points to an invalid record\n", + item); + return G10ERR_TRUSTDB; + } + } + + return 0; +} + + + void tdbio_dump_record( TRUSTREC *rec, FILE *fp ) { - int i, any; + int i; ulong rnum = rec->recnum; fprintf(fp, "rec %5lu, ", rnum ); @@ -193,7 +362,8 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) switch( rec->rectype ) { case 0: fprintf(fp, "free\n"); break; - case RECTYPE_VER: fprintf(fp, "version\n"); + case RECTYPE_VER: fprintf(fp, "version, keyhashtbl=%lu\n", + rec->r.ver.keyhashtbl ); break; case RECTYPE_DIR: fprintf(fp, "dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x", @@ -213,11 +383,12 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) putc('\n', fp); break; case RECTYPE_KEY: - fprintf(fp, "key %lu, next=%lu, algo=%d, flen=%d", + fprintf(fp, "key %lu, next=%lu, algo=%d, ", rec->r.key.lid, rec->r.key.next, - rec->r.key.pubkey_algo, - rec->r.key.fingerprint_len ); + rec->r.key.pubkey_algo ); + for(i=0; i < rec->r.key.fingerprint_len; i++ ) + fprintf(fp, "%02X", rec->r.key.fingerprint[i] ); if( rec->r.key.keyflags & KEYF_REVOKED ) fputs(", revoked", fp ); putc('\n', fp); @@ -239,29 +410,29 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) rec->r.uid.next); break; case RECTYPE_SIG: - fprintf(fp, "sig %lu, next=%lu\n", + fprintf(fp, "sig %lu, next=%lu,", rec->r.sig.lid, rec->r.sig.next ); - for(i=any=0; i < SIGS_PER_RECORD; i++ ) { - if( rec->r.sig.sig[i].lid ) { - if( !any ) { - putc('\t', fp); - any++; - } - fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].lid, + for(i=0; i < SIGS_PER_RECORD; i++ ) { + if( rec->r.sig.sig[i].lid ) + fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].lid, rec->r.sig.sig[i].flag ); - } } - if( any ) - putc('\n', fp); + putc('\n', fp); break; case RECTYPE_CACH: fprintf(fp, "cach\n"); break; case RECTYPE_HTBL: - fprintf(fp, "htbl\n"); + fprintf(fp, "htbl,"); + for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) + fprintf(fp, " %lu", rec->r.htbl.item[i] ); + putc('\n', fp); break; case RECTYPE_HLST: - fprintf(fp, "hlst\n"); + fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next ); + for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) + fprintf(fp, " %lu", rec->r.hlst.rnum[i] ); + putc('\n', fp); break; default: fprintf(fp, "unknown type %d\n", rec->rectype ); @@ -302,30 +473,29 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) recnum, expected, rec->rectype ); return G10ERR_TRUSTDB; } - p++; + p++; /* skip reserved byte */ switch( rec->rectype ) { - case 0: /* unused record */ + case 0: /* unused (free) record */ break; case RECTYPE_VER: /* version record */ if( memcmp(buf+1, "gpg", 3 ) ) { log_error_f( db_name, _("not a trustdb file\n") ); rc = G10ERR_TRUSTDB; } - p += 2; /* skip magic */ + p += 2; /* skip "pgp" */ rec->r.ver.version = *p++; - rec->r.ver.locked = buftoulong(p); p += 4; + p += 3; /* reserved bytes */ + p += 4; /* lock flags */ rec->r.ver.created = buftoulong(p); p += 4; rec->r.ver.modified = buftoulong(p); p += 4; rec->r.ver.validated= buftoulong(p); p += 4; - rec->r.ver.marginals_needed = *p++; - rec->r.ver.completes_needed = *p++; - rec->r.ver.max_cert_depth = *p++; + rec->r.ver.keyhashtbl=buftoulong(p); p += 4; if( recnum ) { log_error_f( db_name, "version record with recnum %lu\n", (ulong)recnum ); rc = G10ERR_TRUSTDB; } - if( rec->r.ver.version != 2 ) { + else if( rec->r.ver.version != 2 ) { log_error_f( db_name, "invalid file version %d\n", rec->r.ver.version ); rc = G10ERR_TRUSTDB; @@ -381,6 +551,17 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) memcpy(rec->r.cache.blockhash, p, 20); p += 20; rec->r.cache.trustlevel = *p++; break; + case RECTYPE_HTBL: + for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { + rec->r.htbl.item[i] = buftoulong(p); p += 4; + } + break; + case RECTYPE_HLST: + rec->r.hlst.next = buftoulong(p); p += 4; + for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { + rec->r.hlst.rnum[i] = buftoulong(p); p += 4; + } + break; default: log_error_f( db_name, "invalid record type %d at recnum %lu\n", rec->rectype, (ulong)recnum ); @@ -412,8 +593,16 @@ tdbio_write_record( TRUSTREC *rec ) switch( rec->rectype ) { case 0: /* unused record */ break; - case 1: /* version record */ - BUG(); + case RECTYPE_VER: /* version record */ + if( recnum ) + BUG(); + memcpy(p-1, "gpg", 3 ); p += 2; + *p++ = rec->r.ver.version; + p += 7; /* skip reserved bytes and lock flags */ + ulongtobuf(p, rec->r.ver.created); p += 4; + ulongtobuf(p, rec->r.ver.modified); p += 4; + ulongtobuf(p, rec->r.ver.validated); p += 4; + ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4; break; case RECTYPE_DIR: /*directory record */ @@ -466,6 +655,19 @@ tdbio_write_record( TRUSTREC *rec ) *p++ = rec->r.cache.trustlevel; break; + case RECTYPE_HTBL: + for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { + ulongtobuf( p, rec->r.htbl.item[i]); p += 4; + } + break; + + case RECTYPE_HLST: + ulongtobuf( p, rec->r.hlst.next); p += 4; + for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { + ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4; + } + break; + default: BUG(); } @@ -479,6 +681,8 @@ tdbio_write_record( TRUSTREC *rec ) log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) ); return G10ERR_WRITE_FILE; } + else if( rec->rectype == RECTYPE_KEY ) + rc = update_keyhashtbl( rec ); return rc; } @@ -528,9 +732,6 @@ tdbio_new_recnum() /**************** * Search the trustdb for a key which matches PK and return the dir record * The local_id of PK is set to the correct value - * - * Note: To increase performance, we could use a index search here. - * tdbio_write_record shoudl create this index automagically */ int tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec ) @@ -540,32 +741,111 @@ tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec ) byte *fingerprint; size_t fingerlen; int rc; + ulong hashrec, item; + int msb; + int level=0; keyid_from_pk( pk, keyid ); fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen ); assert( fingerlen == 20 || fingerlen == 16 ); - for(recnum=1; !(rc=tdbio_read_record( recnum, rec, 0)); recnum++ ) { - if( rec->rectype != RECTYPE_KEY ) - continue; - if( rec->r.key.pubkey_algo == pk->pubkey_algo - && !memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) ) { - /* found: read the dir record for this key */ - recnum = rec->r.key.lid; - rc = tdbio_read_record( recnum, rec, RECTYPE_DIR); - if( rc ) - break; - if( pk->local_id && pk->local_id != recnum ) - log_error_f(db_name, - "found record, but LID from memory does " - "not match recnum (%lu,%lu)\n", - pk->local_id, recnum ); - pk->local_id = recnum; - return 0; + /* 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; } - if( rc != -1 ) - log_error_f( db_name, _("search_db failed: %s\n"), g10_errstr(rc) ); + 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( tmp.r.key.pubkey_algo == pk->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( rec->r.key.pubkey_algo != pk->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; + } + + recnum = rec->r.key.lid; + + if( pk->local_id && pk->local_id != recnum ) + log_error_f(db_name, + "found record, but LID from memory does " + "not match recnum (%lu,%lu)\n", + pk->local_id, recnum ); + pk->local_id = recnum; + + /* 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; } |