diff --git a/cipher/blowfish.c b/cipher/blowfish.c index 72d617a5c..5dbaf7195 100644 --- a/cipher/blowfish.c +++ b/cipher/blowfish.c @@ -26,6 +26,7 @@ * key "abcdefghijklmnopqrstuvwxyz"; * plain "BLOWFISH" * cipher 32 4E D0 FE F4 13 A2 03 + * */ #include @@ -385,6 +386,9 @@ selftest() BLOWFISH_context c; byte plain[] = "BLOWFISH"; byte buffer[8]; + byte plain3[] = { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }; + byte key3[] = { 0x41, 0x79, 0x6E, 0xA0, 0x52, 0x61, 0x6E, 0xE4 }; + byte cipher3[] = { 0xE1, 0x13, 0xF4, 0x10, 0x2C, 0xFC, 0xCE, 0x43 }; blowfish_setkey( &c, "abcdefghijklmnopqrstuvwxyz", 26 ); encrypt_block( &c, buffer, plain ); @@ -393,6 +397,14 @@ selftest() decrypt_block( &c, buffer, buffer ); if( memcmp( buffer, plain, 8 ) ) log_bug("blowfish failed\n"); + + blowfish_setkey( &c, key3, 8 ); + encrypt_block( &c, buffer, plain3 ); + if( memcmp( buffer, cipher3, 8 ) ) + log_error("wrong blowfish encryption (3)\n"); + decrypt_block( &c, buffer, buffer ); + if( memcmp( buffer, plain3, 8 ) ) + log_bug("blowfish failed (3)\n"); } diff --git a/doc/DETAILS b/doc/DETAILS index a3a2773fb..81aff2912 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -36,7 +36,7 @@ Record type 1: against the pubring) 1 u32 Local-Id-Counter. Used to keep track of Local-IDs. 32 bits are enough numbers for all practial purposes; if this - counter rolls over (due to deleted keyblock,an d new ones), + counter rolls over (due to deleted keyblock and new ones), the software should reassign new Local-Ids to the whole database (not expected to ever occur). 1 byte marginals needed @@ -49,6 +49,7 @@ Record type 1: Record type 2: -------------- Informations about a public key certificate. + These are static values which are never changed without user interaction. 1 byte value 2 1 byte reserved @@ -57,7 +58,7 @@ Record type 2: and usefull if we have duplicate keyids It is not defined, how an implementaion selects such a Local-Id, but it may use the local-ID counter from - record type 1 + record type 1, or simply use the offset of Record type 2 8 bytes keyid (of the primary key) 1 byte pubkey algorithm 1 byte reserved @@ -67,9 +68,9 @@ Record type 2: 0 = undefined (not yet initialized) 1 = unknown owner (could not initialize it) 2 = do not trust this owner - 3 = usually trust this owner - 4 = always trust this owner - 5 = ultimately trust this owner. This can only be set if + 4 = usually trust this owner + 5 = always trust this owner + 7 = ultimately trust this owner. This can only be set if we have control over the secret key too. Bit 3: set if key is revoked; do not use it. Bit 7-4: reserved diff --git a/g10/encode.c b/g10/encode.c index 688cbfc08..ff125079e 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -159,7 +159,7 @@ encode_crypt( const char *filename, STRLIST remusr ) { IOBUF inp = NULL, out = NULL; PACKET pkt; - PKT_plaintext *pt; + PKT_plaintext *pt = NULL; int rc = 0; u32 filesize; cipher_filter_context_t cfx; @@ -244,7 +244,8 @@ encode_crypt( const char *filename, STRLIST remusr ) iobuf_cancel(out); else iobuf_close(out); /* fixme: check returncode */ - pt->buf = NULL; + if( pt ) + pt->buf = NULL; free_packet(&pkt); m_free(cfx.dek); release_pkc_list( pkc_list ); diff --git a/g10/free-packet.c b/g10/free-packet.c index fb5949a8f..e2efa5a66 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -57,18 +57,24 @@ free_seckey_enc( PKT_signature *enc ) } void -free_public_cert( PKT_public_cert *cert ) +release_public_cert_parts( PKT_public_cert *cert ) { if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { - mpi_free( cert->d.elg.p ); - mpi_free( cert->d.elg.g ); - mpi_free( cert->d.elg.y ); + mpi_free( cert->d.elg.p ); cert->d.elg.p = NULL; + mpi_free( cert->d.elg.g ); cert->d.elg.g = NULL; + mpi_free( cert->d.elg.y ); cert->d.elg.y = NULL; } else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) { - mpi_free( cert->d.rsa.rsa_n ); - mpi_free( cert->d.rsa.rsa_e ); + mpi_free( cert->d.rsa.rsa_n ); cert->d.rsa.rsa_n = NULL; + mpi_free( cert->d.rsa.rsa_e ); cert->d.rsa.rsa_e = NULL; } - md_close( cert->mfx.md ); + md_close( cert->mfx.md ); cert->mfx.md = NULL; +} + +void +free_public_cert( PKT_public_cert *cert ) +{ + release_public_cert_parts( cert ); m_free(cert); } @@ -92,22 +98,28 @@ copy_public_cert( PKT_public_cert *d, PKT_public_cert *s ) } void -free_secret_cert( PKT_secret_cert *cert ) +release_secret_cert_parts( PKT_secret_cert *cert ) { if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { - mpi_free( cert->d.elg.p ); - mpi_free( cert->d.elg.g ); - mpi_free( cert->d.elg.y ); - mpi_free( cert->d.elg.x ); + mpi_free( cert->d.elg.p ); cert->d.elg.p = NULL; + mpi_free( cert->d.elg.g ); cert->d.elg.g = NULL; + mpi_free( cert->d.elg.y ); cert->d.elg.y = NULL; + mpi_free( cert->d.elg.x ); cert->d.elg.x = NULL; } else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) { - mpi_free( cert->d.rsa.rsa_n ); - mpi_free( cert->d.rsa.rsa_e ); - mpi_free( cert->d.rsa.rsa_d ); - mpi_free( cert->d.rsa.rsa_p ); - mpi_free( cert->d.rsa.rsa_q ); - mpi_free( cert->d.rsa.rsa_u ); + mpi_free( cert->d.rsa.rsa_n ); cert->d.rsa.rsa_n = NULL; + mpi_free( cert->d.rsa.rsa_e ); cert->d.rsa.rsa_e = NULL; + mpi_free( cert->d.rsa.rsa_d ); cert->d.rsa.rsa_d = NULL; + mpi_free( cert->d.rsa.rsa_p ); cert->d.rsa.rsa_p = NULL; + mpi_free( cert->d.rsa.rsa_q ); cert->d.rsa.rsa_q = NULL; + mpi_free( cert->d.rsa.rsa_u ); cert->d.rsa.rsa_u = NULL; } +} + +void +free_secret_cert( PKT_secret_cert *cert ) +{ + release_secret_cert_parts( cert ); m_free(cert); } @@ -240,4 +252,34 @@ free_packet( PACKET *pkt ) pkt->pkt.generic = NULL; } +/**************** + * Returns 0 if they match. + */ +int +cmp_public_secret_cert( PKT_public_cert *pkc, PKT_secret_cert *skc ) +{ + if( pkc->timestamp != skc->timestamp ) + return -1; + if( pkc->valid_days != skc->valid_days ) + return -1; + if( pkc->pubkey_algo != skc->pubkey_algo ) + return -1; + + if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { + if( mpi_cmp( pkc->d.elg.p , skc->d.elg.p ) ) + return -1; + if( mpi_cmp( pkc->d.elg.g , skc->d.elg.g ) ) + return -1; + if( mpi_cmp( pkc->d.elg.y , skc->d.elg.y ) ) + return -1; + } + else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { + if( mpi_cmp( pkc->d.rsa.rsa_n , skc->d.rsa.rsa_n ) ) + return -1; + if( mpi_cmp( pkc->d.rsa.rsa_e , skc->d.rsa.rsa_e ) ) + return -1; + } + + return 0; +} diff --git a/g10/g10.c b/g10/g10.c index e5c7139d1..2002e69e1 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -424,17 +424,36 @@ main( int argc, char **argv ) break; case aSign: /* sign the given file */ - if( argc > 1 ) - usage(1); - if( (rc = sign_file(fname, detached_sig, locusr, 0, NULL)) ) - log_error("sign_file('%s'): %s\n", fname_print, g10_errstr(rc) ); + sl = NULL; + if( detached_sig ) { /* sign all files */ + for( ; argc; argc--, argv++ ) + add_to_strlist( &sl, *argv ); + } + else { + if( argc > 1 ) + usage(1); + if( argc ) { + sl = m_alloc_clear( sizeof *sl + strlen(fname)); + strcpy(sl->d, fname); + } + } + if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) ) + log_error("sign_file: %s\n", g10_errstr(rc) ); + free_strlist(sl); break; case aSignEncr: /* sign and encrypt the given file */ if( argc > 1 ) usage(1); - if( (rc = sign_file(fname, detached_sig, locusr, 1, remusr)) ) + if( argc ) { + sl = m_alloc_clear( sizeof *sl + strlen(fname)); + strcpy(sl->d, fname); + } + else + sl = NULL; + if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) ) log_error("sign_file('%s'): %s\n", fname_print, g10_errstr(rc) ); + free_strlist(sl); break; diff --git a/g10/getkey.c b/g10/getkey.c index 3ec6a6bf4..b1cb64a29 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -51,6 +51,13 @@ typedef struct pkc_cache_entry { PKT_public_cert *pkc; } *pkc_cache_entry_t; +typedef struct enum_seckey_context { + int eof; + STRLIST sl; + IOBUF iobuf; +} enum_seckey_context_t; + + static STRLIST keyrings; static STRLIST secret_keyrings; @@ -351,6 +358,9 @@ get_seckey_byname( PKT_secret_cert *skc, const char *name, int unprotect ) } + + + /**************** * scan the keyring and look for either the keyid or the name. */ @@ -584,6 +594,69 @@ scan_secret_keyring( PKT_secret_cert *skc, u32 *keyid, } +/**************** + * Enumerate all secret keys. Caller must use these procedure: + * 1) create a void pointer and initialize it to NULL + * 2) pass this void pointer by reference to this function + * and provide space for the secret key (pass a buffer for skc) + * 3) call this function as long as it does not return -1 + * to indicate EOF. + * 4) Always call this function a last time with SKC set to NULL, + * so that can free it's context. + * + * Return + */ +int +enum_secret_keys( void **context, PKT_secret_cert *skc ) +{ + int rc=0; + PACKET pkt; + int save_mode; + enum_seckey_context_t *c = *context; + + if( !c ) { /* make a new context */ + c = m_alloc_clear( sizeof *c ); + *context = c; + c->sl = secret_keyrings; + } + + if( !skc ) { /* free the context */ + m_free( c ); + *context = NULL; + return 0; + } + + if( c->eof ) + return -1; + + for( ; c->sl; c->sl = c->sl->next ) { + if( !c->iobuf ) { + if( !(c->iobuf = iobuf_open( c->sl->d ) ) ) { + log_error("enum_secret_keys: can't open '%s'\n", c->sl->d ); + continue; /* try next file */ + } + } + + save_mode = set_packet_list_mode(0); + init_packet(&pkt); + while( (rc=parse_packet(c->iobuf, &pkt)) != -1 ) { + if( rc ) + ; /* e.g. unknown packet */ + else if( pkt.pkttype == PKT_SECRET_CERT ) { + copy_secret_cert( skc, pkt.pkt.secret_cert ); + set_packet_list_mode(save_mode); + return 0; /* found */ + } + free_packet(&pkt); + } + set_packet_list_mode(save_mode); + iobuf_close(c->iobuf); c->iobuf = NULL; + } + c->eof = 1; + return -1; +} + + /**************** * Return a string with a printable representation of the user_id. * this string must be freed by m_free. diff --git a/g10/keydb.h b/g10/keydb.h index 6770f866e..8eb49478d 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -97,6 +97,7 @@ int get_pubkey( PKT_public_cert *pkc, u32 *keyid ); int get_pubkey_byname( PKT_public_cert *pkc, const char *name ); int get_seckey( PKT_secret_cert *skc, u32 *keyid ); int get_seckey_byname( PKT_secret_cert *skc, const char *name, int unlock ); +int enum_secret_keys( void **context, PKT_secret_cert *skc ); char*get_user_id_string( u32 *keyid ); char*get_user_id( u32 *keyid, size_t *rn ); diff --git a/g10/main.h b/g10/main.h index a10ce4ba3..a1a13e41e 100644 --- a/g10/main.h +++ b/g10/main.h @@ -41,8 +41,8 @@ int encrypt_filter( void *opaque, int control, /*-- sign.c --*/ -int sign_file( const char *filename, int detached, STRLIST locusr, - int encrypt, STRLIST remusr ); +int sign_file( STRLIST filenames, int detached, STRLIST locusr, + int encrypt, STRLIST remusr, const char *outfile ); int sign_key( const char *username, STRLIST locusr ); int edit_keysigs( const char *username ); int change_passphrase( const char *username ); diff --git a/g10/options.h b/g10/options.h index 0df1f41ea..3b72547ce 100644 --- a/g10/options.h +++ b/g10/options.h @@ -58,11 +58,13 @@ struct { #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the cacheing */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ +#define DBG_TRUST_VALUE 256 /* debug the trustdb */ #define DBG_PACKET (opt.debug & DBG_PACKET_VALUE) #define DBG_FILTER (opt.debug & DBG_FILTER_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) +#define DBG_TRUST (opt.debug & DBG_TRUST_VALUE) #endif /*G10_OPTIONS_H*/ diff --git a/g10/packet.h b/g10/packet.h index e82248a85..6326c43d4 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -98,6 +98,7 @@ typedef struct { u16 valid_days; /* valid for this number of days */ byte pubkey_algo; /* algorithm used for public key scheme */ md_filter_context_t mfx; + u32 local_id; /* internal use, valid if > 0 */ union { struct { MPI p; /* prime */ @@ -225,13 +226,16 @@ void hash_public_cert( MD_HANDLE md, PKT_public_cert *pkc ); /*-- free-packet.c --*/ void free_pubkey_enc( PKT_pubkey_enc *enc ); void free_seckey_enc( PKT_signature *enc ); +void release_public_cert_parts( PKT_public_cert *cert ); void free_public_cert( PKT_public_cert *cert ); +void release_secret_cert_parts( PKT_secret_cert *cert ); void free_secret_cert( PKT_secret_cert *cert ); void free_user_id( PKT_user_id *uid ); void free_comment( PKT_comment *rem ); void free_packet( PACKET *pkt ); PKT_public_cert *copy_public_cert( PKT_public_cert *d, PKT_public_cert *s ); PKT_secret_cert *copy_secret_cert( PKT_secret_cert *d, PKT_secret_cert *s ); +int cmp_public_secret_cert( PKT_public_cert *pkc, PKT_secret_cert *skc ); /*-- sig-check.c --*/ diff --git a/g10/pkclist.c b/g10/pkclist.c index f753eb6fc..3fd22549c 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -41,6 +41,25 @@ static int do_we_trust( PKT_public_cert *pkc, int trustlevel ) { + int rc; + + if( trustlevel & TRUST_NO_PUBKEY ) { + /* No pubkey in trustDB: Insert and check again */ + rc = insert_trust_record( pkc ); + if( rc ) { + log_error("failed to insert it into the trustdb: %s\n", + g10_errstr(rc) ); + return 0; /* no */ + } + rc = check_pkc_trust( pkc, &trustlevel ); + if( rc ) + log_fatal("trust check after insert failed: %s\n", + g10_errstr(rc) ); + if( trustlevel & TRUST_NO_PUBKEY ) + log_bug(NULL); + } + + /* Eventuell fragen falls der trustlevel nicht ausreichend ist */ @@ -90,6 +109,7 @@ build_pkc_list( STRLIST remusr, PKC_LIST *ret_pkc_list ) remusr->d, g10_errstr(rc) ); } else if( do_we_trust( pkc, trustlevel ) ) { + /* note: do_we_trust may have changed the trustlevel */ PKC_LIST r; r = m_alloc( sizeof *r ); diff --git a/g10/sign.c b/g10/sign.c index 04ae72a2f..9e8275ae9 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -64,17 +64,22 @@ complete_sig( PKT_signature *sig, PKT_secret_cert *skc, MD_HANDLE md ) /**************** - * Sign the file with name FILENAME. If DETACHED has the value true, - * make a detached signature. If FILENAME is NULL read from stdin + * Sign the files whose names are in FILENAME. + * If DETACHED has the value true, + * make a detached signature. If FILENAMES->d is NULL read from stdin * and ignore the detached mode. Sign the file with all secret keys * which can be taken from LOCUSR, if this is NULL, use the default one * If ENCRYPT is true, use REMUSER (or ask if it is NULL) to encrypt the * signed data for these users. + * If OUTFILE is not NULL; this file is used for output and the function + * does not ask for overwrite permission; output is then always + * uncompressed, non-armored and in binary mode. */ int -sign_file( const char *filename, int detached, STRLIST locusr, - int encrypt, STRLIST remusr ) +sign_file( STRLIST filenames, int detached, STRLIST locusr, + int encrypt, STRLIST remusr, const char *outfile ) { + const char *fname; armor_filter_context_t afx; compress_filter_context_t zfx; md_filter_context_t mfx; @@ -88,6 +93,7 @@ sign_file( const char *filename, int detached, STRLIST locusr, PKC_LIST pkc_list = NULL; SKC_LIST skc_list = NULL; SKC_LIST skc_rover = NULL; + int multifile = 0; memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); @@ -96,6 +102,16 @@ sign_file( const char *filename, int detached, STRLIST locusr, memset( &efx, 0, sizeof efx); init_packet( &pkt ); + if( filenames ) { + fname = filenames->d; + multifile = !!filenames->next; + } + else + fname = NULL; + + if( fname && filenames->next && (!detached || encrypt) ) + log_bug("multiple files can only be detached signed"); + if( (rc=build_skc_list( locusr, &skc_list, 1 )) ) goto leave; if( encrypt ) { @@ -104,28 +120,40 @@ sign_file( const char *filename, int detached, STRLIST locusr, } /* prepare iobufs */ - if( !(inp = iobuf_open(filename)) ) { - log_error("can't open %s: %s\n", filename? filename: "[stdin]", + if( multifile ) /* have list of filenames */ + inp = NULL; /* we do it later */ + else if( !(inp = iobuf_open(fname)) ) { + log_error("can't open %s: %s\n", fname? fname: "[stdin]", strerror(errno) ); rc = G10ERR_OPEN_FILE; goto leave; } - if( !(out = open_outfile( filename, opt.armor? 1: detached? 2:0 )) ) { + if( outfile ) { + if( !(out = iobuf_create( outfile )) ) { + log_error("can't create %s: %s\n", outfile, strerror(errno) ); + rc = G10ERR_CREATE_FILE; + goto leave; + } + else if( opt.verbose ) + log_info("writing to '%s'\n", outfile ); + } + else if( !(out = open_outfile( fname, opt.armor? 1: detached? 2:0 )) ) { rc = G10ERR_CREATE_FILE; goto leave; } /* prepare to calculate the MD over the input */ - if( opt.textmode && opt.armor ) + if( opt.textmode && opt.armor && !outfile ) iobuf_push_filter( inp, text_filter, &tfx ); mfx.md = md_open(DIGEST_ALGO_RMD160, 0); - iobuf_push_filter( inp, md_filter, &mfx ); + if( !multifile ) + iobuf_push_filter( inp, md_filter, &mfx ); - if( opt.armor ) + if( opt.armor && !outfile ) iobuf_push_filter( out, armor_filter, &afx ); write_comment( out, "#Created by G10 pre-release " VERSION ); - if( opt.compress ) + if( opt.compress && !outfile ) iobuf_push_filter( out, compress_filter, &zfx ); if( encrypt ) { @@ -141,7 +169,7 @@ sign_file( const char *filename, int detached, STRLIST locusr, skc = skc_rover->skc; ops = m_alloc_clear( sizeof *ops ); - ops->sig_class = opt.textmode? 0x01 : 0x00; + ops->sig_class = opt.textmode && !outfile ? 0x01 : 0x00; ops->digest_algo = DIGEST_ALGO_RMD160; ops->pubkey_algo = skc->pubkey_algo; keyid_from_skc( skc, ops->keyid ); @@ -161,17 +189,40 @@ sign_file( const char *filename, int detached, STRLIST locusr, /* setup the inner packet */ if( detached ) { - /* read, so that the filter can calculate the digest */ - while( iobuf_get(inp) != -1 ) - ; + if( multifile ) { + STRLIST sl = filenames; + + if( opt.verbose ) + log_info("signing:" ); + for(; sl; sl = sl->next ) { + if( !(inp = iobuf_open(sl->d)) ) { + log_error("can't open %s: %s\n", sl->d, strerror(errno) ); + rc = G10ERR_OPEN_FILE; + goto leave; + } + if( opt.verbose ) + fprintf(stderr, " '%s'", sl->d ); + iobuf_push_filter( inp, md_filter, &mfx ); + while( iobuf_get(inp) != -1 ) + ; + iobuf_close(inp); inp = NULL; + } + if( opt.verbose ) + putc( '\n', stderr ); + } + else { + /* read, so that the filter can calculate the digest */ + while( iobuf_get(inp) != -1 ) + ; + } } else { - if( filename ) { - pt = m_alloc( sizeof *pt + strlen(filename) - 1 ); - pt->namelen = strlen(filename); - memcpy(pt->name, filename, pt->namelen ); + if( fname ) { + pt = m_alloc( sizeof *pt + strlen(fname) - 1 ); + pt->namelen = strlen(fname); + memcpy(pt->name, fname, pt->namelen ); if( !(filesize = iobuf_get_filelength(inp)) ) - log_info("warning: '%s' is an empty file\n", filename ); + log_info("warning: '%s' is an empty file\n", fname ); } else { /* no filename */ pt = m_alloc( sizeof *pt - 1 ); @@ -179,7 +230,7 @@ sign_file( const char *filename, int detached, STRLIST locusr, filesize = 0; /* stdin */ } pt->timestamp = make_timestamp(); - pt->mode = opt.textmode? 't':'b'; + pt->mode = opt.textmode && !outfile ? 't':'b'; pt->len = filesize; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; @@ -203,7 +254,7 @@ sign_file( const char *filename, int detached, STRLIST locusr, sig = m_alloc_clear( sizeof *sig ); sig->pubkey_algo = skc->pubkey_algo; sig->timestamp = make_timestamp(); - sig->sig_class = opt.textmode? 0x01 : 0x00; + sig->sig_class = opt.textmode && !outfile? 0x01 : 0x00; md = md_copy( mfx.md ); md_putc( md, sig->sig_class ); diff --git a/g10/trustdb.c b/g10/trustdb.c index 46c950021..1dfaaa570 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "errors.h" #include "iobuf.h" @@ -41,7 +42,6 @@ struct trust_record { byte rectype; byte reserved; union { - byte raw[TRUST_RECORD_LEN-2]; struct { /* version record: */ byte magic[2]; byte version; /* should be 1 */ @@ -76,10 +76,32 @@ struct trust_record { } cache; } r; }; +typedef struct trust_record TRUSTREC; +static void create_db( const char *fname ); +static void open_db(void); +static int read_record( u32 recnum, TRUSTREC *rec ); +static u32 new_local_id(void); static char *db_name; +static int db_fd = -1; +static int no_io_dbg = 0; + +#define buftou32( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ + (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3))) +#define buftou16( p ) ((*((byte*)(p)) << 8) | (*((byte*)(p)+1))) +#define u32tobuf( 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 u16tobuf( p, a ) do { \ + ((byte*)p)[0] = a >> 8; \ + ((byte*)p)[1] = a ; \ + } while(0) + /************************************************** ************** read and write helpers ************ @@ -147,7 +169,7 @@ create_db( const char *fname ) fwrite_32( fp, make_timestamp() ); /* created */ fwrite_32( fp, 0 ); /* not yet modified */ fwrite_32( fp, 0 ); /* not yet validated*/ - fwrite_32( fp, 0 ); /* local-id-counter */ + fwrite_32( fp, 0 ); /* local-id-counter (not used) */ fwrite_8( fp, 3 ); /* marginals needed */ fwrite_8( fp, 1 ); /* completes needed */ fwrite_8( fp, 4 ); /* max_cet_depth */ @@ -155,14 +177,255 @@ create_db( const char *fname ) 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 ) ) + log_fatal("TrustDB %s is invalid\n", db_name ); + /* fixme: check ->locked and other stuff */ +} +/**************** + * read the record with number recnum + * returns: -1 on error, 0 on success + */ +static int +read_record( u32 recnum, TRUSTREC *rec ) +{ + byte buf[TRUST_RECORD_LEN], *p; + int rc = 0; + int n; + + if( db_fd == -1 ) + open_db(); + if( DBG_TRUST && !no_io_dbg ) + log_debug("trustdb: read_record(%lu)\n", (ulong)recnum); + 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 ) { + if( DBG_TRUST ) + log_debug("trustdb: no record at %lu\n", (ulong)recnum ); + 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++; + rec->reserved = *p++; + switch( rec->rectype ) { + case 0: /* unused record */ + break; + case 1: /* version record */ + rec->r.version.magic[0] = *p++; + rec->r.version.magic[1] = *p++; + rec->r.version.version = *p++; + memcpy( rec->r.version.reserved, p, 3); p += 3; + rec->r.version.locked = buftou32(p); p += 4; + rec->r.version.created = buftou32(p); p += 4; + rec->r.version.modified = buftou32(p); p += 4; + rec->r.version.validated= buftou32(p); p += 4; + rec->r.version.local_id_counter = buftou32(p); p += 4; + rec->r.version.marginals_needed = *p++; + rec->r.version.completes_needed = *p++; + rec->r.version.max_cert_depth = *p++; + if( recnum ) { + log_error("%s: version record with recnum %lu\n", + db_name, (ulong)recnum ); + rc = G10ERR_TRUSTDB; + } + if( rec->reserved != 'g' || rec->r.version.magic[0] != '1' + || rec->r.version.magic[1] != '0' ) { + log_error("%s: not a trustdb file\n", db_name ); + rc = G10ERR_TRUSTDB; + } + if( rec->r.version.version != 1 ) { + log_error("%s: invalid file version %d\n", + db_name, rec->r.version.version ); + rc = G10ERR_TRUSTDB; + } + break; + case 2: + rec->r.pubkey.local_id = buftou32(p); p += 4; + rec->r.pubkey.keyid[0] = buftou32(p); p += 4; + rec->r.pubkey.keyid[1] = buftou32(p); p += 4; + rec->r.pubkey.algo = *p++; + rec->r.pubkey.reserved = *p++; + memcpy( rec->r.pubkey.fingerprint, p, 20); p += 20; + rec->r.pubkey.ownertrust = *p++; + if( rec->r.pubkey.local_id != recnum ) { + log_error("%s: pubkey local_id != recnum (%lu,%lu)\n", + db_name, + (ulong)rec->r.pubkey.local_id, + (ulong)recnum ); + rc = G10ERR_TRUSTDB; + } + break; + case 3: + rec->r.cache.local_id = buftou32(p); p += 4; + rec->r.cache.keyid[0] = buftou32(p); p += 4; + rec->r.cache.keyid[1] = buftou32(p); p += 4; + rec->r.cache.valid = *p++; + rec->r.cache.reserved = *p++; + memcpy(rec->r.cache.blockhash, p, 20); p += 20; + rec->r.cache.n_untrusted = *p++; + rec->r.cache.n_marginal = *p++; + rec->r.cache.n_fully = *p++; + rec->r.cache.trustlevel = *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( u32 recnum, TRUSTREC *rec ) +{ + byte buf[TRUST_RECORD_LEN], *p; + int rc = 0; + int n; + if( db_fd == -1 ) + open_db(); + if( DBG_TRUST && !no_io_dbg ) + log_debug("trustdb: write_record(%lu)\n", (ulong)recnum); + memset(buf, 0, TRUST_RECORD_LEN); + p = buf; + *p++ = rec->rectype; + *p++ = rec->reserved; + switch( rec->rectype ) { + case 0: /* unused record */ + break; + case 1: /* version record */ + log_bug(NULL); + break; + case 2: + u32tobuf(p, rec->r.pubkey.local_id); p += 4; + u32tobuf(p, rec->r.pubkey.keyid[0]); p += 4; + u32tobuf(p, rec->r.pubkey.keyid[1]); p += 4; + *p++ = rec->r.pubkey.algo; + *p++ = rec->r.pubkey.reserved; + memcpy( p, rec->r.pubkey.fingerprint, 20); p += 20; + *p++ = rec->r.pubkey.ownertrust; + assert( rec->r.pubkey.local_id == recnum ); + break; + case 3: + u32tobuf(p, rec->r.cache.local_id); p += 4; + u32tobuf(p, rec->r.cache.keyid[0]); p += 4; + u32tobuf(p, rec->r.cache.keyid[1]); p += 4; + *p++ = rec->r.cache.valid; + *p++ = rec->r.cache.reserved; + memcpy(p, rec->r.cache.blockhash, 20); p += 20; + *p++ = rec->r.cache.n_untrusted; + *p++ = rec->r.cache.n_marginal; + *p++ = rec->r.cache.n_fully; + *p++ = rec->r.cache.trustlevel; + break; + default: + log_bug(NULL); + } + 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; +} + +static u32 +new_local_id() +{ + off_t offset; + u32 recnum; + + /* 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 */ + return recnum ; +} + +/**************** + * Scan the trustdb for a record of type RECTYPE which maches PKC + * The local_id is set to the correct value + */ +static int +scan_record( PKT_public_cert *pkc, TRUSTREC *rec, int rectype ) +{ + u32 recnum; + u32 keyid[2]; + byte *fingerprint; + size_t fingerlen; + int dbg = DBG_TRUST; + int rc; + + assert( rectype == 2 || rectype == 3 ); + + if( DBG_TRUST ) + log_debug("trustdb: scan_record\n"); + keyid_from_pkc( pkc, keyid ); + fingerprint = fingerprint_from_pkc( pkc, &fingerlen ); + assert( fingerlen == 20 || fingerlen == 16 ); + + no_io_dbg = 1; + for(recnum=1; !(rc=read_record( recnum, rec)); recnum++ ) { + if( rec->rectype != rectype ) + continue; + if( rec->rectype == 2 ) { + if( rec->r.pubkey.keyid[0] == keyid[0] + && rec->r.pubkey.keyid[1] == keyid[1] + && rec->r.pubkey.algo == pkc->pubkey_algo + && !memcmp(rec->r.pubkey.fingerprint, fingerprint, fingerlen) + ) { /* found */ + /* store the local_id */ + 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; + no_io_dbg = 0; + return 0; + } + } + else + log_bug("not yet implemented\n"); + } + no_io_dbg = 0; + if( DBG_TRUST ) + log_debug("trustdb: scan_record: eof or error\n"); + if( rc != -1 ) + log_error("%s: scan_record failed: %s\n",db_name, g10_errstr(rc) ); + return rc; +} @@ -171,7 +434,150 @@ create_db( const char *fname ) ************* trust logic ******************* ***********************************************/ +/**************** + * Verify, that all our public keys are in the trustDB and marked as + * ultimately trusted. + */ +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]; + int trust; + while( !(rc=enum_secret_keys( &enum_context, skc) ) ) { + /* fixme: 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 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] ); + + /* look wether we can access the public key of this secret key */ + 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; + } + /* look into the trustdb */ + rc = check_pkc_trust( pkc, &trust ); + if( rc ) { + log_info("keyid %08lX: problem in trustdb: %s\n", (ulong)keyid[1], + g10_errstr(rc) ); + goto leave; + } + if( trust & TRUST_NO_PUBKEY ) { + log_info("keyid %08lX: not yet in trustdb\n", (ulong)keyid[1] ); + /* FIXME: insert */ + } + else if( (trust & TRUST_MASK) != TRUST_ULT_TRUST ) { + log_error("keyid %08lX: not marked as ultimately trusted\n", + (ulong)keyid[1] ); + /* FIXME: mark */ + } + + 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; +} + + + +/**************** + * Check all the sigs of the given keyblock and mark them + * as checked. + */ +static int +check_sigs( KBNODE keyblock ) +{ + KBNODE kbctx; + KBNODE node; + int rc; + + for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) { + if( node->pkt->pkttype == PKT_SIGNATURE + && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) { + PKT_signature *sig = node->pkt->pkt.signature; + + rc = check_key_signature( keyblock, node ); + if( !rc ) + node->flag |= 1; /* mark signature valid */ + if( DBG_TRUST ) + log_debug("trustdb: sig from %08lX: %s\n", + rc? g10_errstr(rc): "okay" ); + } + } + return 0; +} + + +/**************** + * Recursive check the signatures. + */ +static int +walk( KBNODE keyblock, int levels ) +{ + KBNODE kbctx, node; + + check_sigs( keyblock ); + if( levels ) { /* check the next level */ + for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) { + if( node->pkt->pkttype == PKT_SIGNATURE && (node->flag & 1) ) { + /* read the keyblock for this signator */ + + /* and check his signatures */ + /*walk( his_keyblock, levels-1)*/ + } + } + } + +} + + + + + +/**************** + * + * + * + * + * + * + * + * + * + * + */ +static int +check_trust() +{ +} @@ -181,11 +587,13 @@ create_db( const char *fname ) /**************** * Perform some checks over the trustdb - * level 0: used on initial program startup + * level 0: used for initial program startup */ int check_trustdb( int level ) { + int rc=0; + if( !level ) { char *fname = make_filename("~/.g10", "trustDB", NULL ); if( access( fname, R_OK ) ) { @@ -200,13 +608,22 @@ check_trustdb( int level ) db_name = fname; /* we can verify a signature about our local data (secring and trustdb) - * in ~/.g10/ here - */ + * in ~/.g10/ 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 wether there is no other ultimately trusted + * key in the database? */ + + } } else log_bug(NULL); - return 0; + return rc; } @@ -234,12 +651,171 @@ check_trustdb( int level ) int check_pkc_trust( PKT_public_cert *pkc, int *r_trustlevel ) { + TRUSTREC rec; int trustlevel = 0; + int rc=0; if( opt.verbose ) log_info("check_pkc_trust() called.\n"); + /* get the pubkey record */ + if( pkc->local_id ) { + if( read_record( pkc->local_id, &rec ) ) { + log_error("check_pkc_trust: read record failed\n"); + return G10ERR_TRUSTDB; + } + } + else { /* no local_id: scan the trustdb */ + if( (rc=scan_record( pkc, &rec, 2 )) && rc != -1 ) { + log_error("check_pkc_trust: scan_record(2) failed: %s\n", + g10_errstr(rc)); + return G10ERR_TRUSTDB; + } + else if( rc == -1 ) { + log_error("check_pkc_trust: pubkey not in TrustDB\n"); + trustlevel = TRUST_NO_PUBKEY; + goto leave; + } + } + /* fixme: do some additional checks on the pubkey record */ + + + leave: + if( opt.verbose ) + log_info("check_pkc_trust() returns trustlevel %04x.\n", trustlevel); *r_trustlevel = trustlevel; return 0; } + +/**************** + * Insert a trust record into the TrustDB + * This function failes if this record already exists. + */ +int +insert_trust_record( PKT_public_cert *pkc ) +{ + TRUSTREC rec; + u32 keyid[2]; + u32 recnum; + byte *fingerprint; + size_t fingerlen; + + + if( DBG_TRUST ) + log_debug("trustdb: insert_record\n"); + + assert( !pkc->local_id ); + + keyid_from_pkc( pkc, keyid ); + fingerprint = fingerprint_from_pkc( pkc, &fingerlen ); + + /* FIXME: check that we do not have this record. */ + + recnum = new_local_id(); + /* build record */ + memset( &rec, 0, sizeof rec ); + rec.rectype = 2; /* the pubkey record */ + rec.r.pubkey.local_id = recnum; + rec.r.pubkey.keyid[0] = keyid[0]; + rec.r.pubkey.keyid[1] = keyid[1]; + rec.r.pubkey.algo = pkc->pubkey_algo; + memcpy(rec.r.pubkey.fingerprint, fingerprint, fingerlen ); + rec.r.pubkey.ownertrust = 0; + if( write_record( recnum, &rec ) ) { + log_error("insert_trust_record: write failed\n"); + return G10ERR_TRUSTDB; + } + + pkc->local_id = recnum; + + return 0; +} + + +int +update_trust_record( PKT_public_cert *pkc, int new_trust ) +{ + TRUSTREC rec; + u32 keyid[2]; + u32 recnum; + + if( DBG_TRUST ) + log_debug("trustdb: update_record\n"); + + assert( pkc->local_id ); + + if( read_record( pkc->local_id, &rec ) ) { + log_error("update_trust_record: read failed\n"); + return G10ERR_TRUSTDB; + } + /* check keyid, fingerprint etc ? */ + + recnum = new_local_id(); + /* build record */ + memset( &rec, 0, sizeof rec ); + rec.rectype = 2; /* the pubkey record */ + rec.r.pubkey.local_id = recnum; + rec.r.pubkey.keyid[0] = keyid[0]; + rec.r.pubkey.keyid[1] = keyid[1]; + rec.r.pubkey.algo = pkc->pubkey_algo; + memcpy(rec.r.pubkey.fingerprint, fingerprint, fingerlen ); + rec.r.pubkey.ownertrust = 0; + if( write_record( recnum, &rec ) ) { + log_error("insert_trust_record: write failed\n"); + return G10ERR_TRUSTDB; + } + + pkc->local_id = recnum; + + return 0; +} + + +int +verify_private_data() +{ + int rc = 0; + char *sigfile = make_filename("~/.g10", "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("~/.g10", "sig", NULL ); + char *secring = make_filename("~/.g10", "secring.g10", 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; +} + diff --git a/g10/trustdb.h b/g10/trustdb.h index 01f126808..aff668e84 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -21,8 +21,22 @@ #ifndef G10_TRUSTDB_H #define G10_TRUSTDB_H + + +#define TRUST_MASK 0x07 /* for the trust leveles */ +#define TRUST_UNKNOWN 1 /* unknown */ +#define TRUST_NO_TRUST 2 /* not trusted */ +#define TRUST_MARG_TRUST 4 /* marginally trusted */ +#define TRUST_FULL_TRUST 5 /* fully trusted */ +#define TRUST_ULT_TRUST 7 /* ultimately trusted */ + /* other bits used with the trustlevel */ +#define TRUST_NO_PUBKEY 0x10 /* we do not have the pubkey in out trustDB */ + + /*-- trustdb.c --*/ int check_trustdb( int level ); int check_pkc_trust( PKT_public_cert *pkc, int *r_trustlevel ); +int verify_private_data(void); +int sign_private_data(void); #endif /*G10_TRUSTDB_H*/ diff --git a/include/util.h b/include/util.h index 34015ffe6..7f2593022 100644 --- a/include/util.h +++ b/include/util.h @@ -91,6 +91,7 @@ int answer_is_yes( const char *s ); /*-- strgutil.c --*/ void free_strlist( STRLIST sl ); #define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0) +void add_to_strlist( STRLIST *list, const char *string ); char *memistr( char *buf, size_t buflen, const char *sub ); char *trim_spaces( char *string ); int string_count_chr( const char *string, int c ); diff --git a/util/strgutil.c b/util/strgutil.c index ecdcb750c..cb80c57ab 100644 --- a/util/strgutil.c +++ b/util/strgutil.c @@ -37,6 +37,18 @@ free_strlist( STRLIST sl ) } } + +void +add_to_strlist( STRLIST *list, const char *string ) +{ + STRLIST sl; + + sl = m_alloc( sizeof *sl + strlen(string)); + strcpy(sl->d, string); + sl->next = *list; + *list = sl; +} + /**************** * look for the substring SUB in buffer and return a pointer to that * substring in BUF or NULL if not found.