diff options
Diffstat (limited to 'g10/getkey.c')
-rw-r--r-- | g10/getkey.c | 1641 |
1 files changed, 970 insertions, 671 deletions
diff --git a/g10/getkey.c b/g10/getkey.c index e9b4a231a..17dc6fafb 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -35,9 +35,12 @@ #include "trustdb.h" #include "i18n.h" + +#if 0 #define MAX_UNK_CACHE_ENTRIES 1000 /* we use a linked list - so I guess * this is a reasonable limit */ #define MAX_PK_CACHE_ENTRIES 50 +#endif #define MAX_UID_CACHE_ENTRIES 50 /* A map of the all characters valid used for word_match() @@ -99,7 +102,10 @@ struct getkey_ctx_s { int primary; KBNODE keyblock; KBPOS kbpos; + KBNODE found_key; /* pointer into some keyblock */ int last_rc; + int req_usage; + int req_algo; ulong count; int not_allocated; int nitems; @@ -119,13 +125,13 @@ static struct { } lkup_stats[21]; #endif +typedef struct keyid_list { + struct keyid_list *next; + u32 keyid[2]; +} *keyid_list_t; #if MAX_UNK_CACHE_ENTRIES - typedef struct keyid_list { - struct keyid_list *next; - u32 keyid[2]; - } *keyid_list_t; static keyid_list_t unknown_keyids; static int unk_cache_entries; /* number of entries in unknown keys cache */ static int unk_cache_disabled; @@ -147,7 +153,7 @@ static struct { #endif typedef struct user_id_db { struct user_id_db *next; - u32 keyid[2]; + keyid_list_t keyids; int len; char name[1]; } *user_id_db_t; @@ -157,9 +163,7 @@ static int uid_cache_entries; /* number of entries in uid cache */ static char* prepare_word_match( const byte *name ); -static int lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_kb ); -static int lookup_sk( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_kb ); -static u32 subkeys_expiretime( KBNODE node, u32 *mainkid ); +static int lookup( GETKEY_CTX ctx, KBNODE *ret_kb, int secmode ); #if 0 @@ -222,34 +226,96 @@ cache_public_key( PKT_public_key *pk ) #endif } +/* + * Return the user ID from the given keyblock. + * We use the primary uid flag which has been set by the merge_selfsigs + * function. The returned value is only valid as long as then given + * keyblock is not changed + */ +static const char * +get_primary_uid ( KBNODE keyblock, size_t *uidlen ) +{ + KBNODE k; + + for (k=keyblock; k; k=k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + && k->pkt->pkt.user_id->is_primary ) { + *uidlen = k->pkt->pkt.user_id->len; + return k->pkt->pkt.user_id->name; + } + } + *uidlen = 12; + return "[No user ID]"; +} + + +static void +release_keyid_list ( keyid_list_t k ) +{ + while ( k ) { + keyid_list_t k2 = k->next; + gcry_free (k); + k = k2; + } +} /**************** * Store the association of keyid and userid + * Feed only public keys to this function. */ void -cache_user_id( PKT_user_id *uid, u32 *keyid ) +cache_user_id( KBNODE keyblock ) { user_id_db_t r; + const char *uid; + size_t uidlen; + keyid_list_t keyids = NULL; + KBNODE k; - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - if( DBG_CACHE ) - log_debug("cache_user_id: already in cache\n"); - return; - } + for (k=keyblock; k; k = k->next ) { + if ( k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + keyid_list_t a = gcry_xcalloc ( 1, sizeof *a ); + /* Hmmm: For a long list of keyids it might be an advantage + * to append the keys */ + keyid_from_pk( k->pkt->pkt.public_key, a->keyid ); + /* first check for duplicates */ + for(r=user_id_db; r; r = r->next ) { + keyid_list_t b = r->keyids; + for ( b = r->keyids; b; b = b->next ) { + if( b->keyid[0] == a->keyid[0] + && b->keyid[1] == a->keyid[1] ) { + if( DBG_CACHE ) + log_debug("cache_user_id: already in cache\n"); + release_keyid_list ( keyids ); + gcry_free ( a ); + return; + } + } + } + /* now put it into the cache */ + a->next = keyids; + keyids = a; + } + } + if ( !keyids ) + BUG (); /* No key no fun */ + + + uid = get_primary_uid ( keyblock, &uidlen ); if( uid_cache_entries >= MAX_UID_CACHE_ENTRIES ) { /* fixme: use another algorithm to free some cache slots */ r = user_id_db; user_id_db = r->next; + release_keyid_list ( r->keyids ); gcry_free(r); uid_cache_entries--; } - r = gcry_xmalloc( sizeof *r + uid->len-1 ); - r->keyid[0] = keyid[0]; - r->keyid[1] = keyid[1]; - r->len = uid->len; - memcpy(r->name, uid->name, r->len); + r = gcry_xmalloc( sizeof *r + uidlen-1 ); + r->keyids = keyids; + r->len = uidlen; + memcpy(r->name, uid, r->len); r->next = user_id_db; user_id_db = r; uid_cache_entries++; @@ -288,6 +354,31 @@ getkey_disable_caches() } +static void +pk_from_block ( GETKEY_CTX ctx, + PKT_public_key *pk, KBNODE keyblock, const char *namehash ) +{ + KBNODE a = ctx->found_key ? ctx->found_key : keyblock; + + assert ( a->pkt->pkttype == PKT_PUBLIC_KEY + || a->pkt->pkttype == PKT_PUBLIC_SUBKEY ); + + copy_public_key_new_namehash( pk, a->pkt->pkt.public_key, namehash); +} + +static void +sk_from_block ( GETKEY_CTX ctx, + PKT_secret_key *sk, KBNODE keyblock ) +{ + KBNODE a = ctx->found_key ? ctx->found_key : keyblock; + + assert ( a->pkt->pkttype == PKT_SECRET_KEY + || a->pkt->pkttype == PKT_SECRET_SUBKEY ); + + copy_secret_key( sk, a->pkt->pkt.secret_key); +} + + /**************** * Get a public key and store it into the allocated pk * can be called with PK set to NULL to just read it into some @@ -329,14 +420,21 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) /* do a lookup */ { struct getkey_ctx_s ctx; + KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_pk( &ctx, pk, NULL ); + ctx.req_algo = pk->req_algo; + ctx.req_usage = pk->req_usage; + rc = lookup( &ctx, &kb, 0 ); + if ( !rc ) { + pk_from_block ( &ctx, pk, kb, NULL ); + } get_pubkey_end( &ctx ); + release_kbnode ( kb ); } if( !rc ) goto leave; @@ -374,7 +472,6 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) KBNODE get_pubkeyblock( u32 *keyid ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); struct getkey_ctx_s ctx; int rc = 0; KBNODE keyblock = NULL; @@ -385,8 +482,7 @@ get_pubkeyblock( u32 *keyid ) ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_pk( &ctx, pk, &keyblock ); - free_public_key(pk); + rc = lookup( &ctx, &keyblock, 0 ); get_pubkey_end( &ctx ); return rc ? NULL : keyblock; @@ -403,6 +499,7 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) { int rc; struct getkey_ctx_s ctx; + KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; @@ -410,8 +507,15 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_sk( &ctx, sk, NULL ); + ctx.req_algo = sk->req_algo; + ctx.req_usage = sk->req_usage; + rc = lookup( &ctx, &kb, 1 ); + if ( !rc ) { + sk_from_block ( &ctx, sk, kb ); + } get_seckey_end( &ctx ); + release_kbnode ( kb ); + if( !rc ) { /* check the secret key (this may prompt for a passprase to * unlock the secret key @@ -424,30 +528,6 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) /**************** - * Get the primary secret key and store it into sk - * Note: This function does not unprotect the key! - */ -int -get_primary_seckey( PKT_secret_key *sk, u32 *keyid ) -{ - struct getkey_ctx_s ctx; - int rc; - - memset( &ctx, 0, sizeof ctx ); - ctx.not_allocated = 1; - ctx.primary = 1; - ctx.nitems = 1; - ctx.items[0].mode = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_sk( &ctx, sk, NULL ); - get_seckey_end( &ctx ); - return rc; -} - - - -/**************** * Check whether the secret key is available * Returns: 0 := key is available * GPGERR_NO_SECKEY := not availabe @@ -457,18 +537,17 @@ seckey_available( u32 *keyid ) { int rc; struct getkey_ctx_s ctx; - PKT_secret_key *sk; + KBNODE kb = NULL; - sk = gcry_xcalloc( 1, sizeof *sk ); memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_sk( &ctx, sk, NULL ); + rc = lookup( &ctx, &kb, 1 ); get_seckey_end( &ctx ); - free_secret_key( sk ); + release_kbnode ( kb ); return rc; } @@ -612,7 +691,8 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, hexlength = 0; /* a hex number, but really were not. */ } - if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0')){ + if (hexlength == 8 + || (!hexprefix && hexlength == 9 && *s == '0')){ /* short keyid */ if (hexlength == 9) s++; @@ -622,8 +702,8 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, } mode = 10; } - else if (hexlength == 16 || (!hexprefix && hexlength == 17 - && *s == '0')) { + else if (hexlength == 16 + || (!hexprefix && hexlength == 17 && *s == '0')) { /* complete keyid */ char buf[9]; if (hexlength == 17) @@ -701,10 +781,10 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, int n; STRLIST r; GETKEY_CTX ctx; - + KBNODE help_kb = NULL; + if( retctx ) /* reset the returned context in case of error */ *retctx = NULL; - assert( !pk ^ !sk ); /* build the search context */ /* Performance hint: Use a static buffer if there is only one name */ @@ -732,10 +812,25 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, /* and call the lookup function */ ctx->primary = 1; /* we want to look for the primary key only */ - if( sk ) - rc = lookup_sk( ctx, sk, ret_kb ); - else - rc = lookup_pk( ctx, pk, ret_kb ); + + if ( !ret_kb ) + ret_kb = &help_kb; + + if( sk ) { + rc = lookup( ctx, ret_kb, 1 ); + if ( !rc && sk ) { + sk_from_block ( ctx, sk, *ret_kb ); + } + } + else { + + rc = lookup( ctx, ret_kb, 0 ); + if ( !rc && pk ) { + pk_from_block ( ctx, pk, *ret_kb, NULL /* FIXME need to get the namehash*/ ); + } + } + + release_kbnode ( help_kb ); if( retctx ) /* caller wants the context */ *retctx = ctx; @@ -758,16 +853,7 @@ get_pubkey_byname( GETKEY_CTX *retctx, PKT_public_key *pk, STRLIST namelist = NULL; add_to_strlist( &namelist, name ); - - if( !pk ) { - /* Performance Hint: key_byname should not need a pk here */ - pk = gcry_xcalloc( 1, sizeof *pk ); - rc = key_byname( retctx, namelist, pk, NULL, ret_keyblock ); - free_public_key( pk ); - } - else - rc = key_byname( retctx, namelist, pk, NULL, ret_keyblock ); - + rc = key_byname( retctx, namelist, pk, NULL, ret_keyblock ); free_strlist( namelist ); return rc; } @@ -776,18 +862,7 @@ int get_pubkey_bynames( GETKEY_CTX *retctx, PKT_public_key *pk, STRLIST names, KBNODE *ret_keyblock ) { - int rc; - - if( !pk ) { - /* Performance Hint: key_byname should not need a pk here */ - pk = gcry_xcalloc( 1, sizeof *pk ); - rc = key_byname( retctx, names, pk, NULL, ret_keyblock ); - free_public_key( pk ); - } - else - rc = key_byname( retctx, names, pk, NULL, ret_keyblock ); - - return rc; + return key_byname( retctx, names, pk, NULL, ret_keyblock ); } int @@ -795,14 +870,10 @@ get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) { int rc; - if( !pk ) { - /* Performance Hint: lookup_read should not need a pk in this case */ - pk = gcry_xcalloc( 1, sizeof *pk ); - rc = lookup_pk( ctx, pk, ret_keyblock ); - free_public_key( pk ); - } - else - rc = lookup_pk( ctx, pk, ret_keyblock ); + rc = lookup( ctx, ret_keyblock, 0 ); + if ( !rc && pk && ret_keyblock ) + pk_from_block ( ctx, pk, *ret_keyblock, NULL ); + return rc; } @@ -824,18 +895,24 @@ get_pubkey_end( GETKEY_CTX ctx ) * Search for a key with the given fingerprint. */ int -get_pubkey_byfprint( PKT_public_key *pk, const byte *fprint, size_t fprint_len) +get_pubkey_byfprint( PKT_public_key *pk, + const byte *fprint, size_t fprint_len) { int rc; if( fprint_len == 20 || fprint_len == 16 ) { struct getkey_ctx_s ctx; + KBNODE kb = NULL; + memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = fprint_len; memcpy( ctx.items[0].fprint, fprint, fprint_len ); - rc = lookup_pk( &ctx, pk, NULL ); + rc = lookup( &ctx, &kb, 0 ); + if (!rc && pk ) + pk_from_block ( &ctx, pk, kb, NULL ); + release_kbnode ( kb ); get_pubkey_end( &ctx ); } else @@ -852,35 +929,33 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, size_t fprint_len ) { int rc; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); if( fprint_len == 20 || fprint_len == 16 ) { struct getkey_ctx_s ctx; + memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = fprint_len; memcpy( ctx.items[0].fprint, fprint, fprint_len ); - rc = lookup_pk( &ctx, pk, ret_keyblock ); + rc = lookup( &ctx, ret_keyblock, 0 ); get_pubkey_end( &ctx ); } else rc = GPGERR_GENERAL; /* Oops */ - free_public_key( pk ); return rc; } /**************** - * Search for a key with the given lid and return the complete keyblock + * Search for a key with the given lid and return the entire keyblock */ int get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ) { int rc; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); struct getkey_ctx_s ctx; u32 kid[2]; @@ -892,10 +967,9 @@ get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ) ctx.items[0].mode = 12; ctx.items[0].keyid[0] = kid[0]; ctx.items[0].keyid[1] = kid[1]; - rc = lookup_pk( &ctx, pk, ret_keyblock ); + rc = lookup( &ctx, ret_keyblock, 0 ); get_pubkey_end( &ctx ); - free_public_key( pk ); return rc; } @@ -919,13 +993,17 @@ get_seckey_byname( PKT_secret_key *sk, const char *name, int unprotect ) } else if( !name ) { /* use the first one as default key */ struct getkey_ctx_s ctx; + KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.primary = 1; ctx.nitems = 1; ctx.items[0].mode = 15; - rc = lookup_sk( &ctx, sk, NULL ); + rc = lookup( &ctx, &kb, 1 ); + if (!rc && sk ) + sk_from_block ( &ctx, sk, kb ); + release_kbnode ( kb ); get_seckey_end( &ctx ); } else { @@ -945,18 +1023,7 @@ int get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk, STRLIST names, KBNODE *ret_keyblock ) { - int rc; - - if( !sk ) { - /* Performance Hint: key_byname should not need a sk here */ - sk = gcry_xcalloc_secure( 1, sizeof *sk ); - rc = key_byname( retctx, names, NULL, sk, ret_keyblock ); - free_secret_key( sk ); - } - else - rc = key_byname( retctx, names, NULL, sk, ret_keyblock ); - - return rc; + return key_byname( retctx, names, NULL, sk, ret_keyblock ); } @@ -965,32 +1032,24 @@ get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ) { int rc; - if( !sk ) { - /* Performance Hint: lookup_read should not need a pk in this case */ - sk = gcry_xcalloc_secure( 1, sizeof *sk ); - rc = lookup_sk( ctx, sk, ret_keyblock ); - free_secret_key( sk ); - } - else - rc = lookup_sk( ctx, sk, ret_keyblock ); + rc = lookup( ctx, ret_keyblock, 1 ); + if ( !rc && sk && ret_keyblock ) + sk_from_block ( ctx, sk, *ret_keyblock ); + return rc; } void get_seckey_end( GETKEY_CTX ctx ) { - if( ctx ) { - int n; - - enum_keyblocks( 2, &ctx->kbpos, NULL ); /* close */ - for(n=0; n < ctx->nitems; n++ ) - gcry_free( ctx->items[n].namebuf ); - if( !ctx->not_allocated ) - gcry_free( ctx ); - } + get_pubkey_end( ctx ); } + +/******************************************************* + ************** compare functions ********************** + *******************************************************/ /**************** * Do a word match (original user id starts with a '+'). @@ -1130,73 +1189,15 @@ compare_name( const char *uid, size_t uidlen, const char *name, int mode ) -/**************** - * Assume that knode points to a public key packet and keyblock is - * the entire keyblock. This function adds all relevant information from - * a selfsignature to the public key. - */ - -static void -merge_one_pk_and_selfsig( KBNODE keyblock, KBNODE knode, - PKT_public_key *orig_pk ) -{ - PKT_public_key *pk = knode->pkt->pkt.public_key; - PKT_signature *sig; - KBNODE k; - u32 kid[2]; - u32 sigdate = 0; - - assert( knode->pkt->pkttype == PKT_PUBLIC_KEY - || knode->pkt->pkttype == PKT_PUBLIC_SUBKEY ); - - if( pk->version < 4 ) - return; /* this is only needed for version >=4 packets */ - - - /* find the selfsignature */ - if( knode->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - k = find_kbnode( keyblock, PKT_PUBLIC_KEY ); - if( !k ) - BUG(); /* keyblock without primary key!!! */ - keyid_from_pk( k->pkt->pkt.public_key, kid ); - } - else - keyid_from_pk( pk, kid ); - - for(k=knode->next; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SIGNATURE - && (sig=k->pkt->pkt.signature)->sig_class >= 0x10 - && sig->sig_class <= 0x30 - && sig->keyid[0] == kid[0] - && sig->keyid[1] == kid[1] - && sig->version > 3 ) { - /* okay this is a self-signature which can be used. - * We use the latest self-signature. - * FIXME: We should only use this if the signature is valid - * but this is time consuming - we must provide another - * way to handle this - */ - const byte *p; - u32 ed; - - p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL ); - ed = p? pk->timestamp + buffer_to_u32(p):0; - /* use the latest self signature */ - if( sig->timestamp > sigdate ) { - pk->expiredate = ed; - orig_pk->expiredate = ed; - sigdate = sig->timestamp; - } - /* fixme: add usage etc. to pk */ - } - else if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* stop here */ - } -} - + +/************************************************ + ************* Merging stuff ******************** + ************************************************/ /**************** * merge all selfsignatures with the keys. + * FIXME: replace this at least for the public key parts + * by merge_selfsigs */ void merge_keys_and_selfsig( KBNODE keyblock ) @@ -1218,7 +1219,7 @@ merge_keys_and_selfsig( KBNODE keyblock ) keyid_from_pk( pk, kid ); else if( !pk->expiredate ) { /* and subkey */ /* insert the expiration date here */ - pk->expiredate = subkeys_expiretime( k, kid ); + /*FIXME!!! pk->expiredate = subkeys_expiretime( k, kid );*/ } sigdate = 0; } @@ -1266,510 +1267,850 @@ merge_keys_and_selfsig( KBNODE keyblock ) } -static KBNODE -find_by_name( KBNODE keyblock, PKT_public_key *pk, const char *name, - int mode, byte *namehash, int *use_namehash ) +static void +fixup_uidnode ( KBNODE uidnode, KBNODE signode ) { - KBNODE k, kk; + PKT_user_id *uid = uidnode->pkt->pkt.user_id; + PKT_signature *sig = signode->pkt->pkt.signature; + const byte *p; + size_t n; + + uid->created = 0; /* not created == invalid */ + if ( !signode ) + return; /* no self-signature */ + if ( IS_UID_REV ( sig ) ) + return; /* has been revoked */ + + uid->created = sig->timestamp; /* this one is okay */ + + + /* store the key flags in the helper variable for later processing */ + uid->help_key_usage = 0; + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + if ( p && n ) { + /* first octet of the keyflags */ + if ( (*p & 3) ) + uid->help_key_usage |= GCRY_PK_USAGE_SIGN; + if ( (*p & 12) ) + uid->help_key_usage |= GCRY_PK_USAGE_ENCR; + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_USER_ID - && !compare_name( k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len, name, mode)) { - /* we found a matching name, look for the key */ - for(kk=keyblock; kk; kk = kk->next ) { - if( ( kk->pkt->pkttype == PKT_PUBLIC_KEY - || kk->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - && ( !pk->pubkey_algo - || pk->pubkey_algo - == kk->pkt->pkt.public_key->pubkey_algo) - && ( !pk->pubkey_usage - || !openpgp_pk_test_algo( - kk->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage )) - ) - break; - } - if( kk ) { - u32 aki[2]; - keyid_from_pk( kk->pkt->pkt.public_key, aki ); - cache_user_id( k->pkt->pkt.user_id, aki ); - if( k->pkt->pkt.user_id->photo ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, - k->pkt->pkt.user_id->photo, - k->pkt->pkt.user_id->photolen ); - } - else { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, - k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len ); - } - *use_namehash = 1; - return kk; - } - else if( is_RSA(pk->pubkey_algo) ) - log_error(_("RSA key cannot be used in this version\n")); - else - log_error(_("No key for user ID\n")); - } + /* ditto or the key expiration */ + uid->help_key_expire = 0; + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) { + uid->help_key_expire = sig->timestamp + buffer_to_u32(p); } - return NULL; + + /* Set the primary user ID flag - we will later wipe out some + * of them to only have one in out keyblock */ + uid->is_primary = 0; + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_PRIMARY_UID, NULL ); + if ( p && *p ) + uid->is_primary = 1; + /* We could also query this from the unhashed area if it is not in + * the hased area and then later try to decide which is the better + * there should be no security problem with this. + * For now we only look at the hashed one. + */ } -static KBNODE -find_by_name_sk( KBNODE keyblock, PKT_secret_key *sk, const char *name, - int mode ) +static void +merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) { - KBNODE k, kk; + PKT_public_key *pk = NULL; + KBNODE k; + u32 kid[2]; + u32 sigdate = 0, uiddate=0, uiddate2; + KBNODE signode, uidnode, uidnode2; + u32 curtime = make_timestamp (); + unsigned int key_usage = 0; + u32 key_expire = 0; + int key_expire_seen = 0; + + *r_revoked = 0; + if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) + BUG (); + pk = keyblock->pkt->pkt.public_key; + pk->created = 0; + keyid_from_pk( pk, kid ); + pk->main_keyid[0] = kid[0]; + pk->main_keyid[1] = kid[1]; + + if ( pk->version < 4 ) + return; /* nothing to do for old keys FIXME: This is wrong!!!!*/ + + /* first pass: find the latest direct key self-signature. + * We assume that the newest one overrides all others + */ + signode = NULL; + sigdate = 0; /* helper to find the latest signature */ + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { + if ( k->pkt->pkttype == PKT_SIGNATURE ) { + PKT_signature *sig = k->pkt->pkt.signature; + if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) { + if ( check_key_signature( keyblock, k, NULL ) ) + ; /* signature did not verify */ + else if ( IS_KEY_REV (sig) ){ + /* key has been revoked - there is no way to override + * such a revocation, so we can stop now. + * we can't cope with expiration times for revocations + * here because we have to assumethat an attacker can + * generate all kinds of signatures. + */ + *r_revoked = 1; + return; + } + else if ( IS_KEY_SIG (sig) && sig->timestamp >= sigdate ) { + const byte *p; + + p = parse_sig_subpkt( sig->hashed_data, + SIGSUBPKT_SIG_EXPIRE, NULL ); + if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + ; /* signature has expired - ignore it */ + else { + sigdate = sig->timestamp; + signode = k; + } + } + } + } + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_USER_ID - && !compare_name( k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len, name, mode)) { - /* we found a matching name, look for the key */ - for(kk=keyblock; kk; kk = kk->next ) { - if( ( kk->pkt->pkttype == PKT_SECRET_KEY - || kk->pkt->pkttype == PKT_SECRET_SUBKEY ) - && ( !sk->pubkey_algo - || sk->pubkey_algo - == kk->pkt->pkt.secret_key->pubkey_algo) - && ( !sk->pubkey_usage - || !openpgp_pk_test_algo( - kk->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage )) - ) - break; - } - if( kk ) { - u32 aki[2]; - keyid_from_sk( kk->pkt->pkt.secret_key, aki ); - cache_user_id( k->pkt->pkt.user_id, aki ); - return kk; - } - else if( is_RSA(sk->pubkey_algo) ) - log_error(_("RSA key cannot be used in this version\n")); - else - log_error(_("No key for user ID\n")); - } + if ( signode ) { + /* some information from a direct key signature take precedence + * over the same information given in UID sigs. + */ + PKT_signature *sig = signode->pkt->pkt.signature; + const byte *p; + size_t n; + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + if ( p && n ) { + /* first octet of the keyflags */ + if ( (*p & 3) ) + key_usage |= GCRY_PK_USAGE_SIGN; + if ( (*p & 12) ) + key_usage |= GCRY_PK_USAGE_ENCR; + } + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) { + key_expire = sig->timestamp + buffer_to_u32(p); + key_expire_seen = 1; + } + + /* and set the created field */ + pk->created = sigdate; + /* and mark that key as valid: one direct key signature should + * render a key as valid */ + pk->is_valid = 1; } - return NULL; + + + /* second pass: look at the self-signature of all user IDs */ + signode = uidnode = NULL; + sigdate = 0; /* helper to find the latest signature in one user ID */ + uiddate = 0; /* and over of all user IDs */ + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + if ( uidnode ) + fixup_uidnode ( uidnode, signode ); + uidnode = k; + signode = NULL; + if ( sigdate > uiddate ) + uiddate = sigdate; + sigdate = 0; + } + else if ( k->pkt->pkttype == PKT_SIGNATURE && uidnode ) { + PKT_signature *sig = k->pkt->pkt.signature; + if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) { + if ( check_key_signature( keyblock, k, NULL ) ) + ; /* signature did not verify */ + else if ( IS_UID_SIG (sig) || IS_UID_REV (sig)) { + /* Note: we allow to invalidated cert revocations + * by a newer signature. An attacker can't use this + * because a key should be revoced with a key revocation. + * The reason why we have to allow for that is that at + * one time an email address may become invalid but later + * the same email address may become valid again (hired, + * fired, hired again). + */ + const byte *p; + + p = parse_sig_subpkt( sig->hashed_data, + SIGSUBPKT_SIG_EXPIRE, NULL ); + if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + ; /* signature/revocation has expired - ignore it */ + else { + sigdate = sig->timestamp; + signode = k; + } + } + } + } + } + if ( uidnode ) { + fixup_uidnode ( uidnode, signode ); + pk->is_valid = 1; + } + if ( sigdate > uiddate ) + uiddate = sigdate; + /* if we do not have a direct key signature, take the key creation date + * from the latest user ID. Hmmm, another possibilty would be to take + * it from the latest primary user ID - but we don't implement it for + * now */ + if ( !pk->created ) + pk->created = uiddate; + if ( !pk->created ) { + /* oops, still no creation date: use the timestamp */ + if (DBG_CACHE) + log_debug( "merge_selfsigs_main: " + "using timestamp as creation date\n"); + pk->created = pk->timestamp; + } + + /* Now that we had a look at all user IDs we can now get some information + * from those user IDs. + */ + + if ( !key_usage ) { + /* find the latest user ID with key flags set */ + uiddate = 0; /* helper to find the latest user ID */ + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( uid->help_key_usage && uid->created > uiddate ) { + key_usage = uid->help_key_usage; + uiddate = uid->created; + } + } + } + } + if ( !key_usage ) { /* no key flags at all: get it from the algo */ + key_usage = openpgp_pk_algo_usage ( pk->pubkey_algo ); + } + else { /* check that the usage matches the usage as given by the algo */ + int x = openpgp_pk_algo_usage ( pk->pubkey_algo ); + if ( x ) /* mask it down to the actual allowed usage */ + key_usage &= x; + } + pk->pubkey_usage = key_usage; + + + if ( !key_expire_seen ) { + /* find the latest valid user ID with a key expiration set + * Note, that this may be a diferent one from the above because + * some user IDs may have no expiration date set */ + uiddate = 0; + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( uid->help_key_expire && uid->created > uiddate ) { + key_expire = uid->help_key_expire; + uiddate = uid->created; + } + } + } + } + if ( key_expire >= curtime ) + pk->has_expired = key_expire; + /* FIXME: we should see how to get rid of the expiretime fields */ + + + /* and now find the real primary user ID and delete all others */ + uiddate = uiddate2 = 0; + uidnode = uidnode2 = NULL; + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( uid->is_primary && uid->created > uiddate ) { + uiddate = uid->created; + uidnode = k; + } + if ( !uid->is_primary && uid->created > uiddate2 ) { + uiddate2 = uid->created; + uidnode2 = k; + } + } + } + if ( uidnode ) { + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( k != uidnode ) + uid->is_primary = 0; + } + } + } + else if( uidnode2 ) { + /* none is flagged primary - use the latest user ID we have */ + uidnode2->pkt->pkt.user_id->is_primary = 1; + } + } -static KBNODE -find_by_keyid( KBNODE keyblock, PKT_public_key *pk, u32 *keyid, int mode ) +static void +merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) { + PKT_public_key *mainpk = NULL, *subpk = NULL; + PKT_signature *sig; KBNODE k; + u32 mainkid[2]; + u32 sigdate = 0; + KBNODE signode; + u32 curtime = make_timestamp (); + unsigned int key_usage = 0; + u32 key_expire = 0; + const byte *p; + size_t n; + + if ( subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY ) + BUG (); + mainpk = keyblock->pkt->pkt.public_key; + if ( mainpk->version < 4 ) + return; /* (actually this should never happen) */ + keyid_from_pk( mainpk, mainkid ); + subpk = subnode->pkt->pkt.public_key; + subpk->is_valid = 0; + subpk->main_keyid[0] = mainpk->main_keyid[0]; + subpk->main_keyid[1] = mainpk->main_keyid[1]; + if ( subpk->version < 4 ) + return; /* there are no v3 subkeys */ + + /* find the latest key binding self-signature. */ + signode = NULL; + sigdate = 0; /* helper to find the latest signature */ + for(k=subnode->next; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_SIGNATURE ) { + sig = k->pkt->pkt.signature; + if ( sig->keyid[0] == mainkid[0] && sig->keyid[1]==mainkid[1] ) { + if ( check_key_signature( keyblock, k, NULL ) ) + ; /* signature did not verify */ + else if ( IS_SUBKEY_REV (sig) ) { + /* key has been revoked - given the fact that it is easy + * to create a new subkey, it does not make sense to + * revive a revoked key. So we can stop here. + */ + subpk->is_revoked = 1; + return; + } + else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate ) { + p = parse_sig_subpkt( sig->hashed_data, + SIGSUBPKT_SIG_EXPIRE, NULL ); + if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + ; /* signature has expired - ignore it */ + else { + sigdate = sig->timestamp; + signode = k; + } + } + } + } + } - if( DBG_CACHE ) - log_debug("lookup keyid=%08lx%08lx req_algo=%d mode=%d\n", - (ulong)keyid[0], (ulong)keyid[1], pk->pubkey_algo, mode ); + if ( !signode ) { + subpk->created = subpk->timestamp; + return; /* no valid key binding */ + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - u32 aki[2]; - keyid_from_pk( k->pkt->pkt.public_key, aki ); - if( DBG_CACHE ) - log_debug(" aki=%08lx%08lx algo=%d\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.public_key->pubkey_algo ); - - if( aki[1] == keyid[1] - && ( mode == 10 || aki[0] == keyid[0] ) - && ( !pk->pubkey_algo - || pk->pubkey_algo - == k->pkt->pkt.public_key->pubkey_algo) ){ - KBNODE kk; - /* cache the userid */ - for(kk=keyblock; kk; kk = kk->next ) - if( kk->pkt->pkttype == PKT_USER_ID ) - break; - if( kk ) - cache_user_id( kk->pkt->pkt.user_id, aki ); - else - log_error(_("No user ID for key\n")); - return k; /* found */ - } - } + subpk->is_valid = 1; + subpk->created = sigdate; + sig = signode->pkt->pkt.signature; + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + if ( p && n ) { + /* first octet of the keyflags */ + if ( (*p & 3) ) + key_usage |= GCRY_PK_USAGE_SIGN; + if ( (*p & 12) ) + key_usage |= GCRY_PK_USAGE_ENCR; } - return NULL; + if ( !key_usage ) { /* no key flags at all: get it from the algo */ + key_usage = openpgp_pk_algo_usage ( subpk->pubkey_algo ); + } + else { /* check that the usage matches the usage as given by the algo */ + int x = openpgp_pk_algo_usage ( subpk->pubkey_algo ); + if ( x ) /* mask it down to the actual allowed usage */ + key_usage &= x; + } + subpk->pubkey_usage = key_usage; + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) + key_expire = sig->timestamp + buffer_to_u32(p); + else + key_expire = 0; + subpk->has_expired = key_expire >= curtime? key_expire : 0; } -static KBNODE -find_by_keyid_sk( KBNODE keyblock, PKT_secret_key *sk, u32 *keyid, int mode ) + + +/* + * Merge information from the self-signatures with the key, so that + * we can later use them more easy. + * The function works by first applying the self signatures to the + * primary key and the to each subkey. + * Here are the rules we use to decide which inormation from which + * self-signature is used: + * We check all self signatures or validity and ignore all invalid signatures. + * All signatures are then ordered by their creation date .... + * For the primary key: + * FIXME the docs + */ +static void +merge_selfsigs( KBNODE keyblock ) { KBNODE k; + int revoked; + PKT_public_key *main_pk; + + if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) + BUG (); + + merge_selfsigs_main ( keyblock, &revoked ); + main_pk = keyblock->pkt->pkt.public_key; + if ( revoked ) { + /* if the primary key has been revoked we better set the revoke + * flag on that key and all subkeys */ + for(k=keyblock; k; k = k->next ) { + if ( k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + PKT_public_key *pk = k->pkt->pkt.public_key; + pk->is_revoked = 1; + pk->main_keyid[0] = main_pk->main_keyid[0]; + pk->main_keyid[1] = main_pk->main_keyid[1]; + } + } + return; + } - if( DBG_CACHE ) - log_debug("lookup_sk keyid=%08lx%08lx req_algo=%d mode=%d\n", - (ulong)keyid[0], (ulong)keyid[1], sk->pubkey_algo, mode ); - + /* now merge in the data from each of the subkeys */ for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ) { - u32 aki[2]; - keyid_from_sk( k->pkt->pkt.secret_key, aki ); - if( DBG_CACHE ) - log_debug(" aki=%08lx%08lx algo=%d\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.secret_key->pubkey_algo ); - - if( aki[1] == keyid[1] - && ( mode == 10 || aki[0] == keyid[0] ) - && ( !sk->pubkey_algo - || sk->pubkey_algo - == k->pkt->pkt.secret_key->pubkey_algo) ){ - KBNODE kk; - /* cache the userid */ - for(kk=keyblock; kk; kk = kk->next ) - if( kk->pkt->pkttype == PKT_USER_ID ) - break; - if( kk ) - cache_user_id( kk->pkt->pkt.user_id, aki ); - else - log_error(_("No user ID for key\n")); - return k; /* found */ - } - } + if ( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + merge_selfsigs_subkey ( keyblock, k ); + } } - return NULL; } -static KBNODE -find_first( KBNODE keyblock, PKT_public_key *pk ) +/* + * Merge the secret keys from secblock into the pubblock thereby + * replacing the public (sub)keys with their secret counterparts Hmmm: + * It might be better to get away from the concept of entire secret + * keys at all and have a way to store just the real secret parts + * from the key. + */ +static void +merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) { - KBNODE k; + KBNODE pub; + int deleting = 0; + int any_deleted = 0; + + assert ( pubblock->pkt->pkttype == PKT_PUBLIC_KEY ); + assert ( secblock->pkt->pkttype == PKT_SECRET_KEY ); + + for (pub=pubblock; pub; pub = pub->next ) { + if ( pub->pkt->pkttype == PKT_PUBLIC_KEY ) { + PKT_public_key *pk = pub->pkt->pkt.public_key; + PKT_secret_key *sk = secblock->pkt->pkt.secret_key; + assert ( pub == pubblock ); /* only in the first node */ + /* there is nothing to compare in this case, so just replace + * some information */ + copy_public_parts_to_secret_key ( pk, sk ); + free_public_key ( pk ); + pub->pkt->pkttype = PKT_SECRET_KEY; + pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk); + } + else if ( pub->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + KBNODE sec; + PKT_public_key *pk = pub->pkt->pkt.public_key; + + deleting = 0; + /* this is more complicated: it may happen that the sequence + * of the subkeys dosn't match, so we have to find the + * appropriate secret key */ + for (sec=secblock->next; sec; sec = sec->next ) { + if ( sec->pkt->pkttype == PKT_SECRET_SUBKEY ) { + PKT_secret_key *sk = sec->pkt->pkt.secret_key; + if ( !cmp_public_secret_key ( pk, sk ) ) { + copy_public_parts_to_secret_key ( pk, sk ); + free_public_key ( pk ); + pub->pkt->pkttype = PKT_SECRET_KEY; + pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk); + break; + } + } + } + if ( !sec ) { + log_error ( "no corresponding secret subkey " + "for public subkey - removing\n" ); + /* better remove the public subkey in this case */ + delete_kbnode ( pub ); + deleting = 1; + any_deleted = 1; + } + } + else if ( deleting ) { + delete_kbnode (pub); + } + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - { - if( !pk->pubkey_algo - || pk->pubkey_algo == k->pkt->pkt.public_key->pubkey_algo ) - return k; - } + if ( any_deleted ) { + /* because we have not deleted the root node, we don't need to + * update the pubblock */ + pub = pubblock; + commit_kbnode ( &pubblock ); + assert ( pub == pubblock ); } - return NULL; } -static KBNODE -find_first_sk( KBNODE keyblock, PKT_secret_key *sk ) + + + +/************************************************ + ************* Find stuff *********************** + ************************************************/ + +static int +find_by_name( KBNODE keyblock, const char *name, + int mode, byte *namehash ) { KBNODE k; for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ) - { - if( !sk->pubkey_algo - || sk->pubkey_algo == k->pkt->pkt.secret_key->pubkey_algo ) - return k; - } + if( k->pkt->pkttype == PKT_USER_ID + && !compare_name( k->pkt->pkt.user_id->name, + k->pkt->pkt.user_id->len, name, mode)) { + /* we found a matching name, look for the key */ + if( k->pkt->pkt.user_id->photo ) { + /* oops: this can never happen */ + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, + k->pkt->pkt.user_id->photo, + k->pkt->pkt.user_id->photolen ); + } + else { + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, + k->pkt->pkt.user_id->name, + k->pkt->pkt.user_id->len ); + } + return 1; + } } - return NULL; + + return 0; } + static KBNODE -find_by_fpr( KBNODE keyblock, PKT_public_key *pk, const char *name, int mode ) +find_by_keyid( KBNODE keyblock, u32 *keyid, int mode ) { KBNODE k; for(k=keyblock; k; k = k->next ) { if( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - byte afp[MAX_FINGERPRINT_LEN]; - size_t an; - - fingerprint_from_pk(k->pkt->pkt.public_key, afp, &an ); - - if( DBG_CACHE ) { - u32 aki[2]; - keyid_from_pk( k->pkt->pkt.public_key, aki ); - log_debug(" aki=%08lx%08lx algo=%d mode=%d an=%u\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.public_key->pubkey_algo, mode, - (unsigned)an ); + u32 aki[2]; + keyid_from_pk( k->pkt->pkt.public_key, aki ); + if( aki[1] == keyid[1] && ( mode == 10 || aki[0] == keyid[0] ) ) { + return k; /* found */ } - - if( an == mode - && !memcmp( afp, name, an) - && ( !pk->pubkey_algo - || pk->pubkey_algo == k->pkt->pkt.public_key->pubkey_algo) ) - return k; } } return NULL; } + + static KBNODE -find_by_fpr_sk( KBNODE keyblock, PKT_secret_key *sk, - const char *name, int mode ) +find_by_fpr( KBNODE keyblock, const char *name, int mode ) { KBNODE k; for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ) { + if( k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { byte afp[MAX_FINGERPRINT_LEN]; size_t an; - fingerprint_from_sk(k->pkt->pkt.secret_key, afp, &an ); - - if( DBG_CACHE ) { - u32 aki[2]; - keyid_from_sk( k->pkt->pkt.secret_key, aki ); - log_debug(" aki=%08lx%08lx algo=%d mode=%d an=%u\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.secret_key->pubkey_algo, mode, - (unsigned)an ); - } - - if( an == mode - && !memcmp( afp, name, an) - && ( !sk->pubkey_algo - || sk->pubkey_algo == k->pkt->pkt.secret_key->pubkey_algo) ) - return k; + fingerprint_from_pk(k->pkt->pkt.public_key, afp, &an ); + if( an == mode && !memcmp( afp, name, an) ) { + return k; + } } } return NULL; } -/**************** - * Return the expiretime of a subkey. - */ -static u32 -subkeys_expiretime( KBNODE node, u32 *mainkid ) -{ - KBNODE k; - PKT_signature *sig; - u32 expires = 0, sigdate = 0; - - assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ); - for(k=node->next; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SIGNATURE - && (sig=k->pkt->pkt.signature)->sig_class == 0x18 - && sig->keyid[0] == mainkid[0] - && sig->keyid[1] == mainkid[1] - && sig->version > 3 - && sig->timestamp > sigdate ) { - /* okay this is a key-binding which can be used. - * We use the latest self-signature. - * FIXME: We should only use this if the binding signature is valid - * but this is time consuming - we must provide another - * way to handle this - */ - const byte *p; - u32 ed; - - p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL ); - ed = p? node->pkt->pkt.public_key->timestamp + buffer_to_u32(p):0; - sigdate = sig->timestamp; - expires = ed; - } - else if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* stop at the next subkey */ - } - - return expires; -} -/**************** - * Check whether the subkey has expired. Node must point to the subkey +/* See see whether the key fits + * our requirements and in case we do not + * request a the primary key, we should select + * a suitable subkey. + * FIXME: Check against PGP 7 whether we still need a kludge + * to favor type 16 keys over type 20 keys when type 20 + * has not been explitely requested. + * Returns: True when a suitable key has been found. + * + * We have to distinguish four cases: + * 1. No usage and no primary key requested + * Examples for this case are that we have a keyID to be used + * for decrytion or verification. + * 2. No usage but primary key requested + * This is the case for all functions which work on an + * entire keyblock, e.g. for editing or listing + * 3. Usage and primary key requested + * FXME + * 4. Usage but no primary key requested + * FIXME + * FIXME: Tell what is going to happen here and something about the rationale + * */ + static int -has_expired( KBNODE node, u32 *mainkid, u32 cur_time ) +finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) { - u32 expires = subkeys_expiretime( node, mainkid ); - return expires && expires <= cur_time; -} + KBNODE keyblock = ctx->keyblock; + KBNODE k; + #define USAGE_MASK (GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_ENCR) + unsigned int req_usage = ( ctx->req_usage & USAGE_MASK ); + u32 latest_date; + KBNODE latest_key; -static void -finish_lookup( KBNODE keyblock, PKT_public_key *pk, KBNODE k, byte *namehash, - int use_namehash, int primary ) -{ - assert( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ); + assert( !foundk || foundk->pkt->pkttype == PKT_PUBLIC_KEY + || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY ); assert( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ); - if( primary && !pk->pubkey_usage ) { - copy_public_key_new_namehash( pk, keyblock->pkt->pkt.public_key, - use_namehash? namehash:NULL); - merge_one_pk_and_selfsig( keyblock, keyblock, pk ); + + ctx->found_key = NULL; + + if ( DBG_CACHE ) + log_debug( "finish_lookup: checking %s (req_usage=%x)\n", + foundk? "one key":"all keys", req_usage); + + latest_date = 0; + latest_key = NULL; + /* We do check the subkeys only if we either have requested a specific + * usage or have not requested to get the primary key. */ + if ( (req_usage || !ctx->primary) + && (!foundk || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) { + KBNODE nextk; + /* either start a loop or check just this one subkey */ + for (k=foundk?foundk:keyblock; k; k = nextk ) { + PKT_public_key *pk; + nextk = k->next; + if ( k->pkt->pkttype != PKT_PUBLIC_SUBKEY ) + continue; + if ( foundk ) + nextk = NULL; /* what a hack */ + pk = k->pkt->pkt.public_key; + if ( !pk->is_valid ) { + if (DBG_CACHE) + log_debug( "\tsubkey not valid\n"); + continue; + } + if ( pk->is_revoked ) { + if (DBG_CACHE) + log_debug( "\tsubkey has been revoked\n"); + continue; + } + if ( pk->has_expired ) { + if (DBG_CACHE) + log_debug( "\tsubkey has expired\n"); + continue; + } + + if ( req_usage && + !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { + if (DBG_CACHE) + log_debug( "\tusage does not match: want=%x have=%x\n", + req_usage, pk->pubkey_usage ); + continue; + } + + if (DBG_CACHE) + log_debug( "\tconsidering key created %lu\n", + (ulong)pk->created); + if ( pk->created > latest_date ) { + latest_date = pk->created; + latest_key = k; + } + } } - else { - if( primary && pk->pubkey_usage - && openpgp_pk_test_algo( k->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage ) == GPGERR_WR_PUBKEY_ALGO ) { - /* if the usage is not correct, try to use a subkey */ - KBNODE save_k = k; - u32 mainkid[2]; - u32 cur_time = make_timestamp(); - - keyid_from_pk( keyblock->pkt->pkt.public_key, mainkid ); - - k = NULL; - /* kludge for pgp 5: which doesn't accept type 20: - * try to use a type 16 subkey instead */ - if( pk->pubkey_usage == GCRY_PK_USAGE_ENCR ) { - for( k = save_k; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY - && k->pkt->pkt.public_key->pubkey_algo - == GCRY_PK_ELG_E - && !openpgp_pk_test_algo( - k->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage ) - && !has_expired(k, mainkid, cur_time) ) - break; - } - } - if( !k ) { - for(k = save_k ; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY - && !openpgp_pk_test_algo( - k->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage ) - && ( pk->pubkey_usage != GCRY_PK_USAGE_ENCR - || !has_expired( k, mainkid, cur_time ) ) - ) - break; - } - } - if( !k ) - k = save_k; - else - log_info(_("using secondary key %08lX " - "instead of primary key %08lX\n"), - (ulong)keyid_from_pk( k->pkt->pkt.public_key, NULL), - (ulong)keyid_from_pk( save_k->pkt->pkt.public_key, NULL) - ); - } - - copy_public_key_new_namehash( pk, k->pkt->pkt.public_key, - use_namehash? namehash:NULL); - merge_one_pk_and_selfsig( keyblock, k, pk ); + if ( !latest_key ) { + PKT_public_key *pk; + if (DBG_CACHE && !foundk ) + log_debug( "\tno suitable subkeys found - trying primary\n"); + pk = keyblock->pkt->pkt.public_key; + if ( !pk->is_valid ) { + if (DBG_CACHE) + log_debug( "\tprimary key not valid\n"); + } + else if ( pk->is_revoked ) { + if (DBG_CACHE) + log_debug( "\tprimary key has been revoked\n"); + } + else if ( pk->has_expired ) { + if (DBG_CACHE) + log_debug( "\tprimary key has expired\n"); + } + else if ( req_usage + && !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { + if (DBG_CACHE) + log_debug( "\tusage does not match: want=%x have=%x\n", + req_usage, pk->pubkey_usage ); + } + else { /* okay */ + if (DBG_CACHE) + log_debug( "\tprimary key may be used\n"); + latest_key = keyblock; + latest_date = pk->created; + } } -} - -static void -finish_lookup_sk( KBNODE keyblock, PKT_secret_key *sk, KBNODE k, int primary ) -{ - assert( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ); - assert( keyblock->pkt->pkttype == PKT_SECRET_KEY ); - if( primary && !sk->pubkey_usage ) { - copy_secret_key( sk, keyblock->pkt->pkt.secret_key ); + + if ( !latest_key ) { + if (DBG_CACHE) + log_debug("\tno suitable key found - giving up\n"); + return 0; } - else { - if( primary && sk->pubkey_usage - && openpgp_pk_test_algo( k->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage ) == GPGERR_WR_PUBKEY_ALGO ) { - /* if the usage is not correct, try to use a subkey */ - KBNODE save_k = k; - - k = NULL; - /* kludge for pgp 5: which doesn't accept type 20: - * try to use a type 16 subkey instead */ - if( sk->pubkey_usage == GCRY_PK_USAGE_ENCR ) { - for( k = save_k; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_SUBKEY - && k->pkt->pkt.secret_key->pubkey_algo - == GCRY_PK_ELG_E - && !openpgp_pk_test_algo( - k->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage ) ) - break; - } - } - if( !k ) { - for(k = save_k ; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_SUBKEY - && !openpgp_pk_test_algo( - k->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage ) ) - break; - } - } - if( !k ) - k = save_k; - else - log_info(_("using secondary key %08lX " - "instead of primary key %08lX\n"), - (ulong)keyid_from_sk( k->pkt->pkt.secret_key, NULL), - (ulong)keyid_from_sk( save_k->pkt->pkt.secret_key, NULL) - ); - } + if (DBG_CACHE) + log_debug( "\tusing key created %lu\n", (ulong)latest_date ); + + ctx->found_key = latest_key; - copy_secret_key( sk, k->pkt->pkt.secret_key ); + if ( latest_key != keyblock ) { + log_info(_("using secondary key %08lX " + "instead of primary key %08lX\n"), + (ulong)keyid_from_pk( latest_key->pkt->pkt.public_key, NULL), + (ulong)keyid_from_pk( keyblock->pkt->pkt.public_key, NULL) ); } -} + cache_user_id( keyblock ); + + return 1; /* found */ +} + static int -lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) +lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) { int rc; - KBNODE k; int oldmode = set_packet_list_mode(0); byte namehash[20]; int use_namehash=0; + KBNODE secblock = NULL; /* helper */ if( !ctx->count ) /* first time */ - rc = enum_keyblocks( 0, &ctx->kbpos, &ctx->keyblock ); + rc = enum_keyblocks( secmode?5:0, &ctx->kbpos, &ctx->keyblock ); else rc = 0; if( !rc ) { while( !(rc = enum_keyblocks( 1, &ctx->kbpos, &ctx->keyblock )) ) { int n; getkey_item_t *item; - /* fixme: we don't enum the complete keyblock, but - * use the first match and then continue with the next keyblock - */ + + if ( secmode ) { + /* find the correspondig public key and use this + * this one for the selection process */ + u32 aki[2]; + KBNODE k = ctx->keyblock; + + if ( k->pkt->pkttype != PKT_SECRET_KEY ) + BUG(); + keyid_from_sk( k->pkt->pkt.secret_key, aki ); + k = get_pubkeyblock( aki ); + if( !k ) { + log_info(_("key %08lX: secret key without public key " + "- skipped\n"), (ulong)aki[1] ); + goto skip; + } + secblock = ctx->keyblock; + ctx->keyblock = k; + } + + /* loop over all the user ids we want to look for */ item = ctx->items; for(n=0; n < ctx->nitems; n++, item++ ) { - if( item->mode < 10 ) - k = find_by_name( ctx->keyblock, pk, - item->name, item->mode, - namehash, &use_namehash ); - else if( item->mode >= 10 && item->mode <= 12 ) - k = find_by_keyid( ctx->keyblock, pk, + KBNODE k = NULL; + int found = 0; + + if( item->mode < 10 ) { + found = find_by_name( ctx->keyblock, + item->name, item->mode, + namehash ); + use_namehash = found; + } + else if( item->mode >= 10 && item->mode <= 12 ) { + k = find_by_keyid( ctx->keyblock, item->keyid, item->mode ); - else if( item->mode == 15 ) - k = find_first( ctx->keyblock, pk ); - else if( item->mode == 16 || item->mode == 20 ) - k = find_by_fpr( ctx->keyblock, pk, + found = !!k; + } + else if( item->mode == 15 ) { + found = 1; + } + else if( item->mode == 16 || item->mode == 20 ) { + k = find_by_fpr( ctx->keyblock, item->fprint, item->mode ); + found = !!k; + } else BUG(); - if( k ) { - finish_lookup( ctx->keyblock, pk, k, namehash, - use_namehash, ctx->primary ); - goto found; + if( found ) { + /* this keyblock looks fine - do further investigation */ + merge_selfsigs ( ctx->keyblock ); + if ( finish_lookup( ctx, k ) ) { + if ( secmode ) { + merge_public_with_secret ( ctx->keyblock, + secblock); + release_kbnode (secblock); + secblock = NULL; + } + goto found; + } } } + skip: + /* release resources and try the next keyblock */ + if ( secmode ) { + release_kbnode( secblock ); + secblock = NULL; + } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; } - found: ; + found: + ; } if( rc && rc != -1 ) log_error("enum_keyblocks failed: %s\n", gpg_errstr(rc)); if( !rc ) { - if( ret_keyblock ) { - *ret_keyblock = ctx->keyblock; - ctx->keyblock = NULL; - } + *ret_keyblock = ctx->keyblock; /* return the keyblock */ + ctx->keyblock = NULL; } else if( rc == -1 ) - rc = GPGERR_NO_PUBKEY; + rc = secmode ? GPGERR_NO_SECKEY : GPGERR_NO_PUBKEY; + if ( secmode ) { + release_kbnode( secblock ); + secblock = NULL; + } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; set_packet_list_mode(oldmode); @@ -1786,7 +2127,7 @@ lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) lkup_stats[ctx->mode].any = 1; if( !rc ) lkup_stats[ctx->mode].okay_count++; - else if ( rc == GPGERR_NO_PUBKEY ) + else if ( rc == GPGERR_NO_PUBKEY || rc == GPGERR_NO_SECKEY ) lkup_stats[ctx->mode].nokey_count++; else lkup_stats[ctx->mode].error_count++; @@ -1800,75 +2141,14 @@ lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) -static int -lookup_sk( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ) -{ - int rc; - KBNODE k; - int oldmode = set_packet_list_mode(0); - - if( !ctx->count ) /* first time */ - rc = enum_keyblocks( 5, &ctx->kbpos, &ctx->keyblock ); - else - rc = 0; - if( !rc ) { - while( !(rc = enum_keyblocks( 1, &ctx->kbpos, &ctx->keyblock )) ) { - int n; - getkey_item_t *item; - /* fixme: we don't enum the complete keyblock, but - * use the first match and then continue with the next keyblock - */ - /* loop over all the user ids we want to look for */ - item = ctx->items; - for(n=0; n < ctx->nitems; n++, item++ ) { - if( item->mode < 10 ) - k = find_by_name_sk( ctx->keyblock, sk, - item->name, item->mode ); - else if( item->mode >= 10 && item->mode <= 12 ) - k = find_by_keyid_sk( ctx->keyblock, sk, - item->keyid, item->mode ); - else if( item->mode == 15 ) - k = find_first_sk( ctx->keyblock, sk ); - else if( item->mode == 16 || item->mode == 20 ) - k = find_by_fpr_sk( ctx->keyblock, sk, - item->fprint, item->mode ); - else - BUG(); - if( k ) { - finish_lookup_sk( ctx->keyblock, sk, k, ctx->primary ); - goto found; - } - } - release_kbnode( ctx->keyblock ); - ctx->keyblock = NULL; - } - found: ; - } - if( rc && rc != -1 ) - log_error("enum_keyblocks failed: %s\n", gpg_errstr(rc)); - - if( !rc ) { - if( ret_keyblock ) { - *ret_keyblock = ctx->keyblock; - ctx->keyblock = NULL; - } - } - else if( rc == -1 ) - rc = GPGERR_NO_SECKEY; - - release_kbnode( ctx->keyblock ); - ctx->keyblock = NULL; - set_packet_list_mode(oldmode); - - ctx->last_rc = rc; - ctx->count++; - return rc; -} - /**************** - * fixme: replace by the generic function + * FIXME: Replace by the generic function + * It does not work as it is right now - it is used at + * 2 places: a) to get the key for an anonyous recipient + * b) to get the ultimately trusted keys. + * The a) usage might have some problems. * * Enumerate all primary secret keys. Caller must use these procedure: * 1) create a void pointer and initialize it to NULL @@ -1943,6 +2223,11 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ) } + +/********************************************* + *********** user ID printing helpers ******* + *********************************************/ + /**************** * Return a string with a printable representation of the user_id. * this string must be freed by m_free. @@ -1955,12 +2240,17 @@ get_user_id_string( u32 *keyid ) int pass=0; /* try it two times; second pass reads from key resources */ do { - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len + 10 ); - sprintf(p, "%08lX %.*s", (ulong)keyid[1], r->len, r->name ); - return p; - } + for(r=user_id_db; r; r = r->next ) { + keyid_list_t a; + for (a=r->keyids; a; a= a->next ) { + if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { + p = gcry_xmalloc( r->len + 10 ); + sprintf(p, "%08lX %.*s", + (ulong)keyid[1], r->len, r->name ); + return p; + } + } + } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = gcry_xmalloc( 15 ); sprintf(p, "%08lX [?]", (ulong)keyid[1] ); @@ -1987,13 +2277,18 @@ get_long_user_id_string( u32 *keyid ) int pass=0; /* try it two times; second pass reads from key resources */ do { - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len + 20 ); - sprintf(p, "%08lX%08lX %.*s", - (ulong)keyid[0], (ulong)keyid[1], r->len, r->name ); - return p; - } + for(r=user_id_db; r; r = r->next ) { + keyid_list_t a; + for (a=r->keyids; a; a= a->next ) { + if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { + p = gcry_xmalloc( r->len + 20 ); + sprintf(p, "%08lX%08lX %.*s", + (ulong)keyid[0], (ulong)keyid[1], + r->len, r->name ); + return p; + } + } + } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = gcry_xmalloc( 25 ); sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] ); @@ -2009,13 +2304,17 @@ get_user_id( u32 *keyid, size_t *rn ) /* try it two times; second pass reads from key resources */ do { - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len ); - memcpy(p, r->name, r->len ); - *rn = r->len; - return p; - } + for(r=user_id_db; r; r = r->next ) { + keyid_list_t a; + for (a=r->keyids; a; a= a->next ) { + if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { + p = gcry_xmalloc( r->len ); + memcpy(p, r->name, r->len ); + *rn = r->len; + return p; + } + } + } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = gcry_xstrdup( _("[User id not found]") ); *rn = strlen(p); |