1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-07-03 02:58:57 +02:00
gnupg/g10/trustdb.c
2001-09-25 15:20:59 +00:00

1248 lines
31 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* trustdb.c
* Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "errors.h"
#include "iobuf.h"
#include "keydb.h"
#include "memory.h"
#include "util.h"
#include "options.h"
#include "packet.h"
#include "main.h"
#include "i18n.h"
#include "tdbio.h"
#include "trustdb.h"
/*
* A structure to store key identification as well as some stuff needed
* for validation
*/
struct key_item {
struct key_item *next;
unsigned int ownertrust;
u32 kid[2];
};
typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */
/*
* Structure to keep track of keys, this is used as an array wherre
* the item right after the last one has a keyblock set to NULL.
* Maybe we can drop this thing and replace it by key_item
*/
struct key_array {
KBNODE keyblock;
};
/* control information for the trust DB */
static struct {
int init;
int level;
char *dbname;
} trustdb_args;
/* some globals */
static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */
static struct key_item *utk_list; /* all ultimately trusted keys */
/* Keep track on whether we did an update trustDB already */
static int did_nextcheck;
static int validate_keys (int interactive);
/**********************************************
************* some helpers *******************
**********************************************/
static struct key_item *
new_key_item (void)
{
struct key_item *k;
k = m_alloc_clear (sizeof *k);
return k;
}
static void
release_key_items (struct key_item *k)
{
struct key_item *k2;
for (; k; k = k2)
{
k2 = k->next;
m_free (k);
}
}
/*
* For fast keylook up we need a hash table. Each byte of a KeyIDs
* should be distributed equally over the 256 possible values (except
* for v3 keyIDs but we consider them as not important here). So we
* can just use one byte to index a table of 256 key items.
* Possible optimization: Don not use key_items but other hash_table when the
* duplicates lists gets too large.
*/
static KeyHashTable
new_key_hash_table (void)
{
struct key_item **tbl;
tbl = m_alloc_clear (256 * sizeof *tbl);
return tbl;
}
static void
release_key_hash_table (KeyHashTable tbl)
{
int i;
if (!tbl)
return;
for (i=0; i < 256; i++)
release_key_items (tbl[i]);
m_free (tbl);
}
/*
* Returns: True if the keyID is in the given hash table
*/
static int
test_key_hash_table (KeyHashTable tbl, u32 *kid)
{
struct key_item *k;
for (k = tbl[(kid[1] & 0xff)]; k; k = k->next)
if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
return 1;
return 0;
}
/*
* Add a new key to the hash table. The key is indetified by its key ID.
*/
static void
add_key_hash_table (KeyHashTable tbl, u32 *kid)
{
struct key_item *k, *kk;
for (k = tbl[(kid[1] & 0xff)]; k; k = k->next)
if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
return; /* already in table */
kk = new_key_item ();
kk->kid[0] = kid[0];
kk->kid[1] = kid[1];
kk->next = tbl[(kid[1] & 0xff)];
tbl[(kid[1] & 0xff)] = kk;
}
/*
* Release a key_array
*/
static void
release_key_array ( struct key_array *keys )
{
struct key_array *k;
if (keys) {
for (k=keys; k->keyblock; k++)
release_kbnode (k->keyblock);
m_free (keys);
}
}
/*********************************************
********** Initialization *****************
*********************************************/
/*
* Used to register extra ultimately trusted keys - this has to be done
* before initializing the validation module.
* FIXME: Should be replaced by a function to add those keys to the trustdb.
*/
void
register_trusted_key( const char *string )
{
KEYDB_SEARCH_DESC desc;
struct key_item *k;
if (classify_user_id (string, &desc) != KEYDB_SEARCH_MODE_LONG_KID ) {
log_error(_("`%s' is not a valid long keyID\n"), string );
return;
}
k = new_key_item ();
k->kid[0] = desc.u.kid[0];
k->kid[1] = desc.u.kid[1];
k->next = user_utk_list;
user_utk_list = k;
}
/*
* Helper to add a key to the global list of ultimately trusted keys.
* Retruns: true = inserted, false = already in in list.
*/
static int
add_utk (u32 *kid)
{
struct key_item *k;
for (k = utk_list; k; k = k->next)
{
if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
{
return 0;
}
}
k = new_key_item ();
k->kid[0] = kid[0];
k->kid[1] = kid[1];
k->ownertrust = TRUST_ULTIMATE;
k->next = utk_list;
utk_list = k;
if( opt.verbose > 1 )
log_info(_("key %08lX: accepted as trusted key\n"), (ulong)kid[1]);
return 1;
}
/****************
* Verify that all our secret keys are usable and put them into the utk_list.
*/
static void
verify_own_keys(void)
{
TRUSTREC rec;
ulong recnum;
int rc;
struct key_item *k;
int hint_shown = 0;
if (utk_list)
return;
/* scan the trustdb to find all ultimately trusted keys */
for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ )
{
if ( rec.rectype == RECTYPE_TRUST
&& (rec.r.trust.ownertrust & TRUST_MASK) == TRUST_ULTIMATE)
{
byte *fpr = rec.r.trust.fingerprint;
int fprlen;
u32 kid[2];
/* Problem: We do only use fingerprints in the trustdb but
* we need the keyID here to indetify the key; we can only
* use that ugly hack to distinguish between 16 and 20
* butes fpr - it does not work always so we better change
* the whole validation code to only work with
* fingerprints */
fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20;
keyid_from_fingerprint (fpr, fprlen, kid);
if (!add_utk (kid))
log_info(_("key %08lX occurs more than once in the trustdb\n"),
(ulong)kid[1]);
}
}
/* the --trusted-key option is again deprecated; however we automagically
* add those keys to the trustdb */
for (k = user_utk_list; k; k = k->next)
{
if ( add_utk (k->kid) )
{ /* not yet in trustDB as ultimately trusted */
PKT_public_key pk;
memset (&pk, 0, sizeof pk);
rc = get_pubkey (&pk, k->kid);
if (rc) {
log_info(_("key %08lX: no public key for trusted key - skipped\n"),
(ulong)k->kid[1] );
}
else {
update_ownertrust (&pk,
((get_ownertrust (&pk) & ~TRUST_MASK)
| TRUST_ULTIMATE ));
release_public_key_parts (&pk);
}
if (!hint_shown)
{
log_info ("the --trusted-key option is now obsolete; "
"use the --edit command instead.\n");
log_info ("given keys will be marked as trusted\n");
hint_shown = 1;
}
log_info ("key %08lX marked as ultimately trusted\n",
(ulong)k->kid[1]);
}
}
/* release the helper table table */
release_key_items (user_utk_list);
user_utk_list = NULL;
return;
}
/*********************************************
*********** TrustDB stuff *******************
*********************************************/
/*
* Read a record but die if it does not exist
*/
static void
read_record (ulong recno, TRUSTREC *rec, int rectype )
{
int rc = tdbio_read_record (recno, rec, rectype);
if (rc)
{
log_error(_("trust record %lu, req type %d: read failed: %s\n"),
recno, rec->rectype, g10_errstr(rc) );
tdbio_invalid();
}
if (rectype != rec->rectype)
{
log_error(_("trust record %lu is not of requested type %d\n"),
rec->recnum, rectype);
tdbio_invalid();
}
}
/*
* Write a record and die on error
*/
static void
write_record (TRUSTREC *rec)
{
int rc = tdbio_write_record (rec);
if (rc)
{
log_error(_("trust record %lu, type %d: write failed: %s\n"),
rec->recnum, rec->rectype, g10_errstr(rc) );
tdbio_invalid();
}
}
/*
* sync the TrustDb and die on error
*/
static void
do_sync(void)
{
int rc = tdbio_sync ();
if(rc)
{
log_error (_("trustdb: sync failed: %s\n"), g10_errstr(rc) );
g10_exit(2);
}
}
/****************
* Perform some checks over the trustdb
* level 0: only open the db
* 1: used for initial program startup
*/
int
setup_trustdb( int level, const char *dbname )
{
/* just store the args */
if( trustdb_args.init )
return 0;
trustdb_args.level = level;
trustdb_args.dbname = dbname? m_strdup(dbname): NULL;
return 0;
}
void
init_trustdb()
{
int rc=0;
int level = trustdb_args.level;
const char* dbname = trustdb_args.dbname;
if( trustdb_args.init )
return;
trustdb_args.init = 1;
if ( !level || level==1)
{
rc = tdbio_set_dbname( dbname, !!level );
if( !rc )
{
if( !level )
return;
/* verify that our own keys are in the trustDB
* or move them to the trustdb. */
verify_own_keys();
/* should we check whether there is no other ultimately trusted
* key in the database? */
}
}
else
BUG();
if( rc )
log_fatal("can't init trustdb: %s\n", g10_errstr(rc) );
}
/***********************************************
************* Print helpers ****************
***********************************************/
/****************
* This function returns a letter for a trustvalue Trust flags
* are ignore.
*/
int
trust_letter (unsigned int value)
{
switch( (value & TRUST_MASK) )
{
case TRUST_UNKNOWN: return '-';
case TRUST_EXPIRED: return 'e';
case TRUST_UNDEFINED: return 'q';
case TRUST_NEVER: return 'n';
case TRUST_MARGINAL: return 'm';
case TRUST_FULLY: return 'f';
case TRUST_ULTIMATE: return 'u';
default: return 0;
}
}
/****************
* Recreate the WoT but do not ask for new ownertrusts
*/
void
check_trustdb()
{
init_trustdb();
validate_keys (0);
}
/*
* Recreate the WoT.
*/
void
update_trustdb()
{
init_trustdb();
validate_keys (1);
}
void
revalidation_mark (void)
{
init_trustdb();
/* we simply set the time for the next check to 1 (far back in 1970)
* so that a --update-trustdb will be scheduled */
tdbio_write_nextcheck (1);
}
/***********************************************
*********** Ownertrust et al. ****************
***********************************************/
static int
read_trust_record (PKT_public_key *pk, TRUSTREC *rec)
{
int rc;
init_trustdb();
rc = tdbio_search_trust_bypk (pk, rec);
if (rc == -1)
return -1; /* no record yet */
if (rc)
{
log_error ("trustdb: searching trust record failed: %s\n",
g10_errstr (rc));
return rc;
}
if (rec->rectype != RECTYPE_TRUST)
{
log_error ("trustdb: record %lu is not a trust record\n",
rec->recnum);
return G10ERR_TRUSTDB;
}
return 0;
}
/****************
* Return the assigned ownertrust value for the given public key.
* The key should be the primary key.
*/
unsigned int
get_ownertrust ( PKT_public_key *pk)
{
TRUSTREC rec;
int rc;
rc = read_trust_record (pk, &rec);
if (rc == -1)
return TRUST_UNKNOWN; /* no record yet */
if (rc)
{
tdbio_invalid ();
return rc; /* actually never reached */
}
return rec.r.trust.ownertrust;
}
/*
* Same as get_wonertrust byt return a trust letter
*/
int
get_ownertrust_info (PKT_public_key *pk)
{
unsigned int otrust;
int c;
otrust = get_ownertrust (pk);
c = trust_letter( (otrust & TRUST_MASK) );
if( !c )
c = '?';
return c;
}
/*
* Set the trust value of the given public key to the new value.
* The key should be a primary one.
*/
void
update_ownertrust (PKT_public_key *pk, unsigned int new_trust )
{
TRUSTREC rec;
int rc;
rc = read_trust_record (pk, &rec);
if (!rc)
{
if (DBG_TRUST)
log_debug ("update ownertrust from %u to %u\n",
(unsigned int)rec.r.trust.ownertrust, new_trust );
if (rec.r.trust.ownertrust != new_trust)
{
rec.r.trust.ownertrust = new_trust;
write_record( &rec );
revalidation_mark ();
do_sync();
}
}
else if (rc == -1)
{ /* no record yet - create a new one */
size_t dummy;
if (DBG_TRUST)
log_debug ("insert ownertrust %u\n", new_trust );
memset (&rec, 0, sizeof rec);
rec.recnum = tdbio_new_recnum ();
rec.rectype = RECTYPE_TRUST;
fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy);
rec.r.trust.ownertrust = new_trust;
write_record (&rec);
revalidation_mark ();
do_sync();
rc = 0;
}
else
{
tdbio_invalid ();
}
}
/*
* Note: Caller has to do a sync
*/
static void
update_validity (PKT_public_key *pk, const byte *namehash,
int depth, int validity)
{
TRUSTREC trec, vrec;
int rc;
ulong recno;
rc = read_trust_record (pk, &trec);
if (rc && rc != -1)
{
tdbio_invalid ();
return;
}
if (rc == -1) /* no record yet - create a new one */
{
size_t dummy;
rc = 0;
memset (&trec, 0, sizeof trec);
trec.recnum = tdbio_new_recnum ();
trec.rectype = RECTYPE_TRUST;
fingerprint_from_pk (pk, trec.r.trust.fingerprint, &dummy);
trec.r.trust.ownertrust = 0;
}
/* locate an existing one */
recno = trec.r.trust.validlist;
while (recno)
{
read_record (recno, &vrec, RECTYPE_VALID);
if ( !memcmp (vrec.r.valid.namehash, namehash, 20) )
break;
recno = vrec.r.valid.next;
}
if (!recno) /* insert a new validity record */
{
memset (&vrec, 0, sizeof vrec);
vrec.recnum = tdbio_new_recnum ();
vrec.rectype = RECTYPE_VALID;
memcpy (vrec.r.valid.namehash, namehash, 20);
vrec.r.valid.next = trec.r.trust.validlist;
}
vrec.r.valid.validity = validity;
write_record (&vrec);
trec.r.trust.depth = depth;
trec.r.trust.validlist = vrec.recnum;
write_record (&trec);
}
/***********************************************
********* Query trustdb values **************
***********************************************/
/*
* Return the validity information for PK. If the namehash is not
* NULL, the validity of the corresponsing user ID is returned,
* otherwise, a reasonable value for the entire key is returned.
*/
unsigned int
get_validity (PKT_public_key *pk, const byte *namehash)
{
TRUSTREC trec, vrec;
int rc;
ulong recno;
unsigned int validity;
init_trustdb ();
if (!did_nextcheck)
{
ulong scheduled;
did_nextcheck = 1;
scheduled = tdbio_read_nextcheck ();
if (scheduled && scheduled <= make_timestamp ())
{
if (opt.no_auto_check_trustdb)
log_info ("please do a --check-trustdb\n");
else {
log_info (_("checking the trustdb\n"));
validate_keys (0);
}
}
}
rc = read_trust_record (pk, &trec);
if (rc && rc != -1)
{
tdbio_invalid ();
return 0;
}
if (rc == -1) /* no record found */
return TRUST_UNKNOWN;
/* loop over all user IDs */
recno = trec.r.trust.validlist;
validity = 0;
while (recno)
{
read_record (recno, &vrec, RECTYPE_VALID);
if ( validity < (vrec.r.valid.validity & TRUST_MASK) )
validity = (vrec.r.valid.validity & TRUST_MASK);
if ( namehash && !memcmp (vrec.r.valid.namehash, namehash, 20) )
break;
recno = vrec.r.valid.next;
}
if (recno) /* okay, use the user ID associated one */
validity = (vrec.r.valid.validity & TRUST_MASK);
if ( (trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) )
validity |= TRUST_FLAG_DISABLED;
/* set some flags direct from the key */
if (pk->is_revoked)
validity |= TRUST_FLAG_REVOKED;
/* Note: expiration is a trust value and not a flag - don't know why
* I initially designed it that way */
if (pk->has_expired)
validity = (validity & ~TRUST_MASK) | TRUST_EXPIRED;
return validity;
}
int
get_validity_info (PKT_public_key *pk, const byte *namehash)
{
int trustlevel;
int c;
trustlevel = get_validity (pk, namehash);
if( trustlevel & TRUST_FLAG_DISABLED )
return 'd';
if( trustlevel & TRUST_FLAG_REVOKED )
return 'r';
c = trust_letter ( (trustlevel & TRUST_MASK) );
if( !c )
c = '?';
return c;
}
void
list_trust_path( const char *username )
{
}
/****************
* Enumerate all keys, which are needed to build all trust paths for
* the given key. This function does not return the key itself or
* the ultimate key (the last point in cerificate chain). Only
* certificate chains which ends up at an ultimately trusted key
* are listed. If ownertrust or validity is not NULL, the corresponding
* value for the returned LID is also returned in these variable(s).
*
* 1) create a void pointer and initialize it to NULL
* 2) pass this void pointer by reference to this function.
* Set lid to the key you want to enumerate and pass it by reference.
* 3) call this function as long as it does not return -1
* to indicate EOF. LID does contain the next key used to build the web
* 4) Always call this function a last time with LID set to NULL,
* so that it can free its context.
*
* Returns: -1 on EOF or the level of the returned LID
*/
int
enum_cert_paths( void **context, ulong *lid,
unsigned *ownertrust, unsigned *validity )
{
return -1;
}
/****************
* Print the current path
*/
void
enum_cert_paths_print( void **context, FILE *fp,
int refresh, ulong selected_lid )
{
return;
}
/****************************************
*********** NEW NEW NEW ****************
****************************************/
static unsigned int
ask_ownertrust (u32 *kid)
{
PKT_public_key *pk;
int rc;
unsigned int ot;
pk = m_alloc_clear (sizeof *pk);
rc = get_pubkey (pk, kid);
if (rc)
{
log_error (_("public key %08lX not found: %s\n"),
(ulong)kid[1], g10_errstr(rc) );
return TRUST_UNKNOWN;
}
if (edit_ownertrust (pk, 0))
ot = get_ownertrust (pk);
else
ot = TRUST_UNDEFINED;
free_public_key( pk );
return ot;
}
static int
search_skipfnc (void *opaque, u32 *kid)
{
return test_key_hash_table ((KeyHashTable)opaque, kid);
}
/*
* Scan all keys and return a key_array of all keys which are
* indicated as found by the supplied CMPFNC. The caller has to pass
* a keydb handle so that we don't use to create our own. Returns
* either a key_array or NULL in case of an error. No results found
* are indicated by an empty array. Caller hast to release the
* returned array.
*/
static struct key_array *
make_key_array (KEYDB_HANDLE hd, KeyHashTable visited,
int (*cmpfnc)(KBNODE kb, void *opaque), void *cmpval)
{
KBNODE keyblock = NULL;
struct key_array *keys = NULL;
size_t nkeys, maxkeys;
int rc;
KEYDB_SEARCH_DESC desc;
maxkeys = 1000;
keys = m_alloc ((maxkeys+1) * sizeof *keys);
nkeys = 0;
rc = keydb_search_reset (hd);
if (rc)
{
log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc));
m_free (keys);
return NULL;
}
memset (&desc, 0, sizeof desc);
desc.mode = KEYDB_SEARCH_MODE_FIRST;
desc.skipfnc = search_skipfnc;
desc.skipfncvalue = visited;
rc = keydb_search (hd, &desc, 1);
if (rc == -1)
{
keys[nkeys].keyblock = NULL;
return keys;
}
if (rc)
{
log_error ("keydb_search_first failed: %s\n", g10_errstr(rc));
m_free (keys);
return NULL;
}
desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */
do
{
PKT_public_key *pk;
u32 kid[2];
rc = keydb_get_keyblock (hd, &keyblock);
if (rc)
{
log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
m_free (keys);
return NULL;
}
if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY)
{
log_debug ("ooops: invalid pkttype %d encountered\n",
keyblock->pkt->pkttype);
dump_kbnode (keyblock);
release_kbnode(keyblock);
continue;
}
/* prepare the keyblock for further processing */
merge_keys_and_selfsig (keyblock);
clear_kbnode_flags (keyblock);
pk = keyblock->pkt->pkt.public_key;
keyid_from_pk (pk, kid); /*(cheap: should already be cached in the pk)*/
if (pk->has_expired || pk->is_revoked)
{
/* it does not make sense to look further at those keys */
add_key_hash_table (visited, kid);
}
else if (cmpfnc (keyblock, cmpval))
{
if (nkeys == maxkeys) {
maxkeys += 1000;
keys = m_realloc (keys, (maxkeys+1) * sizeof *keys);
}
keys[nkeys++].keyblock = keyblock;
/* This key is signed - don't check it again */
add_key_hash_table (visited, kid);
}
else
release_kbnode (keyblock);
keyblock = NULL;
}
while ( !(rc = keydb_search (hd, &desc, 1)) );
if (rc && rc != -1)
{
log_error ("keydb_search_next failed: %s\n", g10_errstr(rc));
m_free (keys);
return NULL;
}
keys[nkeys].keyblock = NULL;
return keys;
}
static void
dump_key_array (int depth, struct key_array *keys)
{
struct key_array *kar;
for (kar=keys; kar->keyblock; kar++)
{
KBNODE node = kar->keyblock;
u32 kid[2];
keyid_from_pk(node->pkt->pkt.public_key, kid);
printf ("%d:%08lX%08lX:K::%c::::\n",
depth, (ulong)kid[0], (ulong)kid[1], '?');
for (; node; node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID)
{
int len = node->pkt->pkt.user_id->len;
if (len > 30)
len = 30;
printf ("%d:%08lX%08lX:U:::%c:::",
depth, (ulong)kid[0], (ulong)kid[1],
(node->flag & 4)? 'f':
(node->flag & 2)? 'm':
(node->flag & 1)? 'q':'-');
print_string (stdout, node->pkt->pkt.user_id->name, len, ':');
putchar (':');
putchar ('\n');
}
}
}
}
static void
store_validation_status (int depth, KBNODE keyblock)
{
KBNODE node;
byte namehash[20];
int status;
int any = 0;
for (node=keyblock; node; node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *uid = node->pkt->pkt.user_id;
if (node->flag & 4)
status = TRUST_FULLY;
else if (node->flag & 2)
status = TRUST_MARGINAL;
else if (node->flag & 1)
status = TRUST_UNDEFINED;
else
status = 0;
if (status)
{
if( uid->photo )
rmd160_hash_buffer (namehash, uid->photo, uid->photolen);
else
rmd160_hash_buffer (namehash, uid->name, uid->len );
update_validity (keyblock->pkt->pkt.public_key,
namehash, depth, status);
any = 1;
}
}
}
if (any)
do_sync ();
}
/*
* Return true if the key is signed by one of the keys in the given
* key ID list. User IDs with a valid signature are marked by node
* flags as follows:
* flag bit 0: There is at least one signature
* 1: There is marginal confidence that this is a legitimate uid
* 2: There is full confidence that this is a legitimate uid.
*/
static int
cmp_kid_for_make_key_array (KBNODE kb, void *opaque)
{
struct key_item *klist = opaque;
struct key_item *kr;
KBNODE node, uidnode=NULL;
PKT_public_key *pk = kb->pkt->pkt.public_key;
u32 main_kid[2];
int issigned=0, any_signed = 0, fully_count =0, marginal_count = 0;
keyid_from_pk(pk, main_kid);
for (node=kb; node; node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID)
{
if (uidnode && issigned)
{
if (fully_count >= opt.completes_needed
|| marginal_count >= opt.marginals_needed )
uidnode->flag |= 4;
else if (fully_count || marginal_count)
uidnode->flag |= 2;
uidnode->flag |= 1;
any_signed = 1;
}
uidnode = node;
issigned = 0;
fully_count = marginal_count = 0;
}
else if (node->pkt->pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = node->pkt->pkt.signature;
if ( sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1])
; /* ignore self-signatures */
else if ( IS_UID_SIG(sig) )
{ /* certification */
for (kr=klist; kr; kr = kr->next)
{
if (kr->kid[0] == sig->keyid[0]
&& kr->kid[1] == sig->keyid[1])
{
/* Hmmm: Should we first look whether this
* signature has been revoked? Avoids problem in
* fixing the counters later and we might also
* want to check the signature here. It might
* also be worth to find the latest signature
* first so that we count only one signature for
* each key */
if (kr->ownertrust == TRUST_ULTIMATE)
fully_count = opt.completes_needed;
else if (kr->ownertrust == TRUST_FULLY)
fully_count++;
else if (kr->ownertrust == TRUST_MARGINAL)
marginal_count++;
issigned = 1;
/* fixme: track timestamp to see handle cert revocs */
break;
}
}
}
else if ( IS_UID_REV(sig) )
{ /* certificate revocation */
/* fixme: reset issigned and counter if needed */
}
}
}
if (uidnode && issigned)
{
if (fully_count >= opt.completes_needed
|| marginal_count >= opt.marginals_needed )
uidnode->flag |= 4;
else if (fully_count || marginal_count)
uidnode->flag |= 2;
uidnode->flag |= 1;
any_signed = 1;
}
return any_signed;
}
/*
* Run the key validation procedure.
*
*-----------------------------------
* Assume all signatures are good.
* Find all ultimately trusted keys (UTK).
* mark them all as seen.
* Loop over all key to find keys signed by an UTK.
* mark key as seen
* if OWNERTRUST of that key is undefined
* ask user for ownertrust
* For each user ID of that key which is signed by the UTK
* Calculate validity by counting trusted signatures.
* Set validity of user ID
* if user ID validity is full
* Loop over all keys to find keys signed by current key
* skip those which are already seen.
*
*TODO:
*
* - Make sure that only valid signatures are checked.
* - Skip revoked keys.
*
*/
static int
validate_keys (int interactive)
{
int rc = 0;
struct key_item *klist = NULL;
struct key_item *k;
struct key_array *keys = NULL;
struct key_array *kar;
KEYDB_HANDLE kdb = NULL;
KBNODE node;
int depth;
int key_count;
int ot_unknown;
int ot_undefined;
int ot_marginal;
int ot_full;
int ot_ultimate;
KeyHashTable visited;
visited = new_key_hash_table ();
if (!utk_list)
{
log_info ("no ultimately trusted keys found\n");
goto leave;
}
klist = utk_list;
kdb = keydb_new (0);
for (depth=0; depth < opt.max_cert_depth; depth++)
{
/* See whether we should assign ownertrust values to the
* keys in utk_list.
*/
ot_unknown = ot_undefined = ot_marginal = ot_full = ot_ultimate = 0;
for (k=klist; k; k = k->next)
{
if (interactive && k->ownertrust == TRUST_UNKNOWN)
k->ownertrust = ask_ownertrust (k->kid);
if (k->ownertrust == TRUST_UNKNOWN)
ot_unknown++;
else if (k->ownertrust == TRUST_UNDEFINED)
ot_undefined++;
else if (k->ownertrust == TRUST_MARGINAL)
ot_marginal++;
else if (k->ownertrust == TRUST_FULLY)
ot_full++;
else if (k->ownertrust == TRUST_ULTIMATE)
ot_ultimate++;
}
/* Find all keys which are signed by a key in kdlist */
keys = make_key_array (kdb, visited, cmp_kid_for_make_key_array, klist);
if (!keys)
{
log_error ("make_key_array failed\n");
rc = G10ERR_GENERAL;
goto leave;
}
for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++)
;
/* Store the calculated valididation status somewhere */
if (opt.verbose > 1)
dump_key_array (depth, keys);
log_info (_("depth=%d keys=%d (-=%d q=%d m=%d f=%d u=%d)\n"),
depth, key_count, ot_unknown, ot_undefined,
ot_marginal, ot_full, ot_ultimate );
for (kar=keys; kar->keyblock; kar++)
store_validation_status (depth, kar->keyblock);
/* Build a new kdlist from all fully valid keys in KEYS */
if (klist != utk_list)
release_key_items (klist);
klist = NULL;
for (kar=keys; kar->keyblock; kar++)
{
for (node=kar->keyblock; node; node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID && (node->flag & 4))
{
k = new_key_item ();
keyid_from_pk (kar->keyblock->pkt->pkt.public_key, k->kid);
k->ownertrust = get_ownertrust (kar->keyblock
->pkt->pkt.public_key);
k->next = klist;
klist = k;
break;
}
}
}
release_key_array (keys);
keys = NULL;
if (!klist)
break; /* no need to dive in deeper */
}
leave:
keydb_release (kdb);
release_key_array (keys);
release_key_items (klist);
release_key_hash_table (visited);
if (!rc) /* mark trustDB as checked */
{
tdbio_write_nextcheck (0);
do_sync ();
}
return rc;
}