mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
2053 lines
52 KiB
C
2053 lines
52 KiB
C
/* trustdb.c
|
|
* Copyright (C) 1998 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 <errno.h>
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "errors.h"
|
|
#include "iobuf.h"
|
|
#include "keydb.h"
|
|
#include "memory.h"
|
|
#include "util.h"
|
|
#include "trustdb.h"
|
|
#include "options.h"
|
|
#include "packet.h"
|
|
#include "main.h"
|
|
#include "i18n.h"
|
|
|
|
|
|
#define TRUST_RECORD_LEN 40
|
|
#define SIGS_PER_RECORD ((TRUST_RECORD_LEN-10)/5)
|
|
#define ITEMS_PER_HTBL_RECORD ((TRUST_RECORD_LEN-2)/4)
|
|
#define ITEMS_PER_HLST_RECORD ((TRUST_RECORD_LEN-6)/5)
|
|
#define MAX_LIST_SIGS_DEPTH 20
|
|
|
|
|
|
#define RECTYPE_VER 1
|
|
#define RECTYPE_DIR 2
|
|
#define RECTYPE_KEY 3
|
|
#define RECTYPE_CTL 4
|
|
#define RECTYPE_SIG 5
|
|
#define RECTYPE_HTBL 6
|
|
#define RECTYPE_HLST 7
|
|
|
|
|
|
struct trust_record {
|
|
int rectype;
|
|
union {
|
|
struct { /* version record: */
|
|
byte version; /* should be 1 */
|
|
ulong locked; /* pid of process which holds a lock */
|
|
ulong created; /* timestamp of trustdb creation */
|
|
ulong modified; /* timestamp of last modification */
|
|
ulong validated; /* timestamp of last validation */
|
|
byte marginals_needed;
|
|
byte completes_needed;
|
|
byte max_cert_depth;
|
|
} ver;
|
|
struct { /* directory record */
|
|
ulong local_id;
|
|
u32 keyid[2];
|
|
ulong keyrec; /* recno of public key record */
|
|
ulong ctlrec; /* recno of control record */
|
|
ulong sigrec; /* recno of first signature record */
|
|
ulong link; /* to next dir record */
|
|
byte no_sigs; /* does not have sigature and checked */
|
|
} dir;
|
|
struct { /* public key record */
|
|
ulong owner;
|
|
u32 keyid[2];
|
|
byte pubkey_algo;
|
|
byte fingerprint[20];
|
|
byte ownertrust;
|
|
} key;
|
|
struct { /* control record */
|
|
ulong owner;
|
|
byte blockhash[20];
|
|
byte trustlevel; /* calculated trustlevel */
|
|
} ctl;
|
|
struct { /* signature record */
|
|
ulong owner; /* local_id of record owner (pubkey record) */
|
|
ulong chain; /* offset of next record or NULL for last one */
|
|
struct {
|
|
ulong local_id; /* of pubkey record of signator (0=unused) */
|
|
byte flag; /* reserved */
|
|
} sig[SIGS_PER_RECORD];
|
|
} sig;
|
|
struct {
|
|
ulong item[ITEMS_PER_HTBL_RECORD];
|
|
} htbl;
|
|
struct {
|
|
ulong chain;
|
|
struct {
|
|
byte hash;
|
|
ulong rnum;
|
|
} item[ITEMS_PER_HLST_RECORD];
|
|
} hlst;
|
|
} r;
|
|
};
|
|
typedef struct trust_record TRUSTREC;
|
|
|
|
typedef struct {
|
|
ulong local_id; /* localid of the pubkey */
|
|
ulong sigrec;
|
|
ulong sig_id; /* returned signature id */
|
|
unsigned sig_flag; /* returned signature record flag */
|
|
struct { /* internal data */
|
|
int init_done;
|
|
int eof;
|
|
TRUSTREC rec;
|
|
int index;
|
|
} ctl;
|
|
} SIGREC_CONTEXT;
|
|
|
|
typedef struct local_id_info *LOCAL_ID_INFO;
|
|
struct local_id_info {
|
|
LOCAL_ID_INFO next;
|
|
ulong lid;
|
|
unsigned flag;
|
|
};
|
|
|
|
|
|
typedef struct trust_info TRUST_INFO;
|
|
struct trust_info {
|
|
ulong lid;
|
|
unsigned trust;
|
|
};
|
|
|
|
|
|
typedef struct trust_seg_list *TRUST_SEG_LIST;
|
|
struct trust_seg_list {
|
|
TRUST_SEG_LIST next;
|
|
int nseg; /* number of segmens */
|
|
int dup;
|
|
TRUST_INFO seg[1]; /* segment list */
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
TRUST_SEG_LIST tsl;
|
|
int index;
|
|
} ENUM_TRUST_WEB_CONTEXT;
|
|
|
|
|
|
static void create_db( const char *fname );
|
|
static void open_db(void);
|
|
static void dump_record( ulong rnum, TRUSTREC *rec, FILE *fp );
|
|
static int read_record( ulong recnum, TRUSTREC *rec, int expected );
|
|
static int write_record( ulong recnum, TRUSTREC *rec );
|
|
static ulong new_recnum(void);
|
|
static int search_record( PKT_public_cert *pkc, TRUSTREC *rec );
|
|
static int walk_sigrecs( SIGREC_CONTEXT *c, int create );
|
|
|
|
static LOCAL_ID_INFO *new_lid_table(void);
|
|
static void release_lid_table( LOCAL_ID_INFO *tbl );
|
|
static int ins_lid_table_item( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag );
|
|
static int qry_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned *flag );
|
|
static void upd_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag );
|
|
|
|
static void print_user_id( const char *text, u32 *keyid );
|
|
static int do_list_path( TRUST_INFO *stack, int depth, int max_depth,
|
|
LOCAL_ID_INFO *lids, TRUST_SEG_LIST *tslist );
|
|
|
|
static int list_sigs( ulong pubkey_id );
|
|
static int build_sigrecs( ulong pubkeyid );
|
|
static int propagate_trust( TRUST_SEG_LIST tslist );
|
|
static int do_check( ulong pubkeyid, TRUSTREC *drec, unsigned *trustlevel );
|
|
|
|
static int update_no_sigs( ulong lid, int no_sigs );
|
|
|
|
static char *db_name;
|
|
static int db_fd = -1;
|
|
/* a table used to keep track of ultimately trusted keys
|
|
* which are the ones from our secrings */
|
|
static LOCAL_ID_INFO *ultikey_table;
|
|
|
|
static ulong last_trust_web_key;
|
|
static TRUST_SEG_LIST last_trust_web_tslist;
|
|
|
|
#define buftoulong( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \
|
|
(*((byte*)(p)+2) << 8) | (*((byte*)(p)+3)))
|
|
#define buftoushort( p ) ((*((byte*)(p)) << 8) | (*((byte*)(p)+1)))
|
|
#define ulongtobuf( p, a ) do { \
|
|
((byte*)p)[0] = a >> 24; \
|
|
((byte*)p)[1] = a >> 16; \
|
|
((byte*)p)[2] = a >> 8; \
|
|
((byte*)p)[3] = a ; \
|
|
} while(0)
|
|
#define ushorttobuf( p, a ) do { \
|
|
((byte*)p)[0] = a >> 8; \
|
|
((byte*)p)[1] = a ; \
|
|
} while(0)
|
|
#define buftou32( p) buftoulong( (p) )
|
|
#define u32tobuf( p, a) ulongtobuf( (p), (a) )
|
|
|
|
|
|
/**************************************************
|
|
************** read and write helpers ************
|
|
**************************************************/
|
|
|
|
static void
|
|
fwrite_8(FILE *fp, byte a)
|
|
{
|
|
if( putc( a & 0xff, fp ) == EOF )
|
|
log_fatal("error writing byte to trustdb: %s\n", strerror(errno) );
|
|
}
|
|
|
|
|
|
static void
|
|
fwrite_32( FILE*fp, ulong a)
|
|
{
|
|
putc( (a>>24) & 0xff, fp );
|
|
putc( (a>>16) & 0xff, fp );
|
|
putc( (a>> 8) & 0xff, fp );
|
|
if( putc( a & 0xff, fp ) == EOF )
|
|
log_fatal("error writing ulong to trustdb: %s\n", strerror(errno) );
|
|
}
|
|
|
|
static void
|
|
fwrite_zeros( FILE *fp, size_t n)
|
|
{
|
|
while( n-- )
|
|
if( putc( 0, fp ) == EOF )
|
|
log_fatal("error writing zeros to trustdb: %s\n", strerror(errno) );
|
|
}
|
|
|
|
|
|
/**********************************************
|
|
************* list helpers *******************
|
|
**********************************************/
|
|
|
|
static LOCAL_ID_INFO *
|
|
new_lid_table(void)
|
|
{
|
|
return m_alloc_clear( 16 * sizeof(LOCAL_ID_INFO));
|
|
}
|
|
|
|
static void
|
|
release_lid_table( LOCAL_ID_INFO *tbl )
|
|
{
|
|
LOCAL_ID_INFO a, a2;
|
|
int i;
|
|
|
|
for(i=0; i < 16; i++ ) {
|
|
for(a=tbl[i]; a; a = a2 ) {
|
|
a2 = a->next;
|
|
m_free(a);
|
|
}
|
|
}
|
|
m_free(tbl);
|
|
}
|
|
|
|
/****************
|
|
* Add a new item to the table or return 1 if we already have this item
|
|
* fixme: maybe it's a good idea to take items from an unused item list.
|
|
*/
|
|
static int
|
|
ins_lid_table_item( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag )
|
|
{
|
|
LOCAL_ID_INFO a;
|
|
|
|
for( a = tbl[lid & 0x0f]; a; a = a->next )
|
|
if( a->lid == lid )
|
|
return 1;
|
|
a = m_alloc( sizeof *a );
|
|
a->lid = lid;
|
|
a->flag = flag;
|
|
a->next = tbl[lid & 0x0f];
|
|
tbl[lid & 0x0f] = a;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
qry_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned *flag )
|
|
{
|
|
LOCAL_ID_INFO a;
|
|
|
|
for( a = tbl[lid & 0x0f]; a; a = a->next )
|
|
if( a->lid == lid ) {
|
|
if( flag )
|
|
*flag = a->flag;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
upd_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag )
|
|
{
|
|
LOCAL_ID_INFO a;
|
|
|
|
for( a = tbl[lid & 0x0f]; a; a = a->next )
|
|
if( a->lid == lid ) {
|
|
a->flag = flag;
|
|
return;
|
|
}
|
|
BUG();
|
|
}
|
|
|
|
|
|
/**************************************************
|
|
************** read and write stuff **************
|
|
**************************************************/
|
|
|
|
|
|
/****************
|
|
* Create a new trustdb
|
|
*/
|
|
static void
|
|
create_db( const char *fname )
|
|
{
|
|
FILE *fp;
|
|
|
|
fp =fopen( fname, "w" );
|
|
if( !fp )
|
|
log_fatal(_("can't create %s: %s\n"), fname, strerror(errno) );
|
|
fwrite_8( fp, 1 );
|
|
fwrite_8( fp, 'g' );
|
|
fwrite_8( fp, 'p' );
|
|
fwrite_8( fp, 'g' );
|
|
fwrite_8( fp, 1 ); /* version */
|
|
fwrite_zeros( fp, 3 ); /* reserved */
|
|
fwrite_32( fp, 0 ); /* not locked */
|
|
fwrite_32( fp, make_timestamp() ); /* created */
|
|
fwrite_32( fp, 0 ); /* not yet modified */
|
|
fwrite_32( fp, 0 ); /* not yet validated*/
|
|
fwrite_32( fp, 0 ); /* reserved */
|
|
fwrite_8( fp, 3 ); /* marginals needed */
|
|
fwrite_8( fp, 1 ); /* completes needed */
|
|
fwrite_8( fp, 4 ); /* max_cet_depth */
|
|
fwrite_zeros( fp, 9 ); /* filler */
|
|
fclose(fp);
|
|
}
|
|
|
|
static void
|
|
open_db()
|
|
{
|
|
TRUSTREC rec;
|
|
assert( db_fd == -1 );
|
|
|
|
db_fd = open( db_name, O_RDWR );
|
|
if( db_fd == -1 )
|
|
log_fatal(_("can't open %s: %s\n"), db_name, strerror(errno) );
|
|
if( read_record( 0, &rec, RECTYPE_VER ) )
|
|
log_fatal(_("TrustDB %s is invalid\n"), db_name );
|
|
/* fixme: check ->locked and other stuff */
|
|
}
|
|
|
|
|
|
static void
|
|
dump_record( ulong rnum, TRUSTREC *rec, FILE *fp )
|
|
{
|
|
int i, any;
|
|
|
|
fprintf(fp, "trust record %lu, type=", rnum );
|
|
|
|
switch( rec->rectype ) {
|
|
case 0: fprintf(fp, "free\n");
|
|
break;
|
|
case RECTYPE_VER: fprintf(fp, "version\n");
|
|
break;
|
|
case RECTYPE_DIR:
|
|
fprintf(fp, "dir keyid=%08lX, key=%lu, ctl=%lu, sig=%lu",
|
|
(ulong)rec->r.dir.keyid[1],
|
|
rec->r.dir.keyrec, rec->r.dir.ctlrec, rec->r.dir.sigrec );
|
|
if( rec->r.dir.no_sigs == 1 )
|
|
fputs(", (none)", fp );
|
|
else if( rec->r.dir.no_sigs == 2 )
|
|
fputs(", (invalid)", fp );
|
|
else if( rec->r.dir.no_sigs == 3 )
|
|
fputs(", (revoked)", fp );
|
|
else if( rec->r.dir.no_sigs )
|
|
fputs(", (??)", fp );
|
|
putc('\n', fp);
|
|
break;
|
|
case RECTYPE_KEY: fprintf(fp, "key keyid=%08lX, own=%lu, ownertrust=%02x\n",
|
|
(ulong)rec->r.key.keyid[1],
|
|
rec->r.key.owner, rec->r.key.ownertrust );
|
|
break;
|
|
case RECTYPE_CTL: fprintf(fp, "ctl\n");
|
|
break;
|
|
case RECTYPE_SIG:
|
|
fprintf(fp, "sigrec, owner=%lu, chain=%lu\n",
|
|
rec->r.sig.owner, rec->r.sig.chain );
|
|
for(i=any=0; i < SIGS_PER_RECORD; i++ ) {
|
|
if( rec->r.sig.sig[i].local_id ) {
|
|
if( !any ) {
|
|
putc('\t', fp);
|
|
any++;
|
|
}
|
|
fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].local_id,
|
|
rec->r.sig.sig[i].flag );
|
|
}
|
|
}
|
|
if( any )
|
|
putc('\n', fp);
|
|
break;
|
|
default:
|
|
fprintf(fp, "%d (unknown)\n", rec->rectype );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/****************
|
|
* read the record with number recnum
|
|
* returns: -1 on error, 0 on success
|
|
*/
|
|
static int
|
|
read_record( ulong recnum, TRUSTREC *rec, int expected )
|
|
{
|
|
byte buf[TRUST_RECORD_LEN], *p;
|
|
int rc = 0;
|
|
int n, i;
|
|
|
|
if( db_fd == -1 )
|
|
open_db();
|
|
if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
|
|
log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
|
|
return G10ERR_READ_FILE;
|
|
}
|
|
n = read( db_fd, buf, TRUST_RECORD_LEN);
|
|
if( !n ) {
|
|
return -1; /* eof */
|
|
}
|
|
else if( n != TRUST_RECORD_LEN ) {
|
|
log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) );
|
|
return G10ERR_READ_FILE;
|
|
}
|
|
p = buf;
|
|
rec->rectype = *p++;
|
|
if( expected && rec->rectype != expected ) {
|
|
log_error("%lu: read expected rec type %d, got %d\n",
|
|
recnum, expected, rec->rectype );
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
p++;
|
|
switch( rec->rectype ) {
|
|
case 0: /* unused record */
|
|
break;
|
|
case RECTYPE_VER: /* version record */
|
|
/* g10 was the original name */
|
|
if( memcmp(buf+1, "gpg", 3 ) && memcmp(buf+1, "g10", 3 ) ) {
|
|
log_error(_("%s: not a trustdb file\n"), db_name );
|
|
rc = G10ERR_TRUSTDB;
|
|
}
|
|
p += 2; /* skip magic */
|
|
rec->r.ver.version = *p++;
|
|
rec->r.ver.locked = buftoulong(p); p += 4;
|
|
rec->r.ver.created = buftoulong(p); p += 4;
|
|
rec->r.ver.modified = buftoulong(p); p += 4;
|
|
rec->r.ver.validated= buftoulong(p); p += 4;
|
|
rec->r.ver.marginals_needed = *p++;
|
|
rec->r.ver.completes_needed = *p++;
|
|
rec->r.ver.max_cert_depth = *p++;
|
|
if( recnum ) {
|
|
log_error("%s: version record with recnum %lu\n",
|
|
db_name, (ulong)recnum );
|
|
rc = G10ERR_TRUSTDB;
|
|
}
|
|
if( rec->r.ver.version != 1 ) {
|
|
log_error("%s: invalid file version %d\n",
|
|
db_name, rec->r.ver.version );
|
|
rc = G10ERR_TRUSTDB;
|
|
}
|
|
break;
|
|
case RECTYPE_DIR: /*directory record */
|
|
rec->r.dir.local_id = buftoulong(p); p += 4;
|
|
rec->r.dir.keyid[0] = buftou32(p); p += 4;
|
|
rec->r.dir.keyid[1] = buftou32(p); p += 4;
|
|
rec->r.dir.keyrec = buftoulong(p); p += 4;
|
|
rec->r.dir.ctlrec = buftoulong(p); p += 4;
|
|
rec->r.dir.sigrec = buftoulong(p); p += 4;
|
|
rec->r.dir.no_sigs = *p++;
|
|
if( rec->r.dir.local_id != recnum ) {
|
|
log_error("%s: dir local_id != recnum (%lu,%lu)\n",
|
|
db_name,
|
|
(ulong)rec->r.dir.local_id,
|
|
(ulong)recnum );
|
|
rc = G10ERR_TRUSTDB;
|
|
}
|
|
break;
|
|
case RECTYPE_KEY: /* public key record */
|
|
rec->r.key.owner = buftoulong(p); p += 4;
|
|
rec->r.dir.keyid[0] = buftou32(p); p += 4;
|
|
rec->r.dir.keyid[1] = buftou32(p); p += 4;
|
|
rec->r.key.pubkey_algo = *p++; p++;
|
|
memcpy( rec->r.key.fingerprint, p, 20); p += 20;
|
|
rec->r.key.ownertrust = *p++;
|
|
break;
|
|
case RECTYPE_CTL: /* control record */
|
|
rec->r.ctl.owner = buftoulong(p); p += 4;
|
|
memcpy(rec->r.ctl.blockhash, p, 20); p += 20;
|
|
rec->r.ctl.trustlevel = *p++;
|
|
break;
|
|
case RECTYPE_SIG:
|
|
rec->r.sig.owner = buftoulong(p); p += 4;
|
|
rec->r.sig.chain = buftoulong(p); p += 4;
|
|
for(i=0; i < SIGS_PER_RECORD; i++ ) {
|
|
rec->r.sig.sig[i].local_id = buftoulong(p); p += 4;
|
|
rec->r.sig.sig[i].flag = *p++;
|
|
}
|
|
break;
|
|
default:
|
|
log_error("%s: invalid record type %d at recnum %lu\n",
|
|
db_name, rec->rectype, (ulong)recnum );
|
|
rc = G10ERR_TRUSTDB;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/****************
|
|
* Write the record at RECNUM
|
|
*/
|
|
static int
|
|
write_record( ulong recnum, TRUSTREC *rec )
|
|
{
|
|
byte buf[TRUST_RECORD_LEN], *p;
|
|
int rc = 0;
|
|
int i, n;
|
|
|
|
if( db_fd == -1 )
|
|
open_db();
|
|
|
|
memset(buf, 0, TRUST_RECORD_LEN);
|
|
p = buf;
|
|
*p++ = rec->rectype; p++;
|
|
switch( rec->rectype ) {
|
|
case 0: /* unused record */
|
|
break;
|
|
case 1: /* version record */
|
|
BUG();
|
|
break;
|
|
|
|
case RECTYPE_DIR: /*directory record */
|
|
ulongtobuf(p, rec->r.dir.local_id); p += 4;
|
|
u32tobuf(p, rec->r.key.keyid[0]); p += 4;
|
|
u32tobuf(p, rec->r.key.keyid[1]); p += 4;
|
|
ulongtobuf(p, rec->r.dir.keyrec); p += 4;
|
|
ulongtobuf(p, rec->r.dir.ctlrec); p += 4;
|
|
ulongtobuf(p, rec->r.dir.sigrec); p += 4;
|
|
*p++ = rec->r.dir.no_sigs;
|
|
assert( rec->r.dir.local_id == recnum );
|
|
break;
|
|
|
|
case RECTYPE_KEY:
|
|
ulongtobuf(p, rec->r.key.owner); p += 4;
|
|
u32tobuf(p, rec->r.key.keyid[0]); p += 4;
|
|
u32tobuf(p, rec->r.key.keyid[1]); p += 4;
|
|
*p++ = rec->r.key.pubkey_algo; p++;
|
|
memcpy( p, rec->r.key.fingerprint, 20); p += 20;
|
|
*p++ = rec->r.key.ownertrust;
|
|
break;
|
|
|
|
case RECTYPE_CTL: /* control record */
|
|
ulongtobuf(p, rec->r.ctl.owner); p += 4;
|
|
memcpy(p, rec->r.ctl.blockhash, 20); p += 20;
|
|
*p++ = rec->r.ctl.trustlevel;
|
|
break;
|
|
|
|
case RECTYPE_SIG:
|
|
ulongtobuf(p, rec->r.sig.owner); p += 4;
|
|
ulongtobuf(p, rec->r.sig.chain); p += 4;
|
|
for(i=0; i < SIGS_PER_RECORD; i++ ) {
|
|
ulongtobuf(p, rec->r.sig.sig[i].local_id); p += 4;
|
|
*p++ = rec->r.sig.sig[i].flag;
|
|
}
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
|
|
log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
|
|
return G10ERR_WRITE_FILE;
|
|
}
|
|
n = write( db_fd, buf, TRUST_RECORD_LEN);
|
|
if( n != TRUST_RECORD_LEN ) {
|
|
log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) );
|
|
return G10ERR_WRITE_FILE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************
|
|
* create a new record and return its record number
|
|
*/
|
|
static ulong
|
|
new_recnum()
|
|
{
|
|
off_t offset;
|
|
ulong recnum;
|
|
TRUSTREC rec;
|
|
int rc;
|
|
|
|
/* fixme: look for unused records */
|
|
offset = lseek( db_fd, 0, SEEK_END );
|
|
if( offset == -1 )
|
|
log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
|
|
recnum = offset / TRUST_RECORD_LEN;
|
|
assert(recnum); /* this is will never be the first record */
|
|
|
|
/* we must write a record, so that the next call to this function
|
|
* returns another recnum */
|
|
memset( &rec, 0, sizeof rec );
|
|
rec.rectype = 0; /* free record */
|
|
rc = write_record(recnum, &rec );
|
|
if( rc )
|
|
log_fatal(_("%s: failed to append a record: %s\n"),
|
|
db_name, g10_errstr(rc));
|
|
return recnum ;
|
|
}
|
|
|
|
/****************
|
|
* Search the trustdb for a key which matches PKC and return the dir record
|
|
* The local_id of PKC is set to the correct value
|
|
*
|
|
* Note: To increase performance, we could use a index search here.
|
|
*/
|
|
static int
|
|
search_record( PKT_public_cert *pkc, TRUSTREC *rec )
|
|
{
|
|
ulong recnum;
|
|
u32 keyid[2];
|
|
byte *fingerprint;
|
|
size_t fingerlen;
|
|
int rc;
|
|
|
|
keyid_from_pkc( pkc, keyid );
|
|
fingerprint = fingerprint_from_pkc( pkc, &fingerlen );
|
|
assert( fingerlen == 20 || fingerlen == 16 );
|
|
|
|
for(recnum=1; !(rc=read_record( recnum, rec, 0)); recnum++ ) {
|
|
if( rec->rectype != RECTYPE_DIR )
|
|
continue;
|
|
if( rec->r.dir.keyid[0] == keyid[0]
|
|
&& rec->r.dir.keyid[1] == keyid[1]){
|
|
TRUSTREC keyrec;
|
|
|
|
if( read_record( rec->r.dir.keyrec, &keyrec, RECTYPE_KEY ) ) {
|
|
log_error("%lu: ooops: invalid key record\n", recnum );
|
|
break;
|
|
}
|
|
if( keyrec.r.key.pubkey_algo == pkc->pubkey_algo
|
|
&& !memcmp(keyrec.r.key.fingerprint, fingerprint, fingerlen) ){
|
|
if( pkc->local_id && pkc->local_id != recnum )
|
|
log_error("%s: found record, but local_id from mem does "
|
|
"not match recnum (%lu,%lu)\n", db_name,
|
|
(ulong)pkc->local_id, (ulong)recnum );
|
|
pkc->local_id = recnum;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
if( rc != -1 )
|
|
log_error(_("%s: search_db failed: %s\n"),db_name, g10_errstr(rc) );
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************
|
|
* If we do not have a local_id in a signature packet, find the owner of
|
|
* the signature packet in our trustdb or insert them into the trustdb
|
|
*/
|
|
static int
|
|
set_signature_packets_local_id( PKT_signature *sig )
|
|
{
|
|
PKT_public_cert *pkc = m_alloc_clear( sizeof *pkc );
|
|
TRUSTREC rec;
|
|
int rc;
|
|
|
|
rc = get_pubkey( pkc, sig->keyid );
|
|
if( rc)
|
|
goto leave;
|
|
if( !pkc->local_id ) {
|
|
rc = search_record( pkc, &rec );
|
|
if( rc == -1 )
|
|
rc = insert_trust_record( pkc );
|
|
if( rc )
|
|
goto leave;
|
|
/* fixme: we should propagate the local_id to all copies of the PKC */
|
|
}
|
|
sig->local_id = pkc->local_id;
|
|
|
|
leave:
|
|
free_public_cert( pkc );
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
keyid_from_local_id( ulong lid, u32 *keyid )
|
|
{
|
|
TRUSTREC rec;
|
|
int rc;
|
|
|
|
rc = read_record( lid, &rec, RECTYPE_DIR );
|
|
if( rc ) {
|
|
log_error(_("error reading record with local_id %lu: %s\n"),
|
|
lid, g10_errstr(rc));
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
if( rec.rectype != RECTYPE_DIR ) {
|
|
log_error(_("record with local_id %lu is not a dir record\n"), lid);
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
keyid[0] = rec.r.dir.keyid[0];
|
|
keyid[1] = rec.r.dir.keyid[1];
|
|
return 0;
|
|
}
|
|
|
|
/****************
|
|
* Walk through the signatures of a public key.
|
|
* The caller must provide a context structure, with all fields set
|
|
* to zero, but the local_id field set to the requested key;
|
|
* This function does not change this field. On return the context
|
|
* is filled with the local-id of the signature and the signature flag.
|
|
* No fields should be changed (clearing all fields and setting
|
|
* pubkeyid is okay to continue with an other pubkey)
|
|
* Returns: 0 - okay, -1 for eof (no more sigs) or any other errorcode
|
|
*/
|
|
static int
|
|
walk_sigrecs( SIGREC_CONTEXT *c, int create )
|
|
{
|
|
int rc=0;
|
|
TRUSTREC *r;
|
|
ulong rnum;
|
|
|
|
if( c->ctl.eof )
|
|
return -1;
|
|
r = &c->ctl.rec;
|
|
if( !c->ctl.init_done ) {
|
|
c->ctl.init_done = 1;
|
|
if( !c->sigrec ) {
|
|
rc = read_record( c->local_id, r, RECTYPE_DIR );
|
|
if( rc ) {
|
|
log_error(_("%lu: error reading dir record: %s\n"),
|
|
c->local_id, g10_errstr(rc));
|
|
return rc;
|
|
}
|
|
c->sigrec = r->r.dir.sigrec;
|
|
if( !c->sigrec && create && !r->r.dir.no_sigs ) {
|
|
rc = build_sigrecs( c->local_id );
|
|
if( rc ) {
|
|
if( rc == G10ERR_BAD_CERT )
|
|
rc = -1; /* maybe no selcficnature */
|
|
if( rc != -1 )
|
|
log_info(_("%lu: error building sigs on the fly: %s\n"),
|
|
c->local_id, g10_errstr(rc) );
|
|
c->ctl.eof = 1;
|
|
return rc;
|
|
}
|
|
rc = read_record( c->local_id, r, RECTYPE_DIR );
|
|
if( rc ) {
|
|
log_error(_("%lu: error re-reading dir record: %s\n"),
|
|
c->local_id, g10_errstr(rc));
|
|
return rc;
|
|
}
|
|
c->sigrec = r->r.dir.sigrec;
|
|
}
|
|
if( !c->sigrec ) {
|
|
c->ctl.eof = 1;
|
|
return -1;
|
|
}
|
|
}
|
|
/* force a read */
|
|
c->ctl.index = SIGS_PER_RECORD;
|
|
r->r.sig.chain = c->sigrec;
|
|
}
|
|
|
|
/* enter loop to skip deleted sigs */
|
|
do {
|
|
if( c->ctl.index >= SIGS_PER_RECORD ) {
|
|
/* read the record */
|
|
rnum = r->r.sig.chain;
|
|
if( !rnum ) {
|
|
c->ctl.eof = 1;
|
|
return -1; /* return eof */
|
|
}
|
|
rc = read_record( rnum, r, RECTYPE_SIG );
|
|
if( rc ) {
|
|
log_error(_("error reading sigrec: %s\n"), g10_errstr(rc));
|
|
c->ctl.eof = 1;
|
|
return rc;
|
|
}
|
|
if( r->r.sig.owner != c->local_id ) {
|
|
log_error(_("chained sigrec %lu has a wrong owner\n"), rnum );
|
|
c->ctl.eof = 1;
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
c->ctl.index = 0;
|
|
}
|
|
} while( !r->r.sig.sig[c->ctl.index++].local_id );
|
|
c->sig_id = r->r.sig.sig[c->ctl.index-1].local_id;
|
|
c->sig_flag = r->r.sig.sig[c->ctl.index-1].flag;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/***********************************************
|
|
************* Trust stuff ******************
|
|
***********************************************/
|
|
|
|
|
|
/****************
|
|
* Verify that all our public keys are in the trustDB.
|
|
*/
|
|
static int
|
|
verify_own_certs()
|
|
{
|
|
int rc;
|
|
void *enum_context = NULL;
|
|
PKT_secret_cert *skc = m_alloc_clear( sizeof *skc );
|
|
PKT_public_cert *pkc = m_alloc_clear( sizeof *pkc );
|
|
u32 keyid[2];
|
|
|
|
while( !(rc=enum_secret_keys( &enum_context, skc) ) ) {
|
|
/* fixed: to be sure that it is a secret key of our own,
|
|
* we should check it, but this needs a passphrase
|
|
* for every key and this is boring for the user.
|
|
* Solution: Sign the secring and the trustring
|
|
* and verify this signature during
|
|
* startup
|
|
*/
|
|
|
|
keyid_from_skc( skc, keyid );
|
|
|
|
if( DBG_TRUST )
|
|
log_debug("checking secret key %08lX\n", (ulong)keyid[1] );
|
|
|
|
/* see whether we can access the public key of this secret key */
|
|
memset( pkc, 0, sizeof *pkc );
|
|
rc = get_pubkey( pkc, keyid );
|
|
if( rc ) {
|
|
log_error(_("keyid %08lX: secret key without public key\n"),
|
|
(ulong)keyid[1] );
|
|
goto leave;
|
|
}
|
|
if( cmp_public_secret_cert( pkc, skc ) ) {
|
|
log_error(_("keyid %08lX: secret and public key don't match\n"),
|
|
(ulong)keyid[1] );
|
|
rc = G10ERR_GENERAL;
|
|
goto leave;
|
|
}
|
|
|
|
/* make sure that the pubkey is in the trustdb */
|
|
rc = query_trust_record( pkc );
|
|
if( rc == -1 ) { /* put it into the trustdb */
|
|
rc = insert_trust_record( pkc );
|
|
if( rc ) {
|
|
log_error(_("keyid %08lX: can't put it into the trustdb\n"),
|
|
(ulong)keyid[1] );
|
|
goto leave;
|
|
}
|
|
}
|
|
else if( rc ) {
|
|
log_error(_("keyid %08lX: query record failed\n"), (ulong)keyid[1] );
|
|
goto leave;
|
|
|
|
}
|
|
|
|
if( DBG_TRUST )
|
|
log_debug("putting %08lX(%lu) into ultikey_table\n",
|
|
(ulong)keyid[1], pkc->local_id );
|
|
if( ins_lid_table_item( ultikey_table, pkc->local_id, 0 ) )
|
|
log_error(_("keyid %08lX: already in ultikey_table\n"),
|
|
(ulong)keyid[1]);
|
|
|
|
|
|
release_secret_cert_parts( skc );
|
|
release_public_cert_parts( pkc );
|
|
}
|
|
if( rc != -1 )
|
|
log_error(_("enum_secret_keys failed: %s\n"), g10_errstr(rc) );
|
|
else
|
|
rc = 0;
|
|
|
|
leave:
|
|
free_secret_cert( skc );
|
|
free_public_cert( pkc );
|
|
return rc;
|
|
}
|
|
|
|
|
|
static void
|
|
print_user_id( const char *text, u32 *keyid )
|
|
{
|
|
char *p;
|
|
size_t n;
|
|
|
|
p = get_user_id( keyid, &n );
|
|
if( *text ) {
|
|
fputs( text, stdout);
|
|
putchar(' ');
|
|
}
|
|
putchar('\"');
|
|
print_string( stdout, p, n, 0 );
|
|
putchar('\"');
|
|
putchar('\n');
|
|
m_free(p);
|
|
}
|
|
|
|
/* (a non-recursive algorithm would be easier) */
|
|
static int
|
|
do_list_sigs( ulong root, ulong pubkey, int depth,
|
|
LOCAL_ID_INFO *lids, unsigned *lineno )
|
|
{
|
|
SIGREC_CONTEXT sx;
|
|
int rc;
|
|
u32 keyid[2];
|
|
|
|
memset( &sx, 0, sizeof sx );
|
|
sx.local_id = pubkey;
|
|
for(;;) {
|
|
rc = walk_sigrecs( &sx, 0 );
|
|
if( rc )
|
|
break;
|
|
rc = keyid_from_local_id( sx.sig_id, keyid );
|
|
if( rc ) {
|
|
printf("%6u: %*s????????(%lu:%02x)\n", *lineno, depth*4, "",
|
|
sx.sig_id, sx.sig_flag );
|
|
++*lineno;
|
|
}
|
|
else {
|
|
printf("%6u: %*s%08lX(%lu:%02x) ", *lineno, depth*4, "",
|
|
(ulong)keyid[1], sx.sig_id, sx.sig_flag );
|
|
/* check whether we already checked this pubkey */
|
|
if( !qry_lid_table_flag( ultikey_table, sx.sig_id, NULL ) ) {
|
|
print_user_id("[ultimately trusted]", keyid);
|
|
++*lineno;
|
|
}
|
|
else if( sx.sig_id == pubkey ) {
|
|
printf("[self-signature]\n");
|
|
++*lineno;
|
|
}
|
|
else if( sx.sig_id == root ) {
|
|
printf("[closed]\n");
|
|
++*lineno;
|
|
}
|
|
else if( ins_lid_table_item( lids, sx.sig_id, *lineno ) ) {
|
|
unsigned refline;
|
|
qry_lid_table_flag( lids, sx.sig_id, &refline );
|
|
printf("[see line %u]\n", refline);
|
|
++*lineno;
|
|
}
|
|
else if( depth+1 >= MAX_LIST_SIGS_DEPTH ) {
|
|
print_user_id( "[too deeply nested]", keyid );
|
|
++*lineno;
|
|
}
|
|
else {
|
|
print_user_id( "", keyid );
|
|
++*lineno;
|
|
rc = do_list_sigs( root, sx.sig_id, depth+1, lids, lineno );
|
|
if( rc )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rc==-1? 0 : rc;
|
|
}
|
|
|
|
/****************
|
|
* List all signatures of a public key
|
|
*/
|
|
static int
|
|
list_sigs( ulong pubkey_id )
|
|
{
|
|
int rc;
|
|
u32 keyid[2];
|
|
LOCAL_ID_INFO *lids;
|
|
unsigned lineno = 1;
|
|
|
|
rc = keyid_from_local_id( pubkey_id, keyid );
|
|
if( rc ) {
|
|
log_error("Hmmm, no pubkey record for local_id %lu\n", pubkey_id);
|
|
return rc;
|
|
}
|
|
printf("Signatures of %08lX(%lu) ", (ulong)keyid[1], pubkey_id );
|
|
print_user_id("", keyid);
|
|
printf("----------------------\n");
|
|
|
|
lids = new_lid_table();
|
|
rc = do_list_sigs( pubkey_id, pubkey_id, 0, lids, &lineno );
|
|
putchar('\n');
|
|
release_lid_table(lids);
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* Function to collect all trustpaths
|
|
*/
|
|
static int
|
|
do_list_path( TRUST_INFO *stack, int depth, int max_depth,
|
|
LOCAL_ID_INFO *lids, TRUST_SEG_LIST *tslist )
|
|
{
|
|
SIGREC_CONTEXT sx;
|
|
unsigned last_depth;
|
|
int rc;
|
|
|
|
assert(depth);
|
|
|
|
/*printf("%2lu/%d: scrutinizig\n", stack[depth-1], depth);*/
|
|
if( depth >= max_depth || depth >= MAX_LIST_SIGS_DEPTH-1 ) {
|
|
/*printf("%2lu/%d: too deeply nested\n", stack[depth-1], depth);*/
|
|
return 0;
|
|
}
|
|
memset( &sx, 0, sizeof sx );
|
|
sx.local_id = stack[depth-1].lid;
|
|
/* loop over all signatures. If we do not have any, try to
|
|
* create them */
|
|
while( !(rc = walk_sigrecs( &sx, 1 )) ) {
|
|
TRUST_SEG_LIST tsl, t2, tl;
|
|
int i;
|
|
|
|
stack[depth].lid = sx.sig_id;
|
|
stack[depth].trust = 0;
|
|
if( qry_lid_table_flag( lids, sx.sig_id, &last_depth) ) {
|
|
/*printf("%2lu/%d: marked\n", sx.sig_id, depth );*/
|
|
ins_lid_table_item( lids, sx.sig_id, depth);
|
|
last_depth = depth;
|
|
}
|
|
else if( depth < last_depth ) {
|
|
/*printf("%2lu/%d: last_depth=%u - updated\n", sx.sig_id, depth, last_depth);*/
|
|
last_depth = depth;
|
|
upd_lid_table_flag( lids, sx.sig_id, depth);
|
|
}
|
|
|
|
if( last_depth < depth )
|
|
/*printf("%2lu/%d: already visited\n", sx.sig_id, depth)*/;
|
|
else if( !qry_lid_table_flag( ultikey_table, sx.sig_id, NULL ) ) {
|
|
/* found end of path; store it, ordered by path length */
|
|
tsl = m_alloc( sizeof *tsl + depth*sizeof(TRUST_INFO) );
|
|
tsl->nseg = depth+1;
|
|
tsl->dup = 0;
|
|
for(i=0; i <= depth; i++ )
|
|
tsl->seg[i] = stack[i];
|
|
for(t2=*tslist,tl=NULL; t2; tl=t2, t2 = t2->next )
|
|
if( depth < t2->nseg )
|
|
break;
|
|
if( !tl ) {
|
|
tsl->next = t2;
|
|
*tslist = tsl;
|
|
}
|
|
else {
|
|
tsl->next = t2;
|
|
tl->next = tsl;
|
|
}
|
|
/*putchar('.'); fflush(stdout);*/
|
|
/*printf("%2lu/%d: found\n", sx.sig_id, depth);*/
|
|
}
|
|
else {
|
|
rc = do_list_path( stack, depth+1, max_depth, lids, tslist);
|
|
if( rc && rc != -1 )
|
|
break;
|
|
}
|
|
}
|
|
return rc==-1? 0 : rc;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* Check all the sigs of the given keyblock and mark them
|
|
* as checked. Valid signatures which are duplicates are
|
|
* also marked [shall we check them at all?]
|
|
* FIXME: what shall we do if we have duplicate signatures where only
|
|
* some of them are bad?
|
|
*/
|
|
static int
|
|
check_sigs( KBNODE keyblock, int *selfsig_okay, int *revoked )
|
|
{
|
|
KBNODE node;
|
|
int rc;
|
|
LOCAL_ID_INFO *dups = NULL;
|
|
|
|
*selfsig_okay = 0;
|
|
*revoked = 0;
|
|
for( node=keyblock; node; node = node->next ) {
|
|
if( node->pkt->pkttype == PKT_SIGNATURE
|
|
&& ( (node->pkt->pkt.signature->sig_class&~3) == 0x10
|
|
|| node->pkt->pkt.signature->sig_class == 0x20
|
|
|| node->pkt->pkt.signature->sig_class == 0x30) ) {
|
|
int selfsig;
|
|
rc = check_key_signature( keyblock, node, &selfsig );
|
|
if( !rc ) {
|
|
rc = set_signature_packets_local_id( node->pkt->pkt.signature );
|
|
if( rc )
|
|
log_fatal("set_signature_packets_local_id failed: %s\n",
|
|
g10_errstr(rc));
|
|
if( selfsig ) {
|
|
node->flag |= 2; /* mark signature valid */
|
|
*selfsig_okay = 1;
|
|
}
|
|
else if( node->pkt->pkt.signature->sig_class == 0x20 )
|
|
*revoked = 1;
|
|
else
|
|
node->flag |= 1; /* mark signature valid */
|
|
|
|
if( node->pkt->pkt.signature->sig_class != 0x20 ) {
|
|
if( !dups )
|
|
dups = new_lid_table();
|
|
if( ins_lid_table_item( dups,
|
|
node->pkt->pkt.signature->local_id, 0) )
|
|
node->flag |= 4; /* mark as duplicate */
|
|
}
|
|
}
|
|
if( DBG_TRUST )
|
|
log_debug("trustdb: sig from %08lX(%lu): %s%s\n",
|
|
(ulong)node->pkt->pkt.signature->keyid[1],
|
|
node->pkt->pkt.signature->local_id,
|
|
g10_errstr(rc), (node->flag&4)?" (dup)":"" );
|
|
}
|
|
}
|
|
if( dups )
|
|
release_lid_table(dups);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* If we do not have sigrecs for the given key, build them and write them
|
|
* to the trustdb
|
|
*/
|
|
static int
|
|
build_sigrecs( ulong pubkeyid )
|
|
{
|
|
TRUSTREC rec, krec, rec2;
|
|
KBNODE keyblock = NULL;
|
|
KBNODE node;
|
|
int rc=0;
|
|
int i, selfsig, revoked;
|
|
ulong rnum, rnum2;
|
|
ulong first_sigrec = 0;
|
|
|
|
if( DBG_TRUST )
|
|
log_debug("trustdb: build_sigrecs for pubkey %lu\n", (ulong)pubkeyid );
|
|
|
|
/* get the keyblock */
|
|
if( (rc=read_record( pubkeyid, &rec, RECTYPE_DIR )) ) {
|
|
log_error(_("%lu: build_sigrecs: can't read dir record\n"), pubkeyid );
|
|
goto leave;
|
|
}
|
|
if( (rc=read_record( rec.r.dir.keyrec, &krec, RECTYPE_KEY )) ) {
|
|
log_error(_("%lu: build_sigrecs: can't read key record\n"), pubkeyid);
|
|
goto leave;
|
|
}
|
|
rc = get_keyblock_byfprint( &keyblock, krec.r.key.fingerprint );
|
|
if( rc ) {
|
|
log_error(_("build_sigrecs: get_keyblock_byfprint failed\n") );
|
|
goto leave;
|
|
}
|
|
/* check all key signatures */
|
|
rc = check_sigs( keyblock, &selfsig, &revoked );
|
|
if( rc ) {
|
|
log_error(_("build_sigrecs: check_sigs failed\n") );
|
|
goto leave;
|
|
}
|
|
if( !selfsig ) {
|
|
log_error(_("build_sigrecs: self-certificate missing\n") );
|
|
update_no_sigs( pubkeyid, 2 );
|
|
rc = G10ERR_BAD_CERT;
|
|
goto leave;
|
|
}
|
|
if( revoked ) {
|
|
log_info(_("build_sigrecs: key has been revoked\n") );
|
|
update_no_sigs( pubkeyid, 3 );
|
|
}
|
|
else
|
|
update_no_sigs( pubkeyid, 0 ); /* assume we have sigs */
|
|
|
|
/* valid key signatures are now marked; we can now build the
|
|
* sigrecs */
|
|
memset( &rec, 0, sizeof rec );
|
|
rec.rectype = RECTYPE_SIG;
|
|
i = 0;
|
|
rnum = rnum2 = 0;
|
|
for( node=keyblock; node; node = node->next ) {
|
|
/* insert sigs which are not a selfsig nor a duplicate */
|
|
if( (node->flag & 1) && !(node->flag & 4) ) {
|
|
assert( node->pkt->pkttype == PKT_SIGNATURE );
|
|
if( !node->pkt->pkt.signature->local_id ) {
|
|
/* the next function should always succeed, because
|
|
* we have already checked the signature, and for this
|
|
* it was necessary to have the pubkey. The only reason
|
|
* this can fail are I/O errors of the trustdb or a
|
|
* remove operation on the pubkey database - which should
|
|
* not disturb us, because we have to chance them anyway. */
|
|
rc = set_signature_packets_local_id( node->pkt->pkt.signature );
|
|
if( rc )
|
|
log_fatal(_("set_signature_packets_local_id failed: %s\n"),
|
|
g10_errstr(rc));
|
|
}
|
|
if( i == SIGS_PER_RECORD ) {
|
|
/* write the record */
|
|
rnum = new_recnum();
|
|
if( rnum2 ) { /* write the stored record */
|
|
rec2.r.sig.owner = pubkeyid;
|
|
rec2.r.sig.chain = rnum; /* the next record number */
|
|
rc = write_record( rnum2, &rec2 );
|
|
if( rc ) {
|
|
log_error(_("build_sigrecs: write_record failed\n") );
|
|
goto leave;
|
|
}
|
|
if( !first_sigrec )
|
|
first_sigrec = rnum2;
|
|
}
|
|
rec2 = rec;
|
|
rnum2 = rnum;
|
|
memset( &rec, 0, sizeof rec );
|
|
rec.rectype = RECTYPE_SIG;
|
|
i = 0;
|
|
}
|
|
rec.r.sig.sig[i].local_id = node->pkt->pkt.signature->local_id;
|
|
rec.r.sig.sig[i].flag = 0;
|
|
i++;
|
|
}
|
|
}
|
|
if( i || rnum2 ) {
|
|
/* write the record */
|
|
rnum = new_recnum();
|
|
if( rnum2 ) { /* write the stored record */
|
|
rec2.r.sig.owner = pubkeyid;
|
|
rec2.r.sig.chain = rnum;
|
|
rc = write_record( rnum2, &rec2 );
|
|
if( rc ) {
|
|
log_error(_("build_sigrecs: write_record failed\n") );
|
|
goto leave;
|
|
}
|
|
if( !first_sigrec )
|
|
first_sigrec = rnum2;
|
|
}
|
|
if( i ) { /* write the pending record */
|
|
rec.r.sig.owner = pubkeyid;
|
|
rec.r.sig.chain = 0;
|
|
rc = write_record( rnum, &rec );
|
|
if( rc ) {
|
|
log_error(_("build_sigrecs: write_record failed\n") );
|
|
goto leave;
|
|
}
|
|
if( !first_sigrec )
|
|
first_sigrec = rnum;
|
|
}
|
|
}
|
|
if( first_sigrec ) {
|
|
/* update the dir record */
|
|
if( (rc =read_record( pubkeyid, &rec, RECTYPE_DIR )) ) {
|
|
log_error(_("update_dir_record: read failed\n"));
|
|
goto leave;
|
|
}
|
|
rec.r.dir.sigrec = first_sigrec;
|
|
if( (rc=write_record( pubkeyid, &rec )) ) {
|
|
log_error(_("update_dir_record: write failed\n"));
|
|
goto leave;
|
|
}
|
|
}
|
|
else
|
|
update_no_sigs( pubkeyid, revoked? 3:1 ); /* no signatures */
|
|
|
|
leave:
|
|
release_kbnode( keyblock );
|
|
if( DBG_TRUST )
|
|
log_debug(_("trustdb: build_sigrecs: %s\n"), g10_errstr(rc) );
|
|
return rc;
|
|
}
|
|
|
|
/****************
|
|
* Make a list of trust paths
|
|
*/
|
|
static int
|
|
make_tsl( ulong pubkey_id, TRUST_SEG_LIST *ret_tslist )
|
|
{
|
|
int i, rc;
|
|
LOCAL_ID_INFO *lids = new_lid_table();
|
|
TRUST_INFO stack[MAX_LIST_SIGS_DEPTH];
|
|
TRUST_SEG_LIST tsl, tslist;
|
|
int max_depth = 4;
|
|
|
|
tslist = *ret_tslist = NULL;
|
|
|
|
if( !qry_lid_table_flag( ultikey_table, pubkey_id, NULL ) ) {
|
|
tslist = m_alloc( sizeof *tslist );
|
|
tslist->nseg = 1;
|
|
tslist->dup = 0;
|
|
tslist->seg[0].lid = pubkey_id;
|
|
tslist->seg[0].trust = 0;
|
|
tslist->next = NULL;
|
|
rc = 0;
|
|
}
|
|
else {
|
|
stack[0].lid = pubkey_id;
|
|
stack[0].trust = 0;
|
|
rc = do_list_path( stack, 1, max_depth, lids, &tslist );
|
|
}
|
|
if( !rc ) { /* wipe out duplicates */
|
|
LOCAL_ID_INFO *work = new_lid_table();
|
|
for( tsl=tslist; tsl; tsl = tsl->next ) {
|
|
for(i=1; i < tsl->nseg-1; i++ ) {
|
|
if( ins_lid_table_item( work, tsl->seg[i].lid, 0 ) ) {
|
|
tsl->dup = 1; /* mark as duplicate */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
release_lid_table(work);
|
|
*ret_tslist = tslist;
|
|
}
|
|
else
|
|
; /* FIXME: release tslist */
|
|
release_lid_table(lids);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Given a trust segment list tslist, walk over all paths and fill in
|
|
* the trust information for each segment. What this function does is
|
|
* to assign a trustvalue to the first segment (which is the requested key)
|
|
* of each path.
|
|
*
|
|
* FIXME: We have to do more thinking here. e.g. we should never increase
|
|
* the trust value.
|
|
*
|
|
* Do not do it for duplicates.
|
|
*/
|
|
static int
|
|
propagate_trust( TRUST_SEG_LIST tslist )
|
|
{
|
|
int i, rc;
|
|
unsigned trust, tr;
|
|
TRUST_SEG_LIST tsl;
|
|
|
|
for(tsl = tslist; tsl; tsl = tsl->next ) {
|
|
if( tsl->dup )
|
|
continue;
|
|
assert( tsl->nseg );
|
|
/* the last segment is always an ultimately trusted one, so we can
|
|
* assign a fully trust to the next one */
|
|
i = tsl->nseg-1;
|
|
tsl->seg[i].trust = TRUST_ULTIMATE;
|
|
trust = TRUST_FULLY;
|
|
for(i-- ; i >= 0; i-- ) {
|
|
tsl->seg[i].trust = trust;
|
|
if( i > 0 ) {
|
|
/* get the trust of this pubkey */
|
|
rc = get_ownertrust( tsl->seg[i].lid, &tr );
|
|
if( rc )
|
|
return rc;
|
|
if( tr < trust )
|
|
trust = tr;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* we have the pubkey record but nothing more is known.
|
|
* (function may re-read dr)
|
|
*/
|
|
static int
|
|
do_check( ulong pubkeyid, TRUSTREC *dr, unsigned *trustlevel )
|
|
{
|
|
int i, rc=0;
|
|
TRUST_SEG_LIST tsl, tsl2, tslist;
|
|
int marginal, fully;
|
|
int fully_needed = opt.completes_needed;
|
|
int marginal_needed = opt.marginals_needed;
|
|
unsigned tflags = 0;
|
|
|
|
assert( fully_needed > 0 && marginal_needed > 1 );
|
|
|
|
|
|
*trustlevel = TRUST_UNDEFINED;
|
|
|
|
/* verify the cache */
|
|
|
|
/* do we have sigrecs */
|
|
if( !dr->r.dir.sigrec && !dr->r.dir.no_sigs) {
|
|
/* no sigrecs, so build them */
|
|
rc = build_sigrecs( pubkeyid );
|
|
if( !rc ) /* and read again */
|
|
rc = read_record( pubkeyid, dr, RECTYPE_DIR );
|
|
}
|
|
|
|
if( dr->r.dir.no_sigs == 3 )
|
|
tflags |= TRUST_FLAG_REVOKED;
|
|
|
|
if( !rc && !dr->r.dir.sigrec ) {
|
|
/* See whether this is our own key */
|
|
if( !qry_lid_table_flag( ultikey_table, pubkeyid, NULL ) )
|
|
*trustlevel = tflags | TRUST_ULTIMATE;
|
|
return 0;
|
|
}
|
|
if( rc )
|
|
return rc; /* error while looking for sigrec or building sigrecs */
|
|
|
|
/* fixme: take it from the cache if it is valid */
|
|
|
|
/* Make a list of all possible trust-paths */
|
|
rc = make_tsl( pubkeyid, &tslist );
|
|
if( rc )
|
|
return rc;
|
|
rc = propagate_trust( tslist );
|
|
if( rc )
|
|
return rc;
|
|
for(tsl = tslist; tsl; tsl = tsl->next ) {
|
|
if( tsl->dup )
|
|
continue;
|
|
|
|
if( opt.verbose ) {
|
|
log_info("tslist segs:" );
|
|
for(i=0; i < tsl->nseg; i++ )
|
|
fprintf(stderr, " %lu/%02x", tsl->seg[i].lid,
|
|
tsl->seg[i].trust );
|
|
putc('\n',stderr);
|
|
}
|
|
}
|
|
|
|
/* and see whether there is a trusted path.
|
|
* We only have to look at the first segment, because
|
|
* propagate_trust has investigated all other segments */
|
|
marginal = fully = 0;
|
|
for(tsl = tslist; tsl; tsl = tsl->next ) {
|
|
if( tsl->dup )
|
|
continue;
|
|
if( tsl->seg[0].trust == TRUST_ULTIMATE ) {
|
|
*trustlevel = tflags | TRUST_ULTIMATE; /* our own key */
|
|
break;
|
|
}
|
|
if( tsl->seg[0].trust == TRUST_FULLY ) {
|
|
marginal++;
|
|
fully++;
|
|
}
|
|
else if( tsl->seg[0].trust == TRUST_MARGINAL )
|
|
marginal++;
|
|
|
|
if( fully >= fully_needed ) {
|
|
*trustlevel = tflags | TRUST_FULLY;
|
|
break;
|
|
}
|
|
}
|
|
if( !tsl && marginal >= marginal_needed )
|
|
*trustlevel = tflags | TRUST_MARGINAL;
|
|
|
|
/* cache the tslist */
|
|
if( last_trust_web_key ) {
|
|
for( tsl = last_trust_web_tslist; tsl; tsl = tsl2 ) {
|
|
tsl2 = tsl->next;
|
|
m_free(tsl);
|
|
}
|
|
}
|
|
last_trust_web_key = pubkeyid;
|
|
last_trust_web_tslist = tslist;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************
|
|
**************** API ************************
|
|
***********************************************/
|
|
|
|
/****************
|
|
* Perform some checks over the trustdb
|
|
* level 0: only open the db
|
|
* 1: used for initial program startup
|
|
*/
|
|
int
|
|
init_trustdb( int level, const char *dbname )
|
|
{
|
|
int rc=0;
|
|
|
|
if( !ultikey_table )
|
|
ultikey_table = new_lid_table();
|
|
|
|
if( !level || level==1 ) {
|
|
char *fname = dbname? m_strdup( dbname )
|
|
: make_filename(opt.homedir, "trustdb.gpg", NULL );
|
|
if( access( fname, R_OK ) ) {
|
|
if( errno != ENOENT ) {
|
|
log_error(_("can't access %s: %s\n"), fname, strerror(errno) );
|
|
m_free(fname);
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
if( level ) {
|
|
char *p = strrchr( fname, '/' );
|
|
assert(p);
|
|
*p = 0;
|
|
if( access( fname, F_OK ) ) {
|
|
if( strlen(fname) >= 7
|
|
&& !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) {
|
|
#if __MINGW32__
|
|
if( mkdir( fname ) )
|
|
#else
|
|
if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) )
|
|
#endif
|
|
log_fatal(_("can't create directory '%s': %s\n"),
|
|
fname, strerror(errno) );
|
|
}
|
|
else
|
|
log_fatal(_("directory '%s' does not exist!\n"), fname );
|
|
}
|
|
*p = '/';
|
|
create_db( fname );
|
|
}
|
|
}
|
|
m_free(db_name);
|
|
db_name = fname;
|
|
|
|
if( !level )
|
|
return 0;
|
|
|
|
/* we can verify a signature about our local data (secring and trustdb)
|
|
* in ~/.gnupg/ here */
|
|
rc = verify_private_data();
|
|
if( !rc ) {
|
|
/* verify that our own certificates are in the trustDB
|
|
* or move them to the trustdb. */
|
|
rc = verify_own_certs();
|
|
|
|
/* should we check whether there is no other ultimately trusted
|
|
* key in the database? */
|
|
|
|
}
|
|
}
|
|
else
|
|
BUG();
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
void
|
|
list_trustdb( const char *username )
|
|
{
|
|
TRUSTREC rec;
|
|
|
|
if( username ) {
|
|
PKT_public_cert *pkc = m_alloc_clear( sizeof *pkc );
|
|
int rc;
|
|
|
|
if( (rc = get_pubkey_byname( pkc, username )) )
|
|
log_error("user '%s' not found: %s\n", username, g10_errstr(rc) );
|
|
else if( (rc=search_record( pkc, &rec )) && rc != -1 )
|
|
log_error("problem finding '%s' in trustdb: %s\n",
|
|
username, g10_errstr(rc));
|
|
else if( rc == -1 )
|
|
log_error("user '%s' not in trustdb\n", username);
|
|
else if( (rc = list_sigs( pkc->local_id )) )
|
|
log_error("user '%s' list problem: %s\n", username, g10_errstr(rc));
|
|
free_public_cert( pkc );
|
|
}
|
|
else {
|
|
ulong recnum;
|
|
int i;
|
|
|
|
printf("TrustDB: %s\n", db_name );
|
|
for(i=9+strlen(db_name); i > 0; i-- )
|
|
putchar('-');
|
|
putchar('\n');
|
|
for(recnum=0; !read_record( recnum, &rec, 0); recnum++ )
|
|
dump_record( recnum, &rec, stdout );
|
|
}
|
|
}
|
|
|
|
void
|
|
list_trust_path( int max_depth, const char *username )
|
|
{
|
|
int rc;
|
|
int wipe=0;
|
|
int i;
|
|
TRUSTREC rec;
|
|
PKT_public_cert *pkc = m_alloc_clear( sizeof *pkc );
|
|
|
|
if( max_depth < 0 ) {
|
|
wipe = 1;
|
|
max_depth = -max_depth;
|
|
}
|
|
|
|
if( (rc = get_pubkey_byname( pkc, username )) )
|
|
log_error("user '%s' not found: %s\n", username, g10_errstr(rc) );
|
|
else if( (rc=search_record( pkc, &rec )) && rc != -1 )
|
|
log_error("problem finding '%s' in trustdb: %s\n",
|
|
username, g10_errstr(rc));
|
|
else if( rc == -1 ) {
|
|
log_info("user '%s' not in trustdb - inserting\n", username);
|
|
rc = insert_trust_record( pkc );
|
|
if( rc )
|
|
log_error("failed to put '%s' into trustdb: %s\n", username, g10_errstr(rc));
|
|
else {
|
|
assert( pkc->local_id );
|
|
}
|
|
}
|
|
|
|
if( !rc ) {
|
|
TRUST_SEG_LIST tsl, tslist = NULL;
|
|
|
|
if( !qry_lid_table_flag( ultikey_table, pkc->local_id, NULL ) ) {
|
|
tslist = m_alloc( sizeof *tslist );
|
|
tslist->nseg = 1;
|
|
tslist->dup = 0;
|
|
tslist->seg[0].lid = pkc->local_id;
|
|
tslist->seg[0].trust = 0;
|
|
tslist->next = NULL;
|
|
rc = 0;
|
|
}
|
|
else {
|
|
LOCAL_ID_INFO *lids = new_lid_table();
|
|
TRUST_INFO stack[MAX_LIST_SIGS_DEPTH];
|
|
|
|
stack[0].lid = pkc->local_id;
|
|
stack[0].trust = 0;
|
|
rc = do_list_path( stack, 1, max_depth, lids, &tslist );
|
|
if( wipe ) { /* wipe out duplicates */
|
|
LOCAL_ID_INFO *work;
|
|
|
|
work = new_lid_table();
|
|
for( tsl=tslist; tsl; tsl = tsl->next ) {
|
|
for(i=1; i < tsl->nseg-1; i++ ) {
|
|
if( ins_lid_table_item( work, tsl->seg[i].lid, 0 ) ) {
|
|
tsl->dup = 1; /* mark as duplicate */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
release_lid_table(work);
|
|
}
|
|
release_lid_table(lids);
|
|
}
|
|
if( rc )
|
|
log_error("user '%s' list problem: %s\n", username, g10_errstr(rc));
|
|
rc = propagate_trust( tslist );
|
|
if( rc )
|
|
log_error("user '%s' trust problem: %s\n", username, g10_errstr(rc));
|
|
for(tsl = tslist; tsl; tsl = tsl->next ) {
|
|
int i;
|
|
|
|
if( tsl->dup )
|
|
continue;
|
|
printf("trust path:" );
|
|
for(i=0; i < tsl->nseg; i++ )
|
|
printf(" %lu/%02x", tsl->seg[i].lid, tsl->seg[i].trust );
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
free_public_cert( pkc );
|
|
}
|
|
|
|
|
|
/****************
|
|
* Get the trustlevel for this PKC.
|
|
* Note: This does not ask any questions
|
|
* Returns: 0 okay of an errorcode
|
|
*
|
|
* It operates this way:
|
|
* locate the pkc in the trustdb
|
|
* found:
|
|
* Do we have a valid cache record for it?
|
|
* yes: return trustlevel from cache
|
|
* no: make a cache record and all the other stuff
|
|
* not found:
|
|
* Return with a trustlevel, saying that we do not have
|
|
* a trust record for it. The caller may use insert_trust_record()
|
|
* and then call this function here again.
|
|
*
|
|
* Problems: How do we get the complete keyblock to check that the
|
|
* cache record is actually valid? Think we need a clever
|
|
* cache in getkey.c to keep track of this stuff. Maybe it
|
|
* is not necessary to check this if we use a local pubring. Hmmmm.
|
|
*/
|
|
int
|
|
check_trust( PKT_public_cert *pkc, unsigned *r_trustlevel )
|
|
{
|
|
TRUSTREC rec;
|
|
unsigned trustlevel = TRUST_UNKNOWN;
|
|
int rc=0;
|
|
int cur_time;
|
|
|
|
if( DBG_TRUST )
|
|
log_info("check_trust() called.\n");
|
|
|
|
/* get the pubkey record */
|
|
if( pkc->local_id ) {
|
|
if( read_record( pkc->local_id, &rec, RECTYPE_DIR ) ) {
|
|
log_error(_("check_trust: read record failed\n"));
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
}
|
|
else { /* no local_id: scan the trustdb */
|
|
if( (rc=search_record( pkc, &rec )) && rc != -1 ) {
|
|
log_error(_("check_trust: search_record failed: %s\n"),
|
|
g10_errstr(rc));
|
|
return rc;
|
|
}
|
|
else if( rc == -1 ) {
|
|
rc = insert_trust_record( pkc );
|
|
if( rc ) {
|
|
log_error(_("failed to insert pubkey into trustdb: %s\n"),
|
|
g10_errstr(rc));
|
|
goto leave;
|
|
}
|
|
log_info(_("pubkey not in trustdb - inserted as %lu\n"),
|
|
pkc->local_id );
|
|
}
|
|
}
|
|
cur_time = make_timestamp();
|
|
if( pkc->timestamp > cur_time ) {
|
|
log_info(_("public key created in future (time warp or clock problem)\n"));
|
|
return G10ERR_TIME_CONFLICT;
|
|
}
|
|
|
|
if( pkc->valid_days && add_days_to_timestamp(pkc->timestamp,
|
|
pkc->valid_days) < cur_time ) {
|
|
log_info(_("key expiration date is %s\n"), strtimestamp(
|
|
add_days_to_timestamp(pkc->timestamp,
|
|
pkc->valid_days)));
|
|
trustlevel = TRUST_EXPIRED;
|
|
}
|
|
else {
|
|
rc = do_check( pkc->local_id, &rec, &trustlevel );
|
|
if( rc ) {
|
|
log_error(_("check_trust: do_check failed: %s\n"), g10_errstr(rc));
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
leave:
|
|
if( DBG_TRUST )
|
|
log_info("check_trust() returns trustlevel %04x.\n", trustlevel);
|
|
*r_trustlevel = trustlevel;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
query_trust_info( PKT_public_cert *pkc )
|
|
{
|
|
unsigned trustlevel;
|
|
int c;
|
|
|
|
if( check_trust( pkc, &trustlevel ) )
|
|
return '?';
|
|
if( trustlevel & TRUST_FLAG_REVOKED )
|
|
return 'r';
|
|
switch( (trustlevel & TRUST_MASK) ) {
|
|
case TRUST_UNKNOWN: c = 'o'; break;
|
|
case TRUST_EXPIRED: c = 'e'; break;
|
|
case TRUST_UNDEFINED: c = 'q'; break;
|
|
case TRUST_NEVER: c = 'n'; break;
|
|
case TRUST_MARGINAL: c = 'm'; break;
|
|
case TRUST_FULLY: c = 'f'; break;
|
|
case TRUST_ULTIMATE: c = 'u'; break;
|
|
default: BUG();
|
|
}
|
|
return c;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*/
|
|
int
|
|
enum_trust_web( void **context, ulong *lid )
|
|
{
|
|
ENUM_TRUST_WEB_CONTEXT *c = *context;
|
|
|
|
if( !c ) { /* make a new context */
|
|
c = m_alloc_clear( sizeof *c );
|
|
*context = c;
|
|
if( *lid != last_trust_web_key && last_trust_web_key )
|
|
log_bug("enum_trust_web: nyi\n"); /* <--- FIXME */
|
|
c->tsl = last_trust_web_tslist;
|
|
c->index = 1;
|
|
}
|
|
|
|
if( !lid ) { /* free the context */
|
|
m_free( c );
|
|
*context = NULL;
|
|
return 0;
|
|
}
|
|
|
|
while( c->tsl ) {
|
|
if( !c->tsl->dup && c->index < c->tsl->nseg-1 ) {
|
|
*lid = c->tsl->seg[c->index].lid;
|
|
c->index++;
|
|
return 0;
|
|
}
|
|
c->index = 1;
|
|
c->tsl = c->tsl->next;
|
|
}
|
|
return -1; /* eof */
|
|
}
|
|
|
|
|
|
/****************
|
|
* Return the assigned ownertrust value for the given LID
|
|
*/
|
|
int
|
|
get_ownertrust( ulong lid, unsigned *r_otrust )
|
|
{
|
|
TRUSTREC rec;
|
|
|
|
if( read_record( lid, &rec, RECTYPE_DIR ) ) {
|
|
log_error("get_ownertrust: read dir record failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
if( read_record( rec.r.dir.keyrec, &rec, RECTYPE_KEY ) ) {
|
|
log_error("get_ownertrust: read key record failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
if( r_otrust )
|
|
*r_otrust = rec.r.key.ownertrust;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
keyid_from_trustdb( ulong lid, u32 *keyid )
|
|
{
|
|
TRUSTREC rec;
|
|
|
|
if( read_record( lid, &rec, RECTYPE_DIR ) ) {
|
|
log_error("keyid_from_trustdb: read record failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
if( keyid ) {
|
|
keyid[0] = rec.r.dir.keyid[0];
|
|
keyid[1] = rec.r.dir.keyid[1];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* This function simply looks for the key in the trustdb
|
|
* and sets PKC->local_id.
|
|
* Return: 0 = found
|
|
* -1 = not found
|
|
* other = error
|
|
*/
|
|
int
|
|
query_trust_record( PKT_public_cert *pkc )
|
|
{
|
|
TRUSTREC rec;
|
|
int rc=0;
|
|
|
|
if( pkc->local_id ) {
|
|
if( read_record( pkc->local_id, &rec, RECTYPE_DIR ) ) {
|
|
log_error("query_trust_record: read record failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
}
|
|
else { /* no local_id: scan the trustdb */
|
|
if( (rc=search_record( pkc, &rec )) && rc != -1 ) {
|
|
log_error("query_trust_record: search_record failed: %s\n",
|
|
g10_errstr(rc));
|
|
return rc;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Insert a trust record into the TrustDB
|
|
* This function fails if this record already exists.
|
|
*/
|
|
int
|
|
insert_trust_record( PKT_public_cert *pkc )
|
|
{
|
|
TRUSTREC rec;
|
|
u32 keyid[2];
|
|
ulong knum, dnum;
|
|
byte *fingerprint;
|
|
size_t fingerlen;
|
|
|
|
|
|
if( pkc->local_id )
|
|
log_bug("pkc->local_id=%lu\n", (ulong)pkc->local_id );
|
|
|
|
keyid_from_pkc( pkc, keyid );
|
|
fingerprint = fingerprint_from_pkc( pkc, &fingerlen );
|
|
|
|
/* FIXME: check that we do not have this record. */
|
|
|
|
dnum = new_recnum();
|
|
knum = new_recnum();
|
|
/* build dir record */
|
|
memset( &rec, 0, sizeof rec );
|
|
rec.rectype = RECTYPE_DIR;
|
|
rec.r.dir.local_id = dnum;
|
|
rec.r.dir.keyid[0] = keyid[0];
|
|
rec.r.dir.keyid[1] = keyid[1];
|
|
rec.r.dir.keyrec = knum;
|
|
rec.r.dir.no_sigs = 0;
|
|
if( write_record( dnum, &rec ) ) {
|
|
log_error("writing dir record failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
/* and the key record */
|
|
memset( &rec, 0, sizeof rec );
|
|
rec.rectype = RECTYPE_KEY;
|
|
rec.r.key.owner = dnum;
|
|
rec.r.key.keyid[0] = keyid[0];
|
|
rec.r.key.keyid[1] = keyid[1];
|
|
rec.r.key.pubkey_algo = pkc->pubkey_algo;
|
|
memcpy(rec.r.key.fingerprint, fingerprint, fingerlen );
|
|
rec.r.key.ownertrust = 0;
|
|
if( write_record( knum, &rec ) ) {
|
|
log_error("wrinting key record failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
|
|
pkc->local_id = dnum;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
update_ownertrust( ulong lid, unsigned new_trust )
|
|
{
|
|
TRUSTREC rec;
|
|
ulong recnum;
|
|
|
|
if( read_record( lid, &rec, RECTYPE_DIR ) ) {
|
|
log_error("update_ownertrust: read dir failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
recnum = rec.r.dir.keyrec;
|
|
if( read_record( recnum, &rec, RECTYPE_KEY ) ) {
|
|
log_error("update_ownertrust: read key failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
/* check keyid, fingerprint etc ? */
|
|
|
|
rec.r.key.ownertrust = new_trust;
|
|
if( write_record( recnum, &rec ) ) {
|
|
log_error("update_ownertrust: write failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* Kludge to prevent duplicate build_sigrecs() due to an invalid
|
|
* certificate (no selfsignature or something like this)
|
|
*/
|
|
static int
|
|
update_no_sigs( ulong lid, int no_sigs )
|
|
{
|
|
TRUSTREC rec;
|
|
|
|
if( read_record( lid, &rec, RECTYPE_DIR ) ) {
|
|
log_error("update_no_sigs: read failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
|
|
rec.r.dir.no_sigs = no_sigs;
|
|
if( write_record( lid, &rec ) ) {
|
|
log_error("update_no_sigs: write failed\n");
|
|
return G10ERR_TRUSTDB;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
verify_private_data()
|
|
{
|
|
int rc = 0;
|
|
char *sigfile = make_filename(opt.homedir, "gnupg.sig", NULL );
|
|
|
|
if( access( sigfile, R_OK ) ) {
|
|
if( errno != ENOENT ) {
|
|
log_error("can't access %s: %s\n", sigfile, strerror(errno) );
|
|
rc = G10ERR_TRUSTDB;
|
|
goto leave;
|
|
}
|
|
log_info("private data signature missing; creating ...\n");
|
|
rc = sign_private_data();
|
|
if( rc ) {
|
|
log_error("error creating %s: %s\n", sigfile, g10_errstr(rc) );
|
|
goto leave;
|
|
}
|
|
}
|
|
|
|
/* FIXME: verify this signature */
|
|
|
|
leave:
|
|
m_free(sigfile);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int
|
|
sign_private_data()
|
|
{
|
|
int rc;
|
|
char *sigfile = make_filename(opt.homedir, "gnupg.sig", NULL );
|
|
char *secring = make_filename(opt.homedir, "secring.gpg", NULL );
|
|
STRLIST list = NULL;
|
|
|
|
add_to_strlist( &list, db_name );
|
|
add_to_strlist( &list, secring );
|
|
|
|
rc = sign_file( list, 1, NULL, 0, NULL, sigfile);
|
|
|
|
m_free(sigfile);
|
|
m_free(secring);
|
|
free_strlist(list);
|
|
return rc;
|
|
}
|
|
|