From 46efe28815af5bfda86895bd7e5901472183de3d Mon Sep 17 00:00:00 2001 From: David Shaw Date: Thu, 6 Dec 2001 22:04:08 +0000 Subject: [PATCH] "sigclass" patch - adds key signature class levels, notation and policy-url displays in key listings, and shows flags for signature features. --- g10/ChangeLog | 43 ++++++++++++++++++ g10/g10.c | 21 +++++++++ g10/helptext.c | 18 ++++++++ g10/keyedit.c | 108 ++++++++++++++++++++++++++++++++++++++------- g10/keylist.c | 70 ++++++++++++++++++++++++++++- g10/main.h | 2 + g10/options.h | 3 ++ g10/packet.h | 6 +++ g10/parse-packet.c | 35 +++++++++++++-- g10/sign.c | 2 + 10 files changed, 286 insertions(+), 22 deletions(-) diff --git a/g10/ChangeLog b/g10/ChangeLog index 0bf900aa1..25302ced7 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,46 @@ +2001-12-05 David Shaw + + * Force a V4 sig if the user has a notation or policy URL set. + +2001-12-04 David Shaw + + * g10.c: Add options --keyserver-options, --temp-directory, and + auto-key-retrieve (the opposite of no-auto-key-retrieve). + + * hkp.c (hkp_search): New function to handle searching a HKP + keyserver for a key + + * hkp.c (hkp_ask_import, hkp_export): Pretty large changes to make + them communicate via the generic functions in keyserver.c + + * keyserver.c: new file with generic keyserver routines for + getting keys from a keyserver, sending keys to a keyserver, and + searching for keys on a keyserver. Calls the internal HKP stuff + in hkp.c for HKP keyserver functions. Other calls are handled by + an external program which is spawned and written to and read from + via pipes. Platforms that don't have pipes use temp files. + +2001-11-20 David Shaw + + * options.h, g10.c: New options show-notation, no-show-notation, + default-check-level, no-default-check-level, show-policy-url, + no-show-policy-url. + + * packet.h, sign.c (make_keysig_packet), parse-packet.c + (parse_signature), free-packet.c (free_seckey_enc): Fill in + structures for notation, policy, sig class, exportability, etc. + + * keyedit.c, keylist.c (print_and_check_one_sig, + list_keyblock_print): Show flags in signature display for cert + details (class, local, notation, policy, revocable). If selected, + show the notation and policy url. + + * keyedit.c (sign_uids): Prompt for and use different key sig + classes. + + * helptext.c (helptexts): Add help text to explain different + key signature classes + 2001-11-26 David Shaw * trustdb.c (mark_usable_uid_certs): Fix segfault from bad diff --git a/g10/g10.c b/g10/g10.c index f3588151d..8c03f49c8 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -68,6 +68,8 @@ enum cmd_and_opt_values { aNull = 0, oVerbose = 'v', oCompress = 'z', oNotation = 'N', + oShowNotation, + oNoShowNotation, oBatch = 500, aClearsign, aStore, @@ -121,6 +123,8 @@ enum cmd_and_opt_values { aNull = 0, oWithFingerprint, oAnswerYes, oAnswerNo, + oDefCheckLevel, + oNoDefCheckLevel, oKeyring, oSecretKeyring, oDefaultKey, @@ -175,6 +179,8 @@ enum cmd_and_opt_values { aNull = 0, oRunAsShmCP, oSetFilename, oSetPolicyURL, + oShowPolicyURL, + oNoShowPolicyURL, oUseEmbeddedFilename, oComment, oDefaultComment, @@ -407,11 +413,17 @@ static ARGPARSE_OPTS opts[] = { { oSkipVerify, "skip-verify",0, "@" }, { oCompressKeys, "compress-keys",0, "@"}, { oCompressSigs, "compress-sigs",0, "@"}, + { oDefCheckLevel, "default-check-level", 1, "@"}, + { oNoDefCheckLevel, "no-default-check-level", 0, "@"}, { oAlwaysTrust, "always-trust", 0, "@"}, { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"}, { oRunAsShmCP, "run-as-shm-coprocess", 4, "@" }, { oSetFilename, "set-filename", 2, "@" }, { oSetPolicyURL, "set-policy-url", 2, "@" }, + { oShowPolicyURL, "show-policy-url", 0, "@" }, + { oNoShowPolicyURL, "no-show-policy-url", 0, "@" }, + { oShowNotation, "show-notation", 0, "@" }, + { oNoShowNotation, "no-show-notation", 0, "@" }, { oComment, "comment", 2, "@" }, { oDefaultComment, "default-comment", 0, "@" }, { oNoVersion, "no-version", 0, "@"}, @@ -924,6 +936,8 @@ main( int argc, char **argv ) break; case oNoArmor: opt.no_armor=1; opt.armor=0; break; case oNoDefKeyring: default_keyring = 0; break; + case oNoDefCheckLevel: opt.def_check_level=0; break; + case oDefCheckLevel: opt.def_check_level=pargs.r.ret_int; break; case oNoGreeting: nogreeting = 1; break; case oNoVerbose: g10_opt_verbose = 0; opt.verbose = 0; opt.list_sigs=0; break; @@ -1007,6 +1021,8 @@ main( int argc, char **argv ) break; case oSetFilename: opt.set_filename = pargs.r.ret_str; break; case oSetPolicyURL: opt.set_policy_url = pargs.r.ret_str; break; + case oShowPolicyURL: opt.show_policy_url=1; break; + case oNoShowPolicyURL: opt.show_policy_url=0; break; case oUseEmbeddedFilename: opt.use_embedded_filename = 1; break; case oComment: opt.comment_string = pargs.r.ret_str; break; case oDefaultComment: opt.comment_string = NULL; break; @@ -1079,6 +1095,8 @@ main( int argc, char **argv ) break; case oTempDir: opt.temp_dir=pargs.r.ret_str; break; case oNotation: add_notation_data( pargs.r.ret_str ); break; + case oShowNotation: opt.show_notation=1; break; + case oNoShowNotation: opt.show_notation=0; break; case oUtf8Strings: utf8_strings = 1; break; case oNoUtf8Strings: utf8_strings = 0; break; case oDisableCipherAlgo: @@ -1221,6 +1239,9 @@ main( int argc, char **argv ) log_error(_("invalid S2K mode; must be 0, 1 or 3\n")); } + if(opt.def_check_level<0 || opt.def_check_level>3) + log_error(_("invalid default-check-level; must be 0, 1, 2, or 3\n")); + if (preference_list && keygen_set_std_prefs (preference_list)) log_error(_("invalid preferences\n")); diff --git a/g10/helptext.c b/g10/helptext.c index fb919dc44..dcb164d30 100644 --- a/g10/helptext.c +++ b/g10/helptext.c @@ -152,6 +152,24 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "Answer \"yes\" or \"no\"" )}, +{ "sign_uid.class", N_( +"When you sign a user ID on a key, you should first verify that the key\n" +"belongs to the person named in the user ID. It is useful for others to\n" +"know how carefully you verified this.\n\n" +"\"0\" means you make no particular claim as to how carefully you verified the\n" +" key.\n\n" +"\"1\" means you believe the key is owned by the person who claims to own it\n" +" but you could not, or did not verify the key at all. This is useful for\n" +" a \"persona\" verification, where you sign the key of a pseudonymous user.\n\n" +"\"2\" means you did casual verification of the key. For example, this could\n" +" mean that you verified the key fingerprint and checked the user ID on the\n" +" key against a photo ID.\n\n" +"\"3\" means you did extensive verification of the key. For example, this could\n" +" mean that you verified the key fingerprint and checked the user ID on the\n" +" key against a photo ID, and also verified the email address on the key\n" +" belongs to the key owner.\n\n" +"If you don't know what the right answer is, answer \"0\"." +)}, { "change_passwd.empty.okay", N_( "Answer \"yes\" or \"no\"" diff --git a/g10/keyedit.c b/g10/keyedit.c index 980ee0927..3bda19cf4 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -122,9 +122,16 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, break; } if( sigrc != '?' || print_without_key ) { - tty_printf("%s%c %08lX %s ", - is_rev? "rev":"sig", - sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); + tty_printf("%s%c%c %c%c%c%c%c %08lX %s ", + is_rev? "rev":"sig",sigrc, + (sig->sig_class-0x10>0 && + sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ', + sig->flags.exportable?' ':'L', + sig->flags.revocable?' ':'R', + sig->flags.policy_url?'P':' ', + sig->flags.notation?'N':' ', + sig->flags.expired?'X':' ', + (ulong)sig->keyid[1], datestr_from_sig(sig)); if( sigrc == '%' ) tty_printf("[%s] ", g10_errstr(rc) ); else if( sigrc == '?' ) @@ -140,7 +147,14 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, m_free(p); } tty_printf("\n"); + + if(sig->flags.policy_url && opt.show_policy_url) + show_policy_url(sig); + + if(sig->flags.notation && opt.show_notation) + show_notation(sig); } + return (sigrc == '!'); } @@ -245,13 +259,17 @@ static int sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) { int rc = 0; + int class=0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; PKT_secret_key *sk = NULL; KBNODE node, uidnode; PKT_public_key *primary_pk=NULL; int select_all = !count_selected_uids(keyblock); - int upd_trust = 0; + int upd_trust = 0, force_v4=0; + + if(local || opt.set_policy_url || opt.notation_data) + force_v4=1; /* build a list of all signators. * @@ -318,23 +336,81 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) tty_printf("\n"); show_key_with_all_names( keyblock, 1, 1, 0, 0 ); tty_printf("\n"); - tty_printf(_( - "Are you really sure that you want to sign this key\n" - "with your key: \"")); + + if(opt.batch) + class=0x10+opt.def_check_level; + else + { + char *answer; + + tty_printf(_("How carefully have you verified the key you are " + "about to sign actually belongs\nto the person named " + "above? If you don't know what to answer, enter \"0\".\n")); + tty_printf("\n"); + tty_printf(_(" (0) I will not answer.%s\n"), + opt.def_check_level==0?" (default)":""); + tty_printf(_(" (1) I have not checked at all.%s\n"), + opt.def_check_level==1?" (default)":""); + tty_printf(_(" (2) I have done casual checking.%s\n"), + opt.def_check_level==2?" (default)":""); + tty_printf(_(" (3) I have done very careful checking.%s\n"), + opt.def_check_level==3?" (default)":""); + tty_printf("\n"); + + while(class==0) + { + answer = cpr_get("sign_uid.class",_("Your selection? ")); + + if(answer[0]=='\0') + class=0x10+opt.def_check_level; /* Default */ + else if(strcasecmp(answer,"0")==0) + class=0x10; /* Generic */ + else if(strcasecmp(answer,"1")==0) + class=0x11; /* Persona */ + else if(strcasecmp(answer,"2")==0) + class=0x12; /* Casual */ + else if(strcasecmp(answer,"3")==0) + class=0x13; /* Positive */ + else + tty_printf(_("Invalid selection.\n")); + + m_free(answer); + } + } + + tty_printf(_("Are you really sure that you want to sign this key\n" + "with your key: \"")); p = get_user_id( sk_keyid, &n ); tty_print_utf8_string( p, n ); m_free(p); p = NULL; - tty_printf("\"\n\n"); + tty_printf("\"\n"); if( local ) tty_printf( - _("The signature will be marked as non-exportable.\n\n")); + _("\nThe signature will be marked as non-exportable.\n")); + switch(class) + { + case 0x11: + tty_printf(_("\nI have not checked this key at all.\n")); + break; + + case 0x12: + tty_printf(_("\nI have checked this key casually.\n")); + break; + + case 0x13: + tty_printf(_("\nI have checked this key very carefully.\n")); + break; + } + + tty_printf("\n"); if( opt.batch && opt.answer_yes ) - ; + ; else if( !cpr_get_answer_is_yes("sign_uid.okay", _("Really sign? ")) ) continue; + /* now we can sign the user ids */ reloop: /* (must use this, because we are modifing the list) */ primary_pk = NULL; @@ -357,12 +433,12 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) * subpacket with v3 keys and the signature becomes * exportable */ rc = make_keysig_packet( &sig, primary_pk, - node->pkt->pkt.user_id, - NULL, - sk, - 0x10, 0, local?4:0, - sign_mk_attrib, - &attrib ); + node->pkt->pkt.user_id, + NULL, + sk, + class, 0, force_v4?4:0, + sign_mk_attrib, + &attrib ); if( rc ) { log_error(_("signing failed: %s\n"), g10_errstr(rc)); goto leave; diff --git a/g10/keylist.c b/g10/keylist.c index 79e60262e..1cb6a46be 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -63,6 +63,58 @@ secret_key_list( STRLIST list ) list_one( list, 1 ); } +void +show_policy_url(PKT_signature *sig) +{ + const byte *p; + int len; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,&len); + if(p) + { + /* This isn't UTF8 as it is a URL(?) */ + printf(" %s: ",_("Signature policy")); + print_string(stdout,p,len,0); + printf("\n"); + } +} + +void +show_notation(PKT_signature *sig) +{ + const byte *p; + int len,seq=0; + + /* There may be multiple notations in the same sig. */ + + while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq))) + if(len>=8) + { + int n1,n2; + n1=(p[4]<<8)|p[5]; + n2=(p[6]<<8)|p[7]; + + if(8+n1+n2!=len) + { + log_info(_("WARNING: invalid notation data found\n")); + return; + } + + /* This is UTF8 */ + printf(" %s: ",_("Signature notation")); + print_utf8_string(stdout,p+8,n1); + printf("="); + + if(*p&0x80) + print_utf8_string(stdout,p+8+n1,n2); + else + printf("[ %s ]",_("not human readable")); + + printf("\n"); + } + else + log_info(_("WARNING: invalid notation data found\n")); +} static void list_all( int secret ) @@ -374,8 +426,15 @@ list_keyblock_print ( KBNODE keyblock, int secret ) sigrc = ' '; } fputs( sigstr, stdout ); - printf("%c %08lX %s ", - sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); + printf("%c%c %c%c%c%c%c %08lX %s ", + sigrc,(sig->sig_class-0x10>0 && + sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ', + sig->flags.exportable?' ':'L', + sig->flags.revocable?' ':'R', + sig->flags.policy_url?'P':' ', + sig->flags.notation?'N':' ', + sig->flags.expired?'X':' ', + (ulong)sig->keyid[1], datestr_from_sig(sig)); if( sigrc == '%' ) printf("[%s] ", g10_errstr(rc) ); else if( sigrc == '?' ) @@ -387,6 +446,13 @@ list_keyblock_print ( KBNODE keyblock, int secret ) m_free(p); } putchar('\n'); + + if(sig->flags.policy_url && opt.show_policy_url) + show_policy_url(sig); + + if(sig->flags.notation && opt.show_notation) + show_notation(sig); + /* fixme: check or list other sigs here */ } } diff --git a/g10/main.h b/g10/main.h index 92050146a..d254aa452 100644 --- a/g10/main.h +++ b/g10/main.h @@ -156,6 +156,8 @@ void release_revocation_reason_info( struct revocation_reason_info *reason ); void public_key_list( STRLIST list ); void secret_key_list( STRLIST list ); void print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode); +void show_policy_url(PKT_signature *sig); +void show_notation(PKT_signature *sig); /*-- verify.c --*/ int verify_signatures( int nfiles, char **files ); diff --git a/g10/options.h b/g10/options.h index 09a8f173f..6c56553f8 100644 --- a/g10/options.h +++ b/g10/options.h @@ -65,6 +65,7 @@ struct { const char *def_secret_key; char *def_recipient; int def_recipient_self; + int def_check_level; int no_comment; int no_version; int marginals_needed; @@ -106,7 +107,9 @@ struct { int no_encrypt_to; int interactive; STRLIST notation_data; + int show_notation; const char *set_policy_url; + int show_policy_url; int use_embedded_filename; int allow_non_selfsigned_uid; int allow_freeform_uid; diff --git a/g10/packet.h b/g10/packet.h index 4cdf04474..871a9986e 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -120,9 +120,15 @@ typedef struct { unsigned checked:1; /* signature has been checked */ unsigned valid:1; /* signature is good (if checked is set) */ unsigned unknown_critical:1; + unsigned exportable:1; + unsigned revocable:1; + unsigned policy_url:1; /* Policy URL is present */ + unsigned notation:1; /* At least one notation is present */ + unsigned expired:1; } flags; u32 keyid[2]; /* 64 bit keyid */ u32 timestamp; /* signature made */ + u32 expiredate; /* expires at this date or 0 if not at all */ byte version; byte sig_class; /* sig classification, append for MD calculation*/ byte pubkey_algo; /* algorithm used for public key scheme */ diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 4f935768b..5f2bf4ec5 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -910,6 +910,7 @@ parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) case SIGSUBPKT_KEY_FLAGS: return 0; case SIGSUBPKT_EXPORTABLE: + case SIGSUBPKT_REVOCABLE: if( !n ) break; return 0; @@ -974,9 +975,9 @@ can_handle_critical( const byte *buffer, size_t n, int type ) case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_PRIMARY_UID: case SIGSUBPKT_FEATURES: + case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */ return 1; - case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */ default: return 0; } @@ -1108,7 +1109,6 @@ parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype, } - static int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature *sig ) @@ -1143,6 +1143,8 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, } sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--; sig->digest_algo = iobuf_get_noeof(inp); pktlen--; + sig->flags.exportable=1; + sig->flags.revocable=1; if( is_v4 ) { /* read subpackets */ n = read_16(inp); pktlen -= 2; /* length of hashed data */ if( n > 10000 ) { @@ -1218,6 +1220,33 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, sig->keyid[0] = buffer_to_u32(p); sig->keyid[1] = buffer_to_u32(p+4); } + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL); + if(p) + sig->expiredate=sig->timestamp+buffer_to_u32(p); + if(sig->expiredate>0 && sig->expiredateflags.expired=1; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,NULL); + if(p) + sig->flags.policy_url=1; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,NULL); + if(p) + sig->flags.notation=1; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_REVOCABLE,NULL); + if(p && *p==0) + sig->flags.revocable=0; + + /* We accept this subpacket from either the hashed or unhashed + areas as older versions of gpg put it in the unhashed area. + In theory, anyway, we should never see this packet off of a + local keyring. */ + + p=parse_sig_subpkt2(sig,SIGSUBPKT_EXPORTABLE,NULL); + if(p && *p==0) + sig->flags.exportable=0; } if( list_mode ) { @@ -1992,5 +2021,3 @@ create_gpg_control( ctrlpkttype_t type, const byte *data, size_t datalen ) return packet; } - - diff --git a/g10/sign.c b/g10/sign.c index a3e0595c6..ce8f82a41 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1025,6 +1025,8 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, /* and make the signature packet */ sig = m_alloc_clear( sizeof *sig ); sig->version = sigversion; + sig->flags.exportable=1; + sig->flags.revocable=1; keyid_from_sk( sk, sig->keyid ); sig->pubkey_algo = sk->pubkey_algo; sig->digest_algo = digest_algo;