1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-07-02 02:48:57 +02:00

This is the first half of revocation key / designated revoker

support.  That is, it handles all the data to mark a key as revoked if it
has been revoked by a designated revoker.  The second half (coming
later) will contain the code to make someones key your designated revoker
and to issue revocations for someone else.

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.  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.

This also changes importing to allow bringing in more revocation keys, and
exporting to not export revocation keys marked "sensitive".

The --edit menu information will show if a revocation key is present.
This commit is contained in:
David Shaw 2002-02-28 21:01:25 +00:00
parent 6be293e24b
commit fbc66185f8
9 changed files with 332 additions and 30 deletions

View File

@ -1,3 +1,68 @@
2002-02-28 David Shaw <dshaw@jabberwocky.com>
* 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 <dshaw@jabberwocky.com>
* export.c (do_export_stream): Do not include v3 keys in a

View File

@ -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 */
/* delete our verification cache */
delete_sig_subpkt (node->pkt->pkt.signature->unhashed,
SIGSUBPKT_PRIV_VERIFY_CACHE);
if( node->pkt->pkttype == PKT_SIGNATURE ) {
/* 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);
}
if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY ) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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)? '*':' ',

View File

@ -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 );

View File

@ -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;

View File

@ -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 ) {