diff --git a/g10/ChangeLog b/g10/ChangeLog index d0fa9262a..30f8939bf 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,13 @@ 2005-04-24 David Shaw + * trustdb.h, trustdb.c (mark_usable_uid_certs): Add flags for the + no-pubkey and chosen revocation cases. + (clean_uid): New function to clean a user ID of unusable (as + defined by mark_usable_uid_certs) certs. + + * keyedit.c (keyedit_menu, menu_clean_uids): Call it here for new + "clean" command that removes unusable sigs from a key. + * trustdb.h, keyedit.c (keyedit_menu, menu_select_uid_namehash): Allow specifying user ID via the namehash from --with-colons --fixed-list-mode --list-keys. Suggested by Peter Palfrader. diff --git a/g10/keyedit.c b/g10/keyedit.c index 7a00695b2..00499b3d3 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -53,6 +53,7 @@ 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 ); static int menu_delsig( KBNODE pub_keyblock ); +static int menu_clean_uids(KBNODE keyblock); static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ); @@ -1327,7 +1328,7 @@ enum cmdids cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdPREFKS, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST, - cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdNOP + cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN, cmdNOP }; static struct @@ -1426,6 +1427,7 @@ static struct { "enable" , cmdENABLEKEY , KEYEDIT_NOT_SK, N_("enable key") }, { "disable" , cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key") }, { "showphoto",cmdSHOWPHOTO , 0, N_("show selected photo IDs") }, + { "clean", cmdCLEAN , KEYEDIT_NOT_SK, NULL }, { NULL, cmdNONE, 0, NULL } }; @@ -1952,7 +1954,7 @@ keyedit_menu( const char *username, STRLIST locusr, { int sensitive=0; - if(arg_string && ascii_strcasecmp(arg_string,"sensitive")==0) + if(ascii_strcasecmp(arg_string,"sensitive")==0) sensitive=1; if( menu_addrevoker( keyblock, sec_keyblock, sensitive ) ) { redisplay = 1; @@ -2123,9 +2125,27 @@ keyedit_menu( const char *username, STRLIST locusr, } break; - case cmdSHOWPHOTO: - menu_showphoto(keyblock); - break; + case cmdSHOWPHOTO: + menu_showphoto(keyblock); + break; + + case cmdCLEAN: + { + if(*arg_string) + { + if(ascii_strcasecmp(arg_string,"sigs")!=0 + && ascii_strcasecmp(arg_string,"signatures")!=0 + && ascii_strcasecmp(arg_string,"certs")!=0 + && ascii_strcasecmp(arg_string,"certificates")!=0) + { + tty_printf(_("Unable to clean `%s'\n"),arg_string); + break; + } + } + + modified=menu_clean_uids(keyblock); + } + break; case cmdQUIT: if( have_commands ) @@ -3108,6 +3128,41 @@ menu_delsig( KBNODE pub_keyblock ) return changed; } +static int +menu_clean_uids(KBNODE keyblock) +{ + KBNODE uidnode; + int modified=0; + int select_all=!count_selected_uids(keyblock); + + for(uidnode=keyblock;uidnode;uidnode=uidnode->next) + { + if(uidnode->pkt->pkttype==PKT_USER_ID + && (uidnode->flag&NODFLG_SELUID || select_all)) + { + int deleted; + char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name, + uidnode->pkt->pkt.user_id->len, + 0); + deleted=clean_uid(keyblock,uidnode,opt.verbose); + if(deleted) + { + tty_printf(deleted==1? + _("User ID \"%s\": %d signature removed.\n"): + _("User ID \"%s\": %d signatures removed.\n"), + user,deleted); + modified=1; + } + else + tty_printf(_("User ID \"%s\": already clean.\n"),user); + + m_free(user); + } + } + + return modified; +} + /**************** * Remove some of the secondary keys diff --git a/g10/trustdb.c b/g10/trustdb.c index d69b872ca..751f1b4ef 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,6 +1,6 @@ /* trustdb.c - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, - * 2004 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -1409,8 +1409,9 @@ is_in_klist (struct key_item *k, PKT_signature *sig) * To do this, we first revmove all signatures which are not valid and * from the remain ones we look for the latest one. If this is not a * certification revocation signature we mark the signature by setting - * node flag bit 8. Note that flag bits 9 and 10 are used for internal - * purposes. + * node flag bit 8. Revocations are marked with flag 11, and sigs + * from unavailable keys are marked with flag 12. Note that flag bits + * 9 and 10 are used for internal purposes. */ static void mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, @@ -1423,34 +1424,44 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, /* first check all signatures */ for (node=uidnode->next; node; node = node->next) { - node->flag &= ~(1<<8 | 1<<9 | 1<<10); + int rc; + + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype != PKT_SIGNATURE) continue; - sig = node->pkt->pkt.signature; - if (sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) - continue; /* ignore self-signatures */ + if (main_kid + && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) + continue; /* ignore self-signatures if we pass in a main_kid */ if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) continue; /* we only look at these signature classes */ if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && sig->sig_class-0x10flag |= 1<<12; + continue; + } node->flag |= 1<<9; } /* reset the remaining flags */ for (; node; node = node->next) - node->flag &= ~(1<<8 | 1<<9 | 1 << 10); + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); /* kbnode flag usage: bit 9 is here set for signatures to consider, * bit 10 will be set by the loop to keep track of keyIDs already - * processed, bit 8 will be set for the usable signatures */ + * processed, bit 8 will be set for the usable signatures, and bit + * 11 will be set for usable revocations. */ /* for each cert figure out the latest valid one */ for (node=uidnode->next; node; node = node->next) @@ -1458,7 +1469,7 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, KBNODE n, signode; u32 kid[2]; u32 sigdate; - + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(node->flag & (1<<9)) ) @@ -1470,6 +1481,8 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, signode = node; sigdate = sig->timestamp; kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; + + /* Now find the latest and greatest signature */ for (n=uidnode->next; n; n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) @@ -1532,6 +1545,7 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, sigdate = sig->timestamp; } } + sig = signode->pkt->pkt.signature; if (IS_UID_SIG (sig)) { /* this seems to be a usable one which is not revoked. @@ -1550,13 +1564,77 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, if (expire==0 || expire > curtime ) { signode->flag |= (1<<8); /* yeah, found a good cert */ - if (expire && expire < *next_expire) + if (next_expire && expire && expire < *next_expire) *next_expire = expire; } } + else + signode->flag |= (1<<11); } } +int +clean_uid(KBNODE keyblock,KBNODE uidnode,int noisy) +{ + int deleted=0; + KBNODE node; + + assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + + /* Passing in a 0 for current time here means that we'll never weed + out an expired sig. This is correct behavior since we want to + keep the most recent expired sig in a series. */ + mark_usable_uid_certs(keyblock,uidnode,NULL,NULL,0,NULL); + + /* What we want to do here is remove signatures that are not + considered as part of the trust calculations. Thus, all invalid + signatures are out, as are any signatures that aren't the last of + a series of uid sigs or revocations It breaks down like this: + coming out of mark_usable_uid_certs, if a sig is unflagged, it is + not even a candidate. If a sig has flag 9 or 10, that means it + was selected as a candidate and vetted. If a sig has flag 8 it + is a usable signature. If a sig has flag 11 it is a usable + revocation. If a sig has flag 12 it was issued by an unavailable + key. "Usable" here means the most recent valid + signature/revocation in a series from a particular signer. + + Delete everything that isn't a usable uid sig (which might be + expired), a usable revocation, or a sig from an unavailable + key. */ + + for(node=uidnode->next; + node && node->pkt->pkttype==PKT_SIGNATURE; + node=node->next) + { + /* Keep usable uid sigs ... */ + if(node->flag & (1<<8)) + continue; + + /* ... and usable revocations... */ + if(node->flag & (1<<11)) + continue; + + /* ... and sigs from unavailable keys. */ + if(node->flag & (1<<12)) + continue; + + /* Everything else we delete */ + + /* if 9 or 10 is set, but we get this far, it's superceded, + otherwise, it's invalid */ + + if(noisy) + log_info("removing signature issued by key %s: %s\n", + keystr(node->pkt->pkt.signature->keyid), + node->flag&(1<<9)?"superceded":"invalid"); + + delete_kbnode(node); + deleted++; + } + + return deleted; +} + /* Used by validate_one_keyblock to confirm a regexp within a trust signature. Returns 1 for match, and 0 for no match or regex error. */ diff --git a/g10/trustdb.h b/g10/trustdb.h index 710c13b22..a9f98c712 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -81,6 +81,8 @@ const char *get_ownertrust_string (PKT_public_key *pk); void update_ownertrust (PKT_public_key *pk, unsigned int new_trust ); int clear_ownertrusts (PKT_public_key *pk); +int clean_uid(KBNODE keyblock,KBNODE uidnode,int noisy); + /*-- tdbdump.c --*/ void list_trustdb(const char *username); void export_ownertrust(void);