2003-06-05 09:14:21 +02:00
|
|
|
|
/* trustdb.c
|
|
|
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
|
|
|
|
|
* 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>
|
|
|
|
|
|
|
|
|
|
#ifndef DISABLE_REGEX
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#ifdef USE_GNU_REGEX
|
|
|
|
|
#include "_regex.h"
|
|
|
|
|
#else
|
|
|
|
|
#include <regex.h>
|
|
|
|
|
#endif
|
|
|
|
|
#endif /* !DISABLE_REGEX */
|
|
|
|
|
|
2003-06-18 21:56:13 +02:00
|
|
|
|
#include "gpg.h"
|
2003-06-05 09:14:21 +02:00
|
|
|
|
#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,min_ownertrust;
|
|
|
|
|
byte trust_depth;
|
|
|
|
|
byte trust_value;
|
|
|
|
|
char *trust_regexp;
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
|
|
static int pending_check_trustdb;
|
|
|
|
|
|
|
|
|
|
static int validate_keys (int interactive);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**********************************************
|
|
|
|
|
************* some helpers *******************
|
|
|
|
|
**********************************************/
|
|
|
|
|
|
|
|
|
|
static struct key_item *
|
|
|
|
|
new_key_item (void)
|
|
|
|
|
{
|
|
|
|
|
struct key_item *k;
|
|
|
|
|
|
2003-06-18 21:56:13 +02:00
|
|
|
|
k = xcalloc (1,sizeof *k);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
release_key_items (struct key_item *k)
|
|
|
|
|
{
|
|
|
|
|
struct key_item *k2;
|
|
|
|
|
|
|
|
|
|
for (; k; k = k2)
|
|
|
|
|
{
|
|
|
|
|
k2 = k->next;
|
2003-06-18 21:56:13 +02:00
|
|
|
|
xfree (k->trust_regexp);
|
|
|
|
|
xfree (k);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 10 bits to index a table of 1024 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;
|
|
|
|
|
|
2003-06-18 21:56:13 +02:00
|
|
|
|
tbl = xcalloc (1,1024 * sizeof *tbl);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return tbl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
release_key_hash_table (KeyHashTable tbl)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!tbl)
|
|
|
|
|
return;
|
|
|
|
|
for (i=0; i < 1024; i++)
|
|
|
|
|
release_key_items (tbl[i]);
|
2003-06-18 21:56:13 +02:00
|
|
|
|
xfree (tbl);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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] & 0x03ff)]; 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 identified by its key ID.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
add_key_hash_table (KeyHashTable tbl, u32 *kid)
|
|
|
|
|
{
|
|
|
|
|
struct key_item *k, *kk;
|
|
|
|
|
|
|
|
|
|
for (k = tbl[(kid[1] & 0x03ff)]; 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] & 0x03ff)];
|
|
|
|
|
tbl[(kid[1] & 0x03ff)] = 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);
|
2003-06-18 21:56:13 +02:00
|
|
|
|
xfree (keys);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************
|
|
|
|
|
********** 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;
|
|
|
|
|
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Put any --trusted-key keys into 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);
|
|
|
|
|
}
|
|
|
|
|
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"),
|
2003-06-18 21:56:13 +02:00
|
|
|
|
recno, rec->rectype, gpg_strerror (rc) );
|
2003-06-05 09:14:21 +02:00
|
|
|
|
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"),
|
2003-06-18 21:56:13 +02:00
|
|
|
|
rec->recnum, rec->rectype, gpg_strerror (rc) );
|
2003-06-05 09:14:21 +02:00
|
|
|
|
tdbio_invalid();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* sync the TrustDb and die on error
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
do_sync(void)
|
|
|
|
|
{
|
|
|
|
|
int rc = tdbio_sync ();
|
|
|
|
|
if(rc)
|
|
|
|
|
{
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error (_("trustdb: sync failed: %s\n"), gpg_strerror (rc) );
|
2003-06-05 09:14:21 +02:00
|
|
|
|
g10_exit(2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
trust_model_string(void)
|
|
|
|
|
{
|
|
|
|
|
switch(opt.trust_model)
|
|
|
|
|
{
|
|
|
|
|
case TM_PGP: return "PGP";
|
|
|
|
|
case TM_CLASSIC: return "classic";
|
|
|
|
|
case TM_ALWAYS: return "always";
|
|
|
|
|
default: return "unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************
|
|
|
|
|
* 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;
|
2003-06-18 21:56:13 +02:00
|
|
|
|
trustdb_args.dbname = dbname? xstrdup (dbname): NULL;
|
2003-06-05 09:14:21 +02:00
|
|
|
|
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 )
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_fatal("can't init trustdb: %s\n", gpg_strerror (rc) );
|
2003-06-05 09:14:21 +02:00
|
|
|
|
|
|
|
|
|
if(opt.trust_model==TM_AUTO)
|
|
|
|
|
{
|
|
|
|
|
/* Try and set the trust model off of whatever the trustdb says
|
|
|
|
|
it is. */
|
|
|
|
|
opt.trust_model=tdbio_read_model();
|
|
|
|
|
|
|
|
|
|
/* Sanity check this ;) */
|
|
|
|
|
if(opt.trust_model!=TM_PGP && opt.trust_model!=TM_CLASSIC)
|
|
|
|
|
{
|
|
|
|
|
log_info(_("unable to use unknown trust model (%d) - "
|
|
|
|
|
"assuming %s trust model\n"),opt.trust_model,"PGP");
|
|
|
|
|
opt.trust_model=TM_PGP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(opt.verbose)
|
|
|
|
|
log_info(_("using %s trust model\n"),trust_model_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
|
|
|
|
|
&& !tdbio_db_matches_options())
|
|
|
|
|
pending_check_trustdb=1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************
|
|
|
|
|
************* Print helpers ****************
|
|
|
|
|
***********************************************/
|
|
|
|
|
|
|
|
|
|
/****************
|
|
|
|
|
* This function returns a letter for a trustvalue Trust flags
|
|
|
|
|
* are ignore.
|
|
|
|
|
*/
|
|
|
|
|
static 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 '?';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The strings here are similar to those in
|
|
|
|
|
pkclist.c:do_edit_ownertrust() */
|
|
|
|
|
const char *
|
|
|
|
|
trust_value_to_string (unsigned int value)
|
|
|
|
|
{
|
|
|
|
|
switch( (value & TRUST_MASK) )
|
|
|
|
|
{
|
|
|
|
|
case TRUST_UNKNOWN: return _("unknown");
|
|
|
|
|
case TRUST_EXPIRED: return _("expired");
|
|
|
|
|
case TRUST_UNDEFINED: return _("undefined");
|
|
|
|
|
case TRUST_NEVER: return _("never");
|
|
|
|
|
case TRUST_MARGINAL: return _("marginal");
|
|
|
|
|
case TRUST_FULLY: return _("full");
|
|
|
|
|
case TRUST_ULTIMATE: return _("ultimate");
|
|
|
|
|
default: return "err";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
string_to_trust_value (const char *str)
|
|
|
|
|
{
|
|
|
|
|
if(ascii_strcasecmp(str,"undefined")==0)
|
|
|
|
|
return TRUST_UNDEFINED;
|
|
|
|
|
else if(ascii_strcasecmp(str,"never")==0)
|
|
|
|
|
return TRUST_NEVER;
|
|
|
|
|
else if(ascii_strcasecmp(str,"marginal")==0)
|
|
|
|
|
return TRUST_MARGINAL;
|
|
|
|
|
else if(ascii_strcasecmp(str,"full")==0)
|
|
|
|
|
return TRUST_FULLY;
|
|
|
|
|
else if(ascii_strcasecmp(str,"ultimate")==0)
|
|
|
|
|
return TRUST_ULTIMATE;
|
|
|
|
|
else
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************
|
|
|
|
|
* Recreate the WoT but do not ask for new ownertrusts. Special
|
|
|
|
|
* feature: In batch mode and without a forced yes, this is only done
|
|
|
|
|
* when a check is due. This can be used to run the check from a crontab
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
check_trustdb ()
|
|
|
|
|
{
|
|
|
|
|
init_trustdb();
|
|
|
|
|
if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
|
|
|
|
|
{
|
|
|
|
|
if (opt.batch && !opt.answer_yes)
|
|
|
|
|
{
|
|
|
|
|
ulong scheduled;
|
|
|
|
|
|
|
|
|
|
scheduled = tdbio_read_nextcheck ();
|
|
|
|
|
if (!scheduled)
|
|
|
|
|
{
|
|
|
|
|
log_info (_("no need for a trustdb check\n"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scheduled > make_timestamp ())
|
|
|
|
|
{
|
|
|
|
|
log_info (_("next trustdb check due at %s\n"),
|
|
|
|
|
strtimestamp (scheduled));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate_keys (0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
log_info (_("no need for a trustdb check with \"%s\" trust model\n"),
|
|
|
|
|
trust_model_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Recreate the WoT.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
update_trustdb()
|
|
|
|
|
{
|
|
|
|
|
init_trustdb();
|
|
|
|
|
if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
|
|
|
|
|
validate_keys (1);
|
|
|
|
|
else
|
|
|
|
|
log_info (_("no need for a trustdb update with \"%s\" trust model\n"),
|
|
|
|
|
trust_model_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
if (tdbio_write_nextcheck (1))
|
|
|
|
|
do_sync ();
|
|
|
|
|
pending_check_trustdb = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
trustdb_pending_check(void)
|
|
|
|
|
{
|
|
|
|
|
return pending_check_trustdb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************
|
|
|
|
|
*********** 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",
|
2003-06-18 21:56:13 +02:00
|
|
|
|
gpg_strerror (rc));
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rec->rectype != RECTYPE_TRUST)
|
|
|
|
|
{
|
|
|
|
|
log_error ("trustdb: record %lu is not a trust record\n",
|
|
|
|
|
rec->recnum);
|
2003-06-18 21:56:13 +02:00
|
|
|
|
return GPG_ERR_TRUSTDB;
|
2003-06-05 09:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
get_min_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.min_ownertrust;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Same as get_ownertrust but this takes the minimum ownertrust value
|
|
|
|
|
* into into account, and will bump up the value as needed.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
get_ownertrust_with_min (PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
unsigned int otrust,otrust_min;
|
|
|
|
|
|
|
|
|
|
otrust = (get_ownertrust (pk) & TRUST_MASK);
|
|
|
|
|
otrust_min = get_min_ownertrust (pk);
|
|
|
|
|
if(otrust<otrust_min)
|
|
|
|
|
{
|
|
|
|
|
/* If the trust that the user has set is less than the trust
|
|
|
|
|
that was calculated from a trust signature chain, use the
|
|
|
|
|
higher of the two. We do this here and not in
|
|
|
|
|
get_ownertrust since the underlying ownertrust should not
|
|
|
|
|
really be set - just the appearance of the ownertrust. */
|
|
|
|
|
|
|
|
|
|
otrust=otrust_min;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return otrust;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Same as get_ownertrust but return a trust letter instead of an
|
|
|
|
|
* value. This takes the minimum ownertrust value into account.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
get_ownertrust_info (PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
return trust_letter(get_ownertrust_with_min(pk));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Same as get_ownertrust but return a trust string instead of an
|
|
|
|
|
* value. This takes the minimum ownertrust value into account.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
get_ownertrust_string (PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
return trust_value_to_string(get_ownertrust_with_min(pk));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
update_min_ownertrust (u32 *kid, unsigned int new_trust )
|
|
|
|
|
{
|
|
|
|
|
PKT_public_key *pk;
|
|
|
|
|
TRUSTREC rec;
|
|
|
|
|
int rc;
|
|
|
|
|
|
2003-06-18 21:56:13 +02:00
|
|
|
|
pk = xcalloc (1,sizeof *pk);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
rc = get_pubkey (pk, kid);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("public key %08lX not found: %s\n"),
|
2003-06-18 21:56:13 +02:00
|
|
|
|
(ulong)kid[1], gpg_strerror (rc) );
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = read_trust_record (pk, &rec);
|
|
|
|
|
if (!rc)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_TRUST)
|
|
|
|
|
log_debug ("key %08lX: update min_ownertrust from %u to %u\n",
|
|
|
|
|
(ulong)kid[1],(unsigned int)rec.r.trust.min_ownertrust,
|
|
|
|
|
new_trust );
|
|
|
|
|
if (rec.r.trust.min_ownertrust != new_trust)
|
|
|
|
|
{
|
|
|
|
|
rec.r.trust.min_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 min_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.min_ownertrust = new_trust;
|
|
|
|
|
write_record (&rec);
|
|
|
|
|
revalidation_mark ();
|
|
|
|
|
do_sync ();
|
|
|
|
|
rc = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tdbio_invalid ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clear the ownertrust and min_ownertrust values. Return true if a
|
|
|
|
|
change actually happened. */
|
|
|
|
|
int
|
|
|
|
|
clear_ownertrusts (PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
TRUSTREC rec;
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
rc = read_trust_record (pk, &rec);
|
|
|
|
|
if (!rc)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_TRUST)
|
|
|
|
|
{
|
|
|
|
|
log_debug ("clearing ownertrust (old value %u)\n",
|
|
|
|
|
(unsigned int)rec.r.trust.ownertrust);
|
|
|
|
|
log_debug ("clearing min_ownertrust (old value %u)\n",
|
|
|
|
|
(unsigned int)rec.r.trust.min_ownertrust);
|
|
|
|
|
}
|
|
|
|
|
if (rec.r.trust.ownertrust || rec.r.trust.min_ownertrust)
|
|
|
|
|
{
|
|
|
|
|
rec.r.trust.ownertrust = 0;
|
|
|
|
|
rec.r.trust.min_ownertrust = 0;
|
|
|
|
|
write_record( &rec );
|
|
|
|
|
revalidation_mark ();
|
|
|
|
|
do_sync ();
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (rc != -1)
|
|
|
|
|
{
|
|
|
|
|
tdbio_invalid ();
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Note: Caller has to do a sync
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
update_validity (PKT_public_key *pk, PKT_user_id *uid,
|
|
|
|
|
int depth, int validity)
|
|
|
|
|
{
|
|
|
|
|
TRUSTREC trec, vrec;
|
|
|
|
|
int rc;
|
|
|
|
|
ulong recno;
|
|
|
|
|
|
|
|
|
|
namehash_from_uid(uid);
|
|
|
|
|
|
|
|
|
|
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, uid->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, uid->namehash, 20);
|
|
|
|
|
vrec.r.valid.next = trec.r.trust.validlist;
|
|
|
|
|
trec.r.trust.validlist = vrec.recnum;
|
|
|
|
|
}
|
|
|
|
|
vrec.r.valid.validity = validity;
|
|
|
|
|
vrec.r.valid.full_count = uid->help_full_count;
|
|
|
|
|
vrec.r.valid.marginal_count = uid->help_marginal_count;
|
|
|
|
|
write_record (&vrec);
|
|
|
|
|
trec.r.trust.depth = depth;
|
|
|
|
|
write_record (&trec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* reset validity for all user IDs. Caller must sync. */
|
|
|
|
|
static int
|
|
|
|
|
clear_validity (PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
TRUSTREC trec, vrec;
|
|
|
|
|
int rc;
|
|
|
|
|
ulong recno;
|
|
|
|
|
int any = 0;
|
|
|
|
|
|
|
|
|
|
rc = read_trust_record (pk, &trec);
|
|
|
|
|
if (rc && rc != -1)
|
|
|
|
|
{
|
|
|
|
|
tdbio_invalid ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (rc == -1) /* no record yet - no need to clear it then ;-) */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Clear minimum ownertrust, if any */
|
|
|
|
|
if(trec.r.trust.min_ownertrust)
|
|
|
|
|
{
|
|
|
|
|
trec.r.trust.min_ownertrust=0;
|
|
|
|
|
write_record(&trec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
recno = trec.r.trust.validlist;
|
|
|
|
|
while (recno)
|
|
|
|
|
{
|
|
|
|
|
read_record (recno, &vrec, RECTYPE_VALID);
|
|
|
|
|
if ((vrec.r.valid.validity & TRUST_MASK)
|
|
|
|
|
|| vrec.r.valid.marginal_count || vrec.r.valid.full_count)
|
|
|
|
|
{
|
|
|
|
|
vrec.r.valid.validity &= ~TRUST_MASK;
|
|
|
|
|
vrec.r.valid.marginal_count = vrec.r.valid.full_count = 0;
|
|
|
|
|
write_record (&vrec);
|
|
|
|
|
any = 1;
|
|
|
|
|
}
|
|
|
|
|
recno = vrec.r.valid.next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return any;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***********************************************
|
|
|
|
|
********* Query trustdb values **************
|
|
|
|
|
***********************************************/
|
|
|
|
|
|
|
|
|
|
/* Return true if key is disabled */
|
|
|
|
|
int
|
|
|
|
|
cache_disabled_value(PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
TRUSTREC trec;
|
|
|
|
|
int disabled=0;
|
|
|
|
|
|
|
|
|
|
if(pk->is_disabled)
|
|
|
|
|
return (pk->is_disabled==2);
|
|
|
|
|
|
|
|
|
|
init_trustdb();
|
|
|
|
|
|
|
|
|
|
rc = read_trust_record (pk, &trec);
|
|
|
|
|
if (rc && rc != -1)
|
|
|
|
|
{
|
|
|
|
|
tdbio_invalid ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if (rc == -1) /* no record found, so assume not disabled */
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
if(trec.r.trust.ownertrust & TRUST_FLAG_DISABLED)
|
|
|
|
|
disabled=1;
|
|
|
|
|
|
|
|
|
|
/* Cache it for later so we don't need to look at the trustdb every
|
|
|
|
|
time */
|
|
|
|
|
if(disabled)
|
|
|
|
|
pk->is_disabled=2;
|
|
|
|
|
else
|
|
|
|
|
pk->is_disabled=1;
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
return disabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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, PKT_user_id *uid)
|
|
|
|
|
{
|
|
|
|
|
static int did_nextcheck;
|
|
|
|
|
TRUSTREC trec, vrec;
|
|
|
|
|
int rc;
|
|
|
|
|
ulong recno;
|
|
|
|
|
unsigned int validity;
|
|
|
|
|
u32 kid[2];
|
|
|
|
|
PKT_public_key *main_pk;
|
|
|
|
|
|
|
|
|
|
if(uid)
|
|
|
|
|
namehash_from_uid(uid);
|
|
|
|
|
|
|
|
|
|
init_trustdb ();
|
|
|
|
|
if (!did_nextcheck
|
|
|
|
|
&& (opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC))
|
|
|
|
|
{
|
|
|
|
|
ulong scheduled;
|
|
|
|
|
|
|
|
|
|
did_nextcheck = 1;
|
|
|
|
|
scheduled = tdbio_read_nextcheck ();
|
|
|
|
|
if (scheduled && scheduled <= make_timestamp ())
|
|
|
|
|
{
|
|
|
|
|
if (opt.no_auto_check_trustdb)
|
|
|
|
|
{
|
|
|
|
|
pending_check_trustdb = 1;
|
|
|
|
|
log_info (_("please do a --check-trustdb\n"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
log_info (_("checking the trustdb\n"));
|
|
|
|
|
validate_keys (0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyid_from_pk (pk, kid);
|
|
|
|
|
if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1])
|
|
|
|
|
{ /* this is a subkey - get the mainkey */
|
2003-06-18 21:56:13 +02:00
|
|
|
|
main_pk = xcalloc (1,sizeof *main_pk);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
rc = get_pubkey (main_pk, pk->main_keyid);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error getting main key %08lX of subkey %08lX: %s\n",
|
2003-06-18 21:56:13 +02:00
|
|
|
|
(ulong)pk->main_keyid[1], (ulong)kid[1], gpg_strerror (rc));
|
2003-06-05 09:14:21 +02:00
|
|
|
|
validity = TRUST_UNKNOWN;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
main_pk = pk;
|
|
|
|
|
|
|
|
|
|
rc = read_trust_record (main_pk, &trec);
|
|
|
|
|
if (rc && rc != -1)
|
|
|
|
|
{
|
|
|
|
|
tdbio_invalid ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (rc == -1) /* no record found */
|
|
|
|
|
{
|
|
|
|
|
validity = TRUST_UNKNOWN;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* loop over all user IDs */
|
|
|
|
|
recno = trec.r.trust.validlist;
|
|
|
|
|
validity = 0;
|
|
|
|
|
while (recno)
|
|
|
|
|
{
|
|
|
|
|
read_record (recno, &vrec, RECTYPE_VALID);
|
|
|
|
|
|
|
|
|
|
if(uid)
|
|
|
|
|
{
|
|
|
|
|
/* If a user ID is given we return the validity for that
|
|
|
|
|
user ID ONLY. If the namehash is not found, then there
|
|
|
|
|
is no validity at all (i.e. the user ID wasn't
|
|
|
|
|
signed). */
|
|
|
|
|
if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
|
|
|
|
|
{
|
|
|
|
|
validity=(vrec.r.valid.validity & TRUST_MASK);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If no namehash is given, we take the maximum validity
|
|
|
|
|
over all user IDs */
|
|
|
|
|
if ( validity < (vrec.r.valid.validity & TRUST_MASK) )
|
|
|
|
|
validity = (vrec.r.valid.validity & TRUST_MASK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
recno = vrec.r.valid.next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( (trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) )
|
|
|
|
|
{
|
|
|
|
|
validity |= TRUST_FLAG_DISABLED;
|
|
|
|
|
pk->is_disabled=2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
pk->is_disabled=1;
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
/* set some flags direct from the key */
|
|
|
|
|
if (main_pk->is_revoked)
|
|
|
|
|
validity |= TRUST_FLAG_REVOKED;
|
|
|
|
|
if (main_pk != pk && pk->is_revoked)
|
|
|
|
|
validity |= TRUST_FLAG_SUB_REVOKED;
|
|
|
|
|
/* Note: expiration is a trust value and not a flag - don't know why
|
|
|
|
|
* I initially designed it that way */
|
|
|
|
|
if (main_pk->has_expired || pk->has_expired)
|
|
|
|
|
validity = (validity & ~TRUST_MASK) | TRUST_EXPIRED;
|
|
|
|
|
|
|
|
|
|
if (pending_check_trustdb)
|
|
|
|
|
validity |= TRUST_FLAG_PENDING_CHECK;
|
|
|
|
|
|
|
|
|
|
if (main_pk != pk)
|
|
|
|
|
free_public_key (main_pk);
|
|
|
|
|
return validity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
get_validity_info (PKT_public_key *pk, PKT_user_id *uid)
|
|
|
|
|
{
|
|
|
|
|
int trustlevel;
|
|
|
|
|
|
|
|
|
|
trustlevel = get_validity (pk, uid);
|
|
|
|
|
if( trustlevel & TRUST_FLAG_REVOKED )
|
|
|
|
|
return 'r';
|
|
|
|
|
return trust_letter ( trustlevel );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
get_validity_string (PKT_public_key *pk, PKT_user_id *uid)
|
|
|
|
|
{
|
|
|
|
|
int trustlevel;
|
|
|
|
|
|
|
|
|
|
trustlevel = get_validity (pk, uid);
|
|
|
|
|
if( trustlevel & TRUST_FLAG_REVOKED )
|
|
|
|
|
return _("revoked");
|
|
|
|
|
return trust_value_to_string(trustlevel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_validity_counts (PKT_public_key *pk, PKT_user_id *uid)
|
|
|
|
|
{
|
|
|
|
|
TRUSTREC trec, vrec;
|
|
|
|
|
ulong recno;
|
|
|
|
|
|
|
|
|
|
if(pk==NULL || uid==NULL)
|
|
|
|
|
BUG();
|
|
|
|
|
|
|
|
|
|
namehash_from_uid(uid);
|
|
|
|
|
|
|
|
|
|
uid->help_marginal_count=uid->help_full_count=0;
|
|
|
|
|
|
|
|
|
|
init_trustdb ();
|
|
|
|
|
|
|
|
|
|
if(read_trust_record (pk, &trec)!=0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* loop over all user IDs */
|
|
|
|
|
recno = trec.r.trust.validlist;
|
|
|
|
|
while (recno)
|
|
|
|
|
{
|
|
|
|
|
read_record (recno, &vrec, RECTYPE_VALID);
|
|
|
|
|
|
|
|
|
|
if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
|
|
|
|
|
{
|
|
|
|
|
uid->help_marginal_count=vrec.r.valid.marginal_count;
|
|
|
|
|
uid->help_full_count=vrec.r.valid.full_count;
|
|
|
|
|
/* printf("Fetched marginal %d, full %d\n",uid->help_marginal_count,uid->help_full_count); */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
recno = vrec.r.valid.next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 int
|
|
|
|
|
ask_ownertrust (u32 *kid,int minimum)
|
|
|
|
|
{
|
|
|
|
|
PKT_public_key *pk;
|
|
|
|
|
int rc;
|
|
|
|
|
int ot;
|
|
|
|
|
|
2003-06-18 21:56:13 +02:00
|
|
|
|
pk = xcalloc (1,sizeof *pk);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
rc = get_pubkey (pk, kid);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("public key %08lX not found: %s\n"),
|
2003-06-18 21:56:13 +02:00
|
|
|
|
(ulong)kid[1], gpg_strerror (rc) );
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return TRUST_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(opt.force_ownertrust)
|
|
|
|
|
{
|
|
|
|
|
log_info("force trust for key %08lX%08lX to %s\n",
|
|
|
|
|
(ulong)kid[0],(ulong)kid[1],
|
|
|
|
|
trust_value_to_string(opt.force_ownertrust));
|
|
|
|
|
update_ownertrust(pk,opt.force_ownertrust);
|
|
|
|
|
ot=opt.force_ownertrust;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ot=edit_ownertrust(pk,0);
|
|
|
|
|
if(ot>0)
|
|
|
|
|
ot = get_ownertrust (pk);
|
|
|
|
|
else if(ot==0)
|
|
|
|
|
ot = minimum?minimum:TRUST_UNDEFINED;
|
|
|
|
|
else
|
|
|
|
|
ot = -1; /* quit */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free_public_key( pk );
|
|
|
|
|
|
|
|
|
|
return ot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mark_keyblock_seen (KeyHashTable tbl, KBNODE node)
|
|
|
|
|
{
|
|
|
|
|
for ( ;node; node = node->next )
|
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_KEY
|
|
|
|
|
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
|
|
|
|
{
|
|
|
|
|
u32 aki[2];
|
|
|
|
|
|
|
|
|
|
keyid_from_pk (node->pkt->pkt.public_key, aki);
|
|
|
|
|
add_key_hash_table (tbl, aki);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, KeyHashTable stored)
|
|
|
|
|
{
|
|
|
|
|
KBNODE node;
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
update_validity (keyblock->pkt->pkt.public_key,
|
|
|
|
|
uid, depth, status);
|
|
|
|
|
|
|
|
|
|
mark_keyblock_seen(stored,keyblock);
|
|
|
|
|
|
|
|
|
|
any = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (any)
|
|
|
|
|
do_sync ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* check whether the signature sig is in the klist k
|
|
|
|
|
*/
|
|
|
|
|
static struct key_item *
|
|
|
|
|
is_in_klist (struct key_item *k, PKT_signature *sig)
|
|
|
|
|
{
|
|
|
|
|
for (; k; k = k->next)
|
|
|
|
|
{
|
|
|
|
|
if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1])
|
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Mark the signature of the given UID which are used to certify it.
|
|
|
|
|
* To do this, we first revmove all signatures which are not valid and
|
|
|
|
|
* from the remain ones we look for the latest one. If this is not a
|
|
|
|
|
* certification revocation signature we mark the signature by setting
|
|
|
|
|
* node flag bit 8. Note that flag bits 9 and 10 are used for internal
|
|
|
|
|
* purposes.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode,
|
|
|
|
|
u32 *main_kid, struct key_item *klist,
|
|
|
|
|
u32 curtime, u32 *next_expire)
|
|
|
|
|
{
|
|
|
|
|
KBNODE node;
|
|
|
|
|
PKT_signature *sig;
|
|
|
|
|
|
|
|
|
|
/* first check all signatures */
|
|
|
|
|
for (node=uidnode->next; node; node = node->next)
|
|
|
|
|
{
|
|
|
|
|
node->flag &= ~(1<<8 | 1<<9 | 1<<10);
|
|
|
|
|
if (node->pkt->pkttype == PKT_USER_ID
|
|
|
|
|
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
|
|
|
|
break; /* ready */
|
|
|
|
|
if (node->pkt->pkttype != PKT_SIGNATURE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
sig = node->pkt->pkt.signature;
|
|
|
|
|
if (sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1])
|
|
|
|
|
continue; /* ignore self-signatures */
|
|
|
|
|
if (!IS_UID_SIG(sig) && !IS_UID_REV(sig))
|
|
|
|
|
continue; /* we only look at these signature classes */
|
|
|
|
|
if (!is_in_klist (klist, sig))
|
|
|
|
|
continue; /* no need to check it then */
|
|
|
|
|
if (check_key_signature (keyblock, node, NULL))
|
|
|
|
|
continue; /* ignore invalid signatures */
|
|
|
|
|
node->flag |= 1<<9;
|
|
|
|
|
}
|
|
|
|
|
/* reset the remaining flags */
|
|
|
|
|
for (; node; node = node->next)
|
|
|
|
|
node->flag &= ~(1<<8 | 1<<9 | 1 << 10);
|
|
|
|
|
|
|
|
|
|
/* kbnode flag usage: bit 9 is here set for signatures to consider,
|
|
|
|
|
* bit 10 will be set by the loop to keep track of keyIDs already
|
|
|
|
|
* processed, bit 8 will be set for the usable signatures */
|
|
|
|
|
|
|
|
|
|
/* for each cert figure out the latest valid one */
|
|
|
|
|
for (node=uidnode->next; node; node = node->next)
|
|
|
|
|
{
|
|
|
|
|
KBNODE n, signode;
|
|
|
|
|
u32 kid[2];
|
|
|
|
|
u32 sigdate;
|
|
|
|
|
|
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
|
|
|
|
break;
|
|
|
|
|
if ( !(node->flag & (1<<9)) )
|
|
|
|
|
continue; /* not a node to look at */
|
|
|
|
|
if ( (node->flag & (1<<10)) )
|
|
|
|
|
continue; /* signature with a keyID already processed */
|
|
|
|
|
node->flag |= (1<<10); /* mark this node as processed */
|
|
|
|
|
sig = node->pkt->pkt.signature;
|
|
|
|
|
signode = node;
|
|
|
|
|
sigdate = sig->timestamp;
|
|
|
|
|
kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1];
|
|
|
|
|
for (n=uidnode->next; n; n = n->next)
|
|
|
|
|
{
|
|
|
|
|
if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
|
|
|
|
break;
|
|
|
|
|
if ( !(n->flag & (1<<9)) )
|
|
|
|
|
continue;
|
|
|
|
|
if ( (n->flag & (1<<10)) )
|
|
|
|
|
continue; /* shortcut already processed signatures */
|
|
|
|
|
sig = n->pkt->pkt.signature;
|
|
|
|
|
if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1])
|
|
|
|
|
continue;
|
|
|
|
|
n->flag |= (1<<10); /* mark this node as processed */
|
|
|
|
|
|
|
|
|
|
/* If signode is nonrevocable and unexpired and n isn't,
|
|
|
|
|
then take signode (skip). It doesn't matter which is
|
|
|
|
|
older: if signode was older then we don't want to take n
|
|
|
|
|
as signode is nonrevocable. If n was older then we're
|
|
|
|
|
automatically fine. */
|
|
|
|
|
|
|
|
|
|
if(((IS_UID_SIG(signode->pkt->pkt.signature) &&
|
|
|
|
|
!signode->pkt->pkt.signature->flags.revocable &&
|
|
|
|
|
(signode->pkt->pkt.signature->expiredate==0 ||
|
|
|
|
|
signode->pkt->pkt.signature->expiredate>curtime))) &&
|
|
|
|
|
(!(IS_UID_SIG(n->pkt->pkt.signature) &&
|
|
|
|
|
!n->pkt->pkt.signature->flags.revocable &&
|
|
|
|
|
(n->pkt->pkt.signature->expiredate==0 ||
|
|
|
|
|
n->pkt->pkt.signature->expiredate>curtime))))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* If n is nonrevocable and unexpired and signode isn't,
|
|
|
|
|
then take n. Again, it doesn't matter which is older: if
|
|
|
|
|
n was older then we don't want to take signode as n is
|
|
|
|
|
nonrevocable. If signode was older then we're
|
|
|
|
|
automatically fine. */
|
|
|
|
|
|
|
|
|
|
if((!(IS_UID_SIG(signode->pkt->pkt.signature) &&
|
|
|
|
|
!signode->pkt->pkt.signature->flags.revocable &&
|
|
|
|
|
(signode->pkt->pkt.signature->expiredate==0 ||
|
|
|
|
|
signode->pkt->pkt.signature->expiredate>curtime))) &&
|
|
|
|
|
((IS_UID_SIG(n->pkt->pkt.signature) &&
|
|
|
|
|
!n->pkt->pkt.signature->flags.revocable &&
|
|
|
|
|
(n->pkt->pkt.signature->expiredate==0 ||
|
|
|
|
|
n->pkt->pkt.signature->expiredate>curtime))))
|
|
|
|
|
{
|
|
|
|
|
signode = n;
|
|
|
|
|
sigdate = sig->timestamp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* At this point, if it's newer, it goes in as the only
|
|
|
|
|
remaining possibilities are signode and n are both either
|
|
|
|
|
revocable or expired or both nonrevocable and unexpired.
|
|
|
|
|
If the timestamps are equal take the later ordered
|
|
|
|
|
packet, presuming that the key packets are hopefully in
|
|
|
|
|
their original order. */
|
|
|
|
|
|
|
|
|
|
if (sig->timestamp >= sigdate)
|
|
|
|
|
{
|
|
|
|
|
signode = n;
|
|
|
|
|
sigdate = sig->timestamp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sig = signode->pkt->pkt.signature;
|
|
|
|
|
if (IS_UID_SIG (sig))
|
|
|
|
|
{ /* this seems to be a usable one which is not revoked.
|
|
|
|
|
* Just need to check whether there is an expiration time,
|
|
|
|
|
* We do the expired certification after finding a suitable
|
|
|
|
|
* certification, the assumption is that a signator does not
|
|
|
|
|
* want that after the expiration of his certificate the
|
|
|
|
|
* system falls back to an older certification which has a
|
|
|
|
|
* different expiration time */
|
|
|
|
|
const byte *p;
|
|
|
|
|
u32 expire;
|
|
|
|
|
|
|
|
|
|
p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL );
|
|
|
|
|
expire = p? sig->timestamp + buffer_to_u32(p) : 0;
|
|
|
|
|
|
|
|
|
|
if (expire==0 || expire > curtime )
|
|
|
|
|
{
|
|
|
|
|
signode->flag |= (1<<8); /* yeah, found a good cert */
|
|
|
|
|
if (expire && expire < *next_expire)
|
|
|
|
|
*next_expire = expire;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Used by validate_one_keyblock to confirm a regexp within a trust
|
|
|
|
|
signature. Returns 1 for match, and 0 for no match or regex
|
|
|
|
|
error. */
|
|
|
|
|
static int
|
|
|
|
|
check_regexp(const char *exp,const char *string)
|
|
|
|
|
{
|
|
|
|
|
#ifdef DISABLE_REGEX
|
|
|
|
|
/* When DISABLE_REGEX is defined, assume all regexps do not
|
|
|
|
|
match. */
|
|
|
|
|
return 0;
|
|
|
|
|
#elif defined(__riscos__)
|
|
|
|
|
return riscos_check_regexp(exp, string, DBG_TRUST);
|
|
|
|
|
#else
|
|
|
|
|
int ret;
|
|
|
|
|
regex_t pat;
|
|
|
|
|
|
|
|
|
|
if(regcomp(&pat,exp,REG_ICASE|REG_NOSUB|REG_EXTENDED)!=0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
ret=regexec(&pat,string,0,NULL,0);
|
|
|
|
|
|
|
|
|
|
regfree(&pat);
|
|
|
|
|
|
|
|
|
|
if(DBG_TRUST)
|
|
|
|
|
log_debug("regexp \"%s\" on \"%s\": %s\n",exp,string,ret==0?"YES":"NO");
|
|
|
|
|
|
|
|
|
|
return (ret==0);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
* 8: Used for internal purposes.
|
|
|
|
|
* 9: Ditto (in mark_usable_uid_certs())
|
|
|
|
|
* 10: Ditto (ditto)
|
|
|
|
|
* This function assumes that all kbnode flags are cleared on entry.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
validate_one_keyblock (KBNODE kb, struct key_item *klist,
|
|
|
|
|
u32 curtime, u32 *next_expire)
|
|
|
|
|
{
|
|
|
|
|
struct key_item *kr;
|
|
|
|
|
KBNODE node, uidnode=NULL;
|
|
|
|
|
PKT_user_id *uid=NULL;
|
|
|
|
|
PKT_public_key *pk = kb->pkt->pkt.public_key;
|
|
|
|
|
u32 main_kid[2];
|
|
|
|
|
int issigned=0, any_signed = 0;
|
|
|
|
|
|
|
|
|
|
keyid_from_pk(pk, main_kid);
|
|
|
|
|
for (node=kb; node; node = node->next)
|
|
|
|
|
{
|
|
|
|
|
/* A bit of discussion here: is it better for the web of trust
|
|
|
|
|
to be built among only self-signed uids? On the one hand, a
|
|
|
|
|
self-signed uid is a statement that the key owner definitely
|
|
|
|
|
intended that uid to be there, but on the other hand, a
|
|
|
|
|
signed (but not self-signed) uid does carry trust, of a sort,
|
|
|
|
|
even if it is a statement being made by people other than the
|
|
|
|
|
key owner "through" the uids on the key owner's key. I'm
|
|
|
|
|
going with the latter. -dshaw */
|
|
|
|
|
|
|
|
|
|
/* && node->pkt->pkt.user_id->created) */
|
|
|
|
|
if (node->pkt->pkttype == PKT_USER_ID)
|
|
|
|
|
{
|
|
|
|
|
if (uidnode && issigned)
|
|
|
|
|
{
|
|
|
|
|
if (uid->help_full_count >= opt.completes_needed
|
|
|
|
|
|| uid->help_marginal_count >= opt.marginals_needed )
|
|
|
|
|
uidnode->flag |= 4;
|
|
|
|
|
else if (uid->help_full_count || uid->help_marginal_count)
|
|
|
|
|
uidnode->flag |= 2;
|
|
|
|
|
uidnode->flag |= 1;
|
|
|
|
|
any_signed = 1;
|
|
|
|
|
}
|
|
|
|
|
uidnode = node;
|
|
|
|
|
uid=uidnode->pkt->pkt.user_id;
|
|
|
|
|
#if 0
|
|
|
|
|
/* If the selfsig is going to expire... This is disabled as
|
|
|
|
|
we do count un-self-signed uids in the web of trust. */
|
|
|
|
|
if(uid->expiredate && uid->expiredate<*next_expire)
|
|
|
|
|
*next_expire = uid->expiredate;
|
|
|
|
|
#endif
|
|
|
|
|
issigned = 0;
|
|
|
|
|
get_validity_counts(pk,uid);
|
|
|
|
|
mark_usable_uid_certs (kb, uidnode, main_kid, klist,
|
|
|
|
|
curtime, next_expire);
|
|
|
|
|
}
|
|
|
|
|
else if (node->pkt->pkttype == PKT_SIGNATURE
|
|
|
|
|
&& (node->flag & (1<<8)) && uid)
|
|
|
|
|
{
|
|
|
|
|
/* Note that we are only seeing unrevoked sigs here */
|
|
|
|
|
PKT_signature *sig = node->pkt->pkt.signature;
|
|
|
|
|
|
|
|
|
|
kr = is_in_klist (klist, sig);
|
|
|
|
|
/* If the trust_regexp does not match, it's as if the sig
|
|
|
|
|
did not exist. This is safe for non-trust sigs as well
|
|
|
|
|
since we don't accept a regexp on the sig unless it's a
|
|
|
|
|
trust sig. */
|
|
|
|
|
if (kr && (kr->trust_regexp==NULL || opt.trust_model!=TM_PGP ||
|
|
|
|
|
(uidnode && check_regexp(kr->trust_regexp,
|
|
|
|
|
uidnode->pkt->pkt.user_id->name))))
|
|
|
|
|
{
|
|
|
|
|
if(DBG_TRUST && opt.trust_model==TM_PGP && sig->trust_depth)
|
|
|
|
|
log_debug("trust sig on %s, sig depth is %d, kr depth is %d\n",
|
|
|
|
|
uidnode->pkt->pkt.user_id->name,sig->trust_depth,
|
|
|
|
|
kr->trust_depth);
|
|
|
|
|
|
|
|
|
|
/* Are we part of a trust sig chain? We always favor
|
|
|
|
|
the latest trust sig, rather than the greater or
|
|
|
|
|
lesser trust sig or value. I could make a decent
|
|
|
|
|
argument for any of these cases, but this seems to be
|
|
|
|
|
what PGP does, and I'd like to be compatible. -dms */
|
|
|
|
|
if(opt.trust_model==TM_PGP && sig->trust_depth
|
|
|
|
|
&& pk->trust_timestamp<=sig->timestamp
|
|
|
|
|
&& (sig->trust_depth<=kr->trust_depth
|
|
|
|
|
|| kr->ownertrust==TRUST_ULTIMATE))
|
|
|
|
|
{
|
|
|
|
|
/* If we got here, we know that:
|
|
|
|
|
|
|
|
|
|
this is a trust sig.
|
|
|
|
|
|
|
|
|
|
it's a newer trust sig than any previous trust
|
|
|
|
|
sig on this key (not uid).
|
|
|
|
|
|
|
|
|
|
it is legal in that it was either generated by an
|
|
|
|
|
ultimate key, or a key that was part of a trust
|
|
|
|
|
chain, and the depth does not violate the
|
|
|
|
|
original trust sig.
|
|
|
|
|
|
|
|
|
|
if there is a regexp attached, it matched
|
|
|
|
|
successfully.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if(DBG_TRUST)
|
|
|
|
|
log_debug("replacing trust value %d with %d and "
|
|
|
|
|
"depth %d with %d\n",
|
|
|
|
|
pk->trust_value,sig->trust_value,
|
|
|
|
|
pk->trust_depth,sig->trust_depth);
|
|
|
|
|
|
|
|
|
|
pk->trust_value=sig->trust_value;
|
|
|
|
|
pk->trust_depth=sig->trust_depth-1;
|
|
|
|
|
|
|
|
|
|
/* If the trust sig contains a regexp, record it
|
|
|
|
|
on the pk for the next round. */
|
|
|
|
|
if(sig->trust_regexp)
|
|
|
|
|
pk->trust_regexp=sig->trust_regexp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kr->ownertrust == TRUST_ULTIMATE)
|
|
|
|
|
uid->help_full_count = opt.completes_needed;
|
|
|
|
|
else if (kr->ownertrust == TRUST_FULLY)
|
|
|
|
|
uid->help_full_count++;
|
|
|
|
|
else if (kr->ownertrust == TRUST_MARGINAL)
|
|
|
|
|
uid->help_marginal_count++;
|
|
|
|
|
issigned = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (uidnode && issigned)
|
|
|
|
|
{
|
|
|
|
|
if (uid->help_full_count >= opt.completes_needed
|
|
|
|
|
|| uid->help_marginal_count >= opt.marginals_needed )
|
|
|
|
|
uidnode->flag |= 4;
|
|
|
|
|
else if (uid->help_full_count || uid->help_marginal_count)
|
|
|
|
|
uidnode->flag |= 2;
|
|
|
|
|
uidnode->flag |= 1;
|
|
|
|
|
any_signed = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return any_signed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 suitable keys from
|
|
|
|
|
* kllist. The caller has to pass 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 *
|
|
|
|
|
validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust,
|
|
|
|
|
struct key_item *klist, u32 curtime, u32 *next_expire)
|
|
|
|
|
{
|
|
|
|
|
KBNODE keyblock = NULL;
|
|
|
|
|
struct key_array *keys = NULL;
|
|
|
|
|
size_t nkeys, maxkeys;
|
|
|
|
|
int rc;
|
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
|
|
|
|
|
|
|
|
|
maxkeys = 1000;
|
2003-06-18 21:56:13 +02:00
|
|
|
|
keys = xmalloc ((maxkeys+1) * sizeof *keys);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
nkeys = 0;
|
|
|
|
|
|
|
|
|
|
rc = keydb_search_reset (hd);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc));
|
|
|
|
|
xfree (keys);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset (&desc, 0, sizeof desc);
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_FIRST;
|
|
|
|
|
desc.skipfnc = search_skipfnc;
|
|
|
|
|
desc.skipfncvalue = full_trust;
|
|
|
|
|
rc = keydb_search (hd, &desc, 1);
|
|
|
|
|
if (rc == -1)
|
|
|
|
|
{
|
|
|
|
|
keys[nkeys].keyblock = NULL;
|
|
|
|
|
return keys;
|
|
|
|
|
}
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc));
|
|
|
|
|
xfree (keys);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
PKT_public_key *pk;
|
|
|
|
|
|
|
|
|
|
rc = keydb_get_keyblock (hd, &keyblock);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
|
|
|
|
|
xfree (keys);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
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;
|
|
|
|
|
if (pk->has_expired || pk->is_revoked)
|
|
|
|
|
{
|
|
|
|
|
/* it does not make sense to look further at those keys */
|
|
|
|
|
mark_keyblock_seen (full_trust, keyblock);
|
|
|
|
|
}
|
|
|
|
|
else if (validate_one_keyblock (keyblock, klist, curtime, next_expire))
|
|
|
|
|
{
|
|
|
|
|
KBNODE node;
|
|
|
|
|
|
|
|
|
|
if (pk->expiredate && pk->expiredate >= curtime
|
|
|
|
|
&& pk->expiredate < *next_expire)
|
|
|
|
|
*next_expire = pk->expiredate;
|
|
|
|
|
|
|
|
|
|
if (nkeys == maxkeys) {
|
|
|
|
|
maxkeys += 1000;
|
2003-06-18 21:56:13 +02:00
|
|
|
|
keys = xrealloc (keys, (maxkeys+1) * sizeof *keys);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
keys[nkeys++].keyblock = keyblock;
|
|
|
|
|
|
|
|
|
|
/* Optimization - if all uids are fully trusted, then we
|
|
|
|
|
never need to consider this key as a candidate again. */
|
|
|
|
|
|
|
|
|
|
for (node=keyblock; node; node = node->next)
|
|
|
|
|
if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & 4))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if(node==NULL)
|
|
|
|
|
mark_keyblock_seen (full_trust, keyblock);
|
|
|
|
|
|
|
|
|
|
keyblock = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
release_kbnode (keyblock);
|
|
|
|
|
keyblock = NULL;
|
|
|
|
|
}
|
|
|
|
|
while ( !(rc = keydb_search (hd, &desc, 1)) );
|
|
|
|
|
if (rc && rc != -1)
|
|
|
|
|
{
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
|
|
|
|
|
xfree (keys);
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keys[nkeys].keyblock = NULL;
|
|
|
|
|
return keys;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Caller must sync */
|
|
|
|
|
static void
|
|
|
|
|
reset_trust_records (KEYDB_HANDLE hd, KeyHashTable exclude)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
KBNODE keyblock = NULL;
|
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
|
|
|
|
int count = 0, nreset = 0;
|
|
|
|
|
|
|
|
|
|
rc = keydb_search_reset (hd);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc));
|
2003-06-05 09:14:21 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset (&desc, 0, sizeof desc);
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_FIRST;
|
|
|
|
|
if(exclude)
|
|
|
|
|
{
|
|
|
|
|
desc.skipfnc = search_skipfnc;
|
|
|
|
|
desc.skipfncvalue = exclude;
|
|
|
|
|
}
|
|
|
|
|
rc = keydb_search (hd, &desc, 1);
|
|
|
|
|
if (rc && rc != -1 )
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc));
|
2003-06-05 09:14:21 +02:00
|
|
|
|
else if (!rc)
|
|
|
|
|
{
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
rc = keydb_get_keyblock (hd, &keyblock);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
|
2003-06-05 09:14:21 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
|
|
if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) /* paranoid assertion*/
|
|
|
|
|
{
|
|
|
|
|
nreset += clear_validity (keyblock->pkt->pkt.public_key);
|
|
|
|
|
release_kbnode (keyblock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while ( !(rc = keydb_search (hd, &desc, 1)) );
|
|
|
|
|
if (rc && rc != -1)
|
2003-06-18 21:56:13 +02:00
|
|
|
|
log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
|
2003-06-05 09:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info (_("%d keys processed (%d validity counts cleared)\n"),
|
|
|
|
|
count, nreset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Run the key validation procedure.
|
|
|
|
|
*
|
|
|
|
|
* This works this way:
|
|
|
|
|
* Step 1: Find all ultimately trusted keys (UTK).
|
|
|
|
|
* mark them all as seen and put them into klist.
|
|
|
|
|
* Step 2: loop max_cert_times
|
|
|
|
|
* Step 3: if OWNERTRUST of any key in klist is undefined
|
|
|
|
|
* ask user to assign ownertrust
|
|
|
|
|
* Step 4: Loop over all keys in the keyDB which are not marked seen
|
|
|
|
|
* Step 5: if key is revoked or expired
|
|
|
|
|
* mark key as seen
|
|
|
|
|
* continue loop at Step 4
|
|
|
|
|
* Step 6: For each user ID of that key signed by a key in klist
|
|
|
|
|
* Calculate validity by counting trusted signatures.
|
|
|
|
|
* Set validity of user ID
|
|
|
|
|
* Step 7: If any signed user ID was found
|
|
|
|
|
* mark key as seen
|
|
|
|
|
* End Loop
|
|
|
|
|
* Step 8: Build a new klist from all fully trusted keys from step 6
|
|
|
|
|
* End Loop
|
|
|
|
|
* Ready
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
validate_keys (int interactive)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
int quit=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, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate;
|
|
|
|
|
KeyHashTable stored,used,full_trust;
|
|
|
|
|
u32 start_time, next_expire;
|
|
|
|
|
|
|
|
|
|
start_time = make_timestamp ();
|
|
|
|
|
next_expire = 0xffffffff; /* set next expire to the year 2106 */
|
|
|
|
|
stored = new_key_hash_table ();
|
|
|
|
|
used = new_key_hash_table ();
|
|
|
|
|
full_trust = new_key_hash_table ();
|
|
|
|
|
/* Fixme: Instead of always building a UTK list, we could just build it
|
|
|
|
|
* here when needed */
|
|
|
|
|
if (!utk_list)
|
|
|
|
|
{
|
|
|
|
|
log_info (_("no ultimately trusted keys found\n"));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kdb = keydb_new (0);
|
|
|
|
|
|
|
|
|
|
reset_trust_records (kdb,NULL);
|
|
|
|
|
|
|
|
|
|
/* mark all UTKs as used and fully_trusted and set validity to
|
|
|
|
|
ultimate */
|
|
|
|
|
for (k=utk_list; k; k = k->next)
|
|
|
|
|
{
|
|
|
|
|
KBNODE keyblock;
|
|
|
|
|
PKT_public_key *pk;
|
|
|
|
|
|
|
|
|
|
keyblock = get_pubkeyblock (k->kid);
|
|
|
|
|
if (!keyblock)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("public key of ultimately"
|
|
|
|
|
" trusted key %08lX not found\n"), (ulong)k->kid[1]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
mark_keyblock_seen (used, keyblock);
|
|
|
|
|
mark_keyblock_seen (stored, keyblock);
|
|
|
|
|
mark_keyblock_seen (full_trust, keyblock);
|
|
|
|
|
pk = keyblock->pkt->pkt.public_key;
|
|
|
|
|
for (node=keyblock; node; node = node->next)
|
|
|
|
|
{
|
|
|
|
|
if (node->pkt->pkttype == PKT_USER_ID)
|
|
|
|
|
update_validity (pk, node->pkt->pkt.user_id, 0, TRUST_ULTIMATE);
|
|
|
|
|
}
|
|
|
|
|
if ( pk->expiredate && pk->expiredate >= start_time
|
|
|
|
|
&& pk->expiredate < next_expire)
|
|
|
|
|
next_expire = pk->expiredate;
|
|
|
|
|
|
|
|
|
|
release_kbnode (keyblock);
|
|
|
|
|
do_sync ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
klist = utk_list;
|
|
|
|
|
|
|
|
|
|
log_info(_("%d marginal(s) needed, %d complete(s) needed, %s trust model\n"),
|
|
|
|
|
opt.marginals_needed,opt.completes_needed,trust_model_string());
|
|
|
|
|
|
|
|
|
|
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_never = 0;
|
|
|
|
|
ot_marginal = ot_full = ot_ultimate = 0;
|
|
|
|
|
for (k=klist; k; k = k->next)
|
|
|
|
|
{
|
|
|
|
|
int min=0;
|
|
|
|
|
|
|
|
|
|
/* 120 and 60 are as per RFC2440 */
|
|
|
|
|
if(k->trust_value>=120)
|
|
|
|
|
min=TRUST_FULLY;
|
|
|
|
|
else if(k->trust_value>=60)
|
|
|
|
|
min=TRUST_MARGINAL;
|
|
|
|
|
|
|
|
|
|
if(min!=k->min_ownertrust)
|
|
|
|
|
update_min_ownertrust(k->kid,min);
|
|
|
|
|
|
|
|
|
|
if (interactive && k->ownertrust == TRUST_UNKNOWN)
|
|
|
|
|
{
|
|
|
|
|
k->ownertrust = ask_ownertrust (k->kid,min);
|
|
|
|
|
|
|
|
|
|
if (k->ownertrust == -1)
|
|
|
|
|
{
|
|
|
|
|
quit=1;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This can happen during transition from an old trustdb
|
|
|
|
|
before trust sigs. It can also happen if a user uses two
|
|
|
|
|
different versions of GnuPG or changes the --trust-model
|
|
|
|
|
setting. */
|
|
|
|
|
if(k->ownertrust<min)
|
|
|
|
|
{
|
|
|
|
|
if(DBG_TRUST)
|
|
|
|
|
log_debug("key %08lX: "
|
|
|
|
|
"overriding ownertrust \"%s\" with \"%s\"\n",
|
|
|
|
|
(ulong)k->kid[1],
|
|
|
|
|
trust_value_to_string(k->ownertrust),
|
|
|
|
|
trust_value_to_string(min));
|
|
|
|
|
|
|
|
|
|
k->ownertrust=min;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (k->ownertrust == TRUST_UNKNOWN)
|
|
|
|
|
ot_unknown++;
|
|
|
|
|
else if (k->ownertrust == TRUST_UNDEFINED)
|
|
|
|
|
ot_undefined++;
|
|
|
|
|
else if (k->ownertrust == TRUST_NEVER)
|
|
|
|
|
ot_never++;
|
|
|
|
|
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 = validate_key_list (kdb, full_trust, klist,
|
|
|
|
|
start_time, &next_expire);
|
|
|
|
|
if (!keys)
|
|
|
|
|
{
|
|
|
|
|
log_error ("validate_key_list failed\n");
|
2003-06-18 21:56:13 +02:00
|
|
|
|
rc = GPG_ERR_GENERAL;
|
2003-06-05 09:14:21 +02:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
for (kar=keys; kar->keyblock; kar++)
|
|
|
|
|
store_validation_status (depth, kar->keyblock, stored);
|
|
|
|
|
|
|
|
|
|
log_info (_("checking at depth %d valid=%d"
|
|
|
|
|
" ot(-/q/n/m/f/u)=%d/%d/%d/%d/%d/%d\n"),
|
|
|
|
|
depth, key_count, ot_unknown, ot_undefined,
|
|
|
|
|
ot_never, ot_marginal, ot_full, ot_ultimate );
|
|
|
|
|
|
|
|
|
|
/* 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))
|
|
|
|
|
{
|
|
|
|
|
u32 kid[2];
|
|
|
|
|
|
|
|
|
|
/* have we used this key already? */
|
|
|
|
|
keyid_from_pk (kar->keyblock->pkt->pkt.public_key, kid);
|
|
|
|
|
if(test_key_hash_table(used,kid)==0)
|
|
|
|
|
{
|
|
|
|
|
/* Normally we add both the primary and subkey
|
|
|
|
|
ids to the hash via mark_keyblock_seen, but
|
|
|
|
|
since we aren't using this hash as a skipfnc,
|
|
|
|
|
that doesn't matter here. */
|
|
|
|
|
add_key_hash_table (used,kid);
|
|
|
|
|
k = new_key_item ();
|
|
|
|
|
k->kid[0]=kid[0];
|
|
|
|
|
k->kid[1]=kid[1];
|
|
|
|
|
k->ownertrust =
|
|
|
|
|
(get_ownertrust (kar->keyblock->pkt->pkt.public_key)
|
|
|
|
|
& TRUST_MASK);
|
|
|
|
|
k->min_ownertrust =
|
|
|
|
|
get_min_ownertrust(kar->keyblock->pkt->pkt.public_key);
|
|
|
|
|
k->trust_depth=
|
|
|
|
|
kar->keyblock->pkt->pkt.public_key->trust_depth;
|
|
|
|
|
k->trust_value=
|
|
|
|
|
kar->keyblock->pkt->pkt.public_key->trust_value;
|
|
|
|
|
if(kar->keyblock->pkt->pkt.public_key->trust_regexp)
|
|
|
|
|
k->trust_regexp=
|
2003-06-18 21:56:13 +02:00
|
|
|
|
xstrdup (kar->keyblock->pkt->
|
2003-06-05 09:14:21 +02:00
|
|
|
|
pkt.public_key->trust_regexp);
|
|
|
|
|
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 (full_trust);
|
|
|
|
|
release_key_hash_table (used);
|
|
|
|
|
release_key_hash_table (stored);
|
|
|
|
|
if (!rc && !quit) /* mark trustDB as checked */
|
|
|
|
|
{
|
|
|
|
|
if (next_expire == 0xffffffff || next_expire < start_time )
|
|
|
|
|
tdbio_write_nextcheck (0);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tdbio_write_nextcheck (next_expire);
|
|
|
|
|
log_info (_("next trustdb check due at %s\n"),
|
|
|
|
|
strtimestamp (next_expire));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(tdbio_update_version_record()!=0)
|
|
|
|
|
{
|
|
|
|
|
log_error(_("unable to update trustdb version record: "
|
2003-06-18 21:56:13 +02:00
|
|
|
|
"write failed: %s\n"), gpg_strerror (rc));
|
2003-06-05 09:14:21 +02:00
|
|
|
|
tdbio_invalid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do_sync ();
|
|
|
|
|
pending_check_trustdb = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|