diff options
-rw-r--r-- | g10/ChangeLog | 65 | ||||
-rw-r--r-- | g10/export.c | 25 | ||||
-rw-r--r-- | g10/free-packet.c | 9 | ||||
-rw-r--r-- | g10/getkey.c | 115 | ||||
-rw-r--r-- | g10/import.c | 61 | ||||
-rw-r--r-- | g10/keyedit.c | 35 | ||||
-rw-r--r-- | g10/keylist.c | 8 | ||||
-rw-r--r-- | g10/packet.h | 15 | ||||
-rw-r--r-- | g10/parse-packet.c | 27 |
9 files changed, 331 insertions, 29 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog index 4dbd64c12..ccdc0f98e 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,68 @@ +2002-02-28 David Shaw <[email protected]> + + * getkey.c (check_revocation_keys): New function to check a + revocation against a list of potential revocation keys. Note the + loop-breaking code here. This is to prevent blowing up if A is + B's revocation key, while B is also A's. Note also that this is + written so that a revoked revoker can still issue revocations: + i.e. If A revokes B, but A is revoked, B is still revoked. I'm + not completely convinced this is the proper behavior, but it + matches how PGP does it. It does at least have the advantage of + much simpler code - my first version of this had lots of loop + maintaining code so you could chain revokers many levels deep and + if D was revoked, C was not, which meant that B was, and so on. + It was sort of scary, actually. + + * getkey.c (merge_selfsigs_main): Add any revocation keys onto the + pk. This is particularly interesting since we normally only get + data from the most recent 1F signature, but you need multiple 1F + sigs to properly handle revocation keys (PGP does it this way, and + a revocation key could be marked "sensitive" and hence in a + different signature). Also, if a pk has a revocation key set, + check for revocation sigs that were not made by us - if made by a + valid revocation key, mark the pk revoked. + + * packet.h, getkey.c (cache_public_key): do not cache key if + "dont_cache" is set. This allows the revocation key code to look + up a key and return information that may be inaccurate to prevent + loops without caching the fake data. + + * packet.h, sig-check.c (do_signature_check): Record if a + signature was made by a revoked pk. + + * packet.h, parse-packet.c (parse_one_sig_subpkt, + can_handle_critical, parse_signature): Get revocation key + information out of direct sigs. + + * keylist.c (list_keyblock_print): don't assume that the presence + of a 0x20 signature means the key is revoked. With revocation + keys, this may not be true if the revocation key is not around to + verify it or if verification failed. Also, 0x1F should get listed + as "sig", and not "unexpected signature class". + + * keyedit.c (show_key_with_all_names): Add a flag for printing + revoker information and change all callers. + + * import.c (merge_blocks): merge in any new direct key (0x1F) + sigs. + + * import.c (import_revoke_cert): don't keep processing after a + revocation is rejected. + + * import.c (delete_inv_parts): Allow importing a revocation + signature even if it was not issued by the key. This allows a + revocation key to issue it. Of course, the sig still needs to be + checked before we trust it. + + * free-packet.c (copy_public_key): Include a new copy of the + revocation keys when duping a pk. + + * free-packet.c (free_seckey_enc, release_public_key_parts): Free + any revocation keys that are attached to a sig or pk. + + * export.c (do_export_stream): Do not export signatures with + "sensitive" revocation keys in them. + 2002-02-27 David Shaw <[email protected]> * export.c (do_export_stream): Do not include v3 keys in a diff --git a/g10/export.c b/g10/export.c index 2bfb9d018..a8ca9210c 100644 --- a/g10/export.c +++ b/g10/export.c @@ -200,14 +200,27 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) /* make sure that ring_trust packets never get exported */ if (node->pkt->pkttype == PKT_RING_TRUST) continue; - /* do not export packets which are marked as not exportable */ + if( node->pkt->pkttype == PKT_SIGNATURE ) { - if( !node->pkt->pkt.signature->flags.exportable ) - continue; /* not exportable */ + /* do not export packets which are marked as not exportable */ + if( !node->pkt->pkt.signature->flags.exportable ) + continue; /* not exportable */ + + /* do not export packets with a "sensitive" revocation + key. This will need revisiting when we start + supporting creating revocation keys and not just + reading them. */ + if( node->pkt->pkt.signature->revkey ) { + int i; + + for(i=0;i<node->pkt->pkt.signature->numrevkeys;i++) + if(node->pkt->pkt.signature->revkey[i]->class & 0x40) + continue; + } - /* delete our verification cache */ - delete_sig_subpkt (node->pkt->pkt.signature->unhashed, - SIGSUBPKT_PRIV_VERIFY_CACHE); + /* delete our verification cache */ + delete_sig_subpkt (node->pkt->pkt.signature->unhashed, + SIGSUBPKT_PRIV_VERIFY_CACHE); } if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY ) { diff --git a/g10/free-packet.c b/g10/free-packet.c index f62c7ca03..4df3658d3 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -59,6 +59,8 @@ free_seckey_enc( PKT_signature *sig ) mpi_free(sig->data[0]); for(i=0; i < n; i++ ) mpi_free( sig->data[i] ); + + m_free(sig->revkey); m_free(sig->hashed); m_free(sig->unhashed); m_free(sig); @@ -89,6 +91,11 @@ release_public_key_parts( PKT_public_key *pk ) free_user_id (pk->user_id); pk->user_id = NULL; } + if (pk->revkey) { + m_free(pk->revkey); + pk->revkey=NULL; + pk->numrevkeys=0; + } } @@ -157,6 +164,8 @@ copy_public_key ( PKT_public_key *d, PKT_public_key *s) for(i=0; i < n; i++ ) d->pkey[i] = mpi_copy( s->pkey[i] ); } + d->revkey=m_alloc(sizeof(struct revocation_key)*s->numrevkeys); + memcpy(d->revkey,s->revkey,sizeof(struct revocation_key)*s->numrevkeys); return d; } diff --git a/g10/getkey.c b/g10/getkey.c index fddb67ed3..5b8ac80ce 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -96,7 +96,7 @@ static int uid_cache_entries; /* number of entries in uid cache */ static void merge_selfsigs( KBNODE keyblock ); static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ); - +static int check_revocation_keys(PKT_public_key *pk,PKT_signature *sig); #if 0 static void @@ -126,6 +126,9 @@ cache_public_key( PKT_public_key *pk ) if( pk_cache_disabled ) return; + if( pk->dont_cache ) + return; + if( is_ELGAMAL(pk->pubkey_algo) || pk->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(pk->pubkey_algo) ) { @@ -1176,7 +1179,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) if ( pk->version < 4 ) { /* before v4 the key packet itself contains the expiration date - * and there was noway to change it. So we also use only the + * and there was no way to change it. So we also use only the * one from the key packet */ key_expire = pk->expiredate; key_expire_seen = 1; @@ -1216,6 +1219,29 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) sigdate = sig->timestamp; signode = k; sigversion = sig->version; + + /* Add any revocation keys onto the pk. This + is particularly interesting since we + normally only get data from the most recent + 1F signature, but you need multiple 1F sigs + to properly handle revocation keys (PGP + does it this way, and a revocation key + could be sensitive and hence in a different + signature). */ + if(sig->revkey) { + int i; + + pk->revkey= + m_realloc(pk->revkey,sizeof(struct revocation_key)* + (pk->numrevkeys+sig->numrevkeys)); + + for(i=0;i<sig->numrevkeys;i++) + memcpy(&pk->revkey[pk->numrevkeys], + sig->revkey[i], + sizeof(struct revocation_key)); + + pk->numrevkeys+=sig->numrevkeys; + } } } } @@ -1246,11 +1272,37 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) key_expire_seen = 1; } } + /* mark that key as valid: one direct key signature should * render a key as valid */ pk->is_valid = 1; } + /* pass 1.5: look for key revocation signatures that were not made + by the key (i.e. did a revocation key issue a revocation for + us?). Only bother to do this if there is a revocation key in + the first place. */ + + if(pk->revkey) + for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next ) + { + if ( k->pkt->pkttype == PKT_SIGNATURE ) + { + PKT_signature *sig = k->pkt->pkt.signature; + + if(IS_KEY_REV(sig) && + (sig->keyid[0]!=kid[0] || sig->keyid[1]!=kid[1])) + { + if(check_revocation_keys(pk,sig)) + ; /* did not verify, or loop broken */ + else + *r_revoked=1; + + /* In the future handle subkey and cert revocations? + PGP doesn't, but it's in 2440. */ + } + } + } /* second pass: look at the self-signature of all user IDs */ signode = uidnode = NULL; @@ -2189,3 +2241,62 @@ get_ctx_handle(GETKEY_CTX ctx) { return ctx->kr_handle; } + +/* Check the revocation keys to see if any of them have revoked our + pk. sig is the revocation sig. pk is the key it is on. This code + will need to be modified if gpg ever becomes multi-threaded. Note + that this is written so that a revoked revoker can still issue + revocations: i.e. If A revokes B, but A is revoked, B is still + revoked. I'm not completely convinced this is the proper behavior, + but it matches how PGP does it. -dms */ + +/* Return 0 if pk is revoked, non-0 if not revoked */ +static int +check_revocation_keys(PKT_public_key *pk,PKT_signature *sig) +{ + static int busy=0; + int i,rc=-1; + + assert(IS_KEY_REV(sig)); + assert((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1])); + + if(busy) + { + /* return -1 (i.e. not revoked), but mark the pk as uncacheable + as we don't really know its revocation status until it is + checked directly. */ + + pk->dont_cache=1; + return -1; + } + + busy=1; + + /* printf("looking at %08lX with a sig from %08lX\n",(ulong)pk->keyid[1], + (ulong)sig->keyid[1]); */ + + /* is the issuer of the sig one of our revokers? */ + for(i=0;i<pk->numrevkeys;i++) + { + u32 keyid[2]; + + keyid_from_fingerprint(pk->revkey[i].fpr,MAX_FINGERPRINT_LEN,keyid); + + if(keyid[0]==sig->keyid[0] && keyid[1]==sig->keyid[1]) + { + MD_HANDLE md; + + md=md_open(sig->digest_algo,0); + hash_public_key(md,pk); + if(signature_check(sig,md)==0) + { + rc=0; + break; + } + } + } + + busy=0; + + return rc; +} diff --git a/g10/import.c b/g10/import.c index 1daca680e..37147b40d 100644 --- a/g10/import.c +++ b/g10/import.c @@ -743,6 +743,7 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats ) if( rc ) { log_error( _("key %08lX: invalid revocation certificate" ": %s - rejected\n"), (ulong)keyid[1], g10_errstr(rc)); + goto leave; } @@ -954,12 +955,22 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) delete_kbnode( node ); } else { - int rc = check_key_signature( keyblock, node, NULL); - if( rc ) { - log_error( _("key %08lX: invalid revocation " - "certificate: %s - skipped\n"), - (ulong)keyid[1], g10_errstr(rc)); - delete_kbnode( node ); + /* If the revocation cert is from a different key than + the one we're working on don't check it - it's + probably from a revocation key and won't be + verifiable with this key anyway. */ + + if(node->pkt->pkt.signature->keyid[0]==keyid[0] && + node->pkt->pkt.signature->keyid[1]==keyid[1]) + { + int rc = check_key_signature( keyblock, node, NULL); + if( rc ) + { + log_error( _("key %08lX: invalid revocation " + "certificate: %s - skipped\n"), + (ulong)keyid[1], g10_errstr(rc)); + delete_kbnode( node ); + } } } } @@ -1114,7 +1125,37 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } - /* 2nd: try to merge new certificates in */ + /* 2nd: merge in any direct key (0x1F) sigs */ + for(node=keyblock->next; node; node=node->next ) { + if( node->pkt->pkttype == PKT_USER_ID ) + break; + else if( node->pkt->pkttype == PKT_SIGNATURE + && node->pkt->pkt.signature->sig_class == 0x1F ) { + /* check whether we already have this */ + found = 0; + for(onode=keyblock_orig->next; onode; onode=onode->next ) { + if( onode->pkt->pkttype == PKT_USER_ID ) + break; + else if( onode->pkt->pkttype == PKT_SIGNATURE + && onode->pkt->pkt.signature->sig_class == 0x1F + && !cmp_signatures(onode->pkt->pkt.signature, + node->pkt->pkt.signature)) { + found = 1; + break; + } + } + if( !found ) { + KBNODE n2 = clone_kbnode(node); + insert_kbnode( keyblock_orig, n2, 0 ); + n2->flag |= 1; + ++*n_sigs; + log_info( _("key %08lX: direct key signature added\n"), + (ulong)keyid[1]); + } + } + } + + /* 3rd: try to merge new certificates in */ for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( !(onode->flag & 1) && onode->pkt->pkttype == PKT_USER_ID) { /* find the user id in the imported keyblock */ @@ -1131,7 +1172,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } - /* 3rd: add new user-ids */ + /* 4th: add new user-ids */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_USER_ID) { /* do we have this in the original keyblock */ @@ -1149,7 +1190,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } - /* add new subkeys */ + /* 5th: add new subkeys */ for(node=keyblock->next; node; node=node->next ) { onode = NULL; if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { @@ -1182,7 +1223,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } - /* merge subkey certificates */ + /* 6th: merge subkey certificates */ for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( !(onode->flag & 1) && ( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY diff --git a/g10/keyedit.c b/g10/keyedit.c index db834cc74..3cfbf99ec 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -42,8 +42,8 @@ #include "i18n.h" static void show_prefs( PKT_user_id *uid, int verbose ); -static void show_key_with_all_names( KBNODE keyblock, - int only_marked, int with_fpr, int with_subkeys, int with_prefs ); +static void show_key_with_all_names( KBNODE keyblock, int only_marked, + int with_revoker, int with_fpr, int with_subkeys, int with_prefs ); static void show_key_and_fingerprint( KBNODE keyblock ); static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock, int photo ); static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ); @@ -390,7 +390,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, } /* Ask whether we really should sign these user id(s) */ tty_printf("\n"); - show_key_with_all_names( keyblock, 1, 1, 0, 0 ); + show_key_with_all_names( keyblock, 1, 0, 1, 0, 0 ); tty_printf("\n"); if(primary_pk->expiredate) @@ -894,7 +894,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, tty_printf("\n"); if( redisplay ) { - show_key_with_all_names( cur_keyblock, 0, 0, 1, 0 ); + show_key_with_all_names( cur_keyblock, 0, 1, 0, 1, 0 ); tty_printf("\n"); redisplay = 0; } @@ -1167,7 +1167,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; case cmdTRUST: - show_key_with_all_names( keyblock, 0, 0, 1, 0 ); + show_key_with_all_names( keyblock, 0, 0, 0, 1, 0 ); tty_printf("\n"); if( edit_ownertrust( find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key, 1 ) ) @@ -1175,11 +1175,11 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; case cmdPREF: - show_key_with_all_names( keyblock, 0, 0, 0, 1 ); + show_key_with_all_names( keyblock, 0, 0, 0, 0, 1 ); break; case cmdSHOWPREF: - show_key_with_all_names( keyblock, 0, 0, 0, 2 ); + show_key_with_all_names( keyblock, 0, 0, 0, 0, 2 ); break; case cmdSETPREF: @@ -1389,7 +1389,7 @@ show_prefs (PKT_user_id *uid, int verbose) * so for user ids with mark A flag set and dont display the index number */ static void -show_key_with_all_names( KBNODE keyblock, int only_marked, +show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs ) { KBNODE node; @@ -1410,6 +1410,25 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, otrust = get_ownertrust_info (pk); } + if(with_revoker) + for(i=0;i<pk->numrevkeys;i++) + { + u32 r_keyid[2]; + char *user; + + keyid_from_fingerprint(pk->revkey[i].fpr, + MAX_FINGERPRINT_LEN,r_keyid); + + user=get_user_id_string(r_keyid); + + tty_printf(_("This key may be revoked by %s key %s%s\n"), + pubkey_algo_to_string(pk->revkey[i].algid), + user, + pk->revkey[i].class&0x40?_(" (sensitive)"):""); + + m_free(user); + } + tty_printf(_("%s%c %4u%c/%08lX created: %s expires: %s"), node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", (node->flag & NODFLG_SELKEY)? '*':' ', diff --git a/g10/keylist.c b/g10/keylist.c index 8fc486c5d..4655e9adc 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -417,7 +417,11 @@ list_keyblock_print ( KBNODE keyblock, int secret ) char *sigstr; if( !any ) { /* no user id, (maybe a revocation follows)*/ - if( sig->sig_class == 0x20 ) + /* Check if the pk is really revoked - there could be a + 0x20 sig packet there even if we are not revoked + (say, if a revocation key issued the packet, but the + revocation key isn't present to verify it.) */ + if( sig->sig_class == 0x20 && pk->is_revoked ) puts("[revoked]"); else if( sig->sig_class == 0x18 ) puts("[key binding]"); @@ -437,6 +441,8 @@ list_keyblock_print ( KBNODE keyblock, int secret ) sigstr = "sig"; else if( sig->sig_class == 0x18 ) sigstr = "sig"; + else if( sig->sig_class == 0x1F ) + sigstr = "sig"; else { printf("sig " "[unexpected signature class 0x%02x]\n",sig->sig_class ); diff --git a/g10/packet.h b/g10/packet.h index e55ca336a..e27a2c531 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -26,6 +26,7 @@ #include "mpi.h" #include "cipher.h" #include "filter.h" +#include "global.h" #define DEBUG_PARSE_PACKET 1 @@ -113,6 +114,11 @@ typedef struct { byte data[1]; } subpktarea_t; +struct revocation_key { + byte class; + byte algid; + byte fpr[MAX_FINGERPRINT_LEN]; +}; typedef struct { ulong local_id; /* internal use, valid if > 0 */ @@ -133,7 +139,9 @@ typedef struct { byte sig_class; /* sig classification, append for MD calculation*/ byte pubkey_algo; /* algorithm used for public key scheme */ /* (PUBKEY_ALGO_xxx) */ - byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ + byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ + struct revocation_key **revkey; + int numrevkeys; subpktarea_t *hashed; /* all subpackets with hashed data (v4 only) */ subpktarea_t *unhashed; /* ditto for unhashed data */ byte digest_start[2]; /* first 2 bytes of the digest */ @@ -190,13 +198,16 @@ typedef struct { u32 has_expired; /* set to the expiration date if expired */ int is_revoked; /* key has been revoked */ int is_valid; /* key (especially subkey) is valid */ + int dont_cache; /* do not cache this */ ulong local_id; /* internal use, valid if > 0 */ u32 main_keyid[2]; /* keyid of the primary key */ u32 keyid[2]; /* calculated by keyid_from_pk() */ prefitem_t *prefs; /* list of preferences (may be NULL) */ - int mdc_feature; /* mdc feature set */ + int mdc_feature; /* mdc feature set */ byte *namehash; /* if != NULL: found by this name */ PKT_user_id *user_id; /* if != NULL: found by that uid */ + struct revocation_key *revkey; + int numrevkeys; MPI pkey[PUBKEY_MAX_NPKEY]; } PKT_public_key; diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 60c45b56b..e27e22c85 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -922,6 +922,10 @@ parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) if( n < 8 ) /* minimum length needed */ break; return 0; + case SIGSUBPKT_REV_KEY: + if(n < 22) + break; + return 0; case SIGSUBPKT_REVOC_REASON: if( !n ) break; @@ -969,6 +973,7 @@ can_handle_critical( const byte *buffer, size_t n, int type ) case SIGSUBPKT_KEY_EXPIRE: case SIGSUBPKT_EXPORTABLE: case SIGSUBPKT_REVOCABLE: + case SIGSUBPKT_REV_KEY: case SIGSUBPKT_ISSUER:/* issuer key ID */ case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: @@ -1248,6 +1253,28 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, p=parse_sig_subpkt2(sig,SIGSUBPKT_EXPORTABLE,NULL); if(p && *p==0) sig->flags.exportable=0; + + /* Find all revokation keys */ + if(sig->sig_class==0x1F) + { + struct revocation_key *revkey; + int len,seq=0; + + while((revkey= + (struct revocation_key *)enum_sig_subpkt(sig->hashed, + SIGSUBPKT_REV_KEY, + &len,&seq))) + { + if(len==sizeof(struct revocation_key) && + revkey->class&0x80) /* 0x80 bit must be set */ + { + sig->revkey=m_realloc(sig->revkey, + sizeof(struct revocation_key *)*(sig->numrevkeys+1)); + sig->revkey[sig->numrevkeys]=revkey; + sig->numrevkeys++; + } + } + } } if( list_mode ) { |