diff --git a/NEWS b/NEWS index c701794ce..06fa14d78 100644 --- a/NEWS +++ b/NEWS @@ -13,7 +13,8 @@ except for revocation certificates and --enarmor mode. * The command "primary" in the edit menu can be used to change the - primary UID. + primary UID, "setpref" and "updpref" can be used to change the + preferences. Noteworthy changes in version 1.0.6 (2001-05-29) ------------------------------------------------ diff --git a/TODO b/TODO index 401cbd258..69a1f4f1b 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,8 @@ * add listing of notation data + * Make sure that we only update the latest self-signatures. + * Check the changes to the gpg random agtherer on all W32 platforms. * Check that a key signature can be revoked and later be signed again. diff --git a/doc/gpg.sgml b/doc/gpg.sgml index 57c3f4400..80413283c 100644 --- a/doc/gpg.sgml +++ b/doc/gpg.sgml @@ -326,6 +326,12 @@ primary key is changed. passwd Change the passphrase of the secret key. + + primary + + Flag the current user id as the primary one, removes the primary user + id flag from all other user ids and sets the timestamp of all + affected self-signatures one second ahead. uid &ParmN; @@ -348,6 +354,21 @@ List preferences. showpref More verbose preferences listing. + setpref &ParmString; + +Set the list of user ID preferences to &ParmString;, this should be +a string similar to the one printed by "pref". Using an empty string +will set the default preference string, using "none" will set the +preferences to nil. Only available algorithms are allowed. This +command just initializes an internal list and does not change anything +unless another command which changes the self-signatures is used. + + updpref + +Change the preferences of all user IDs (or just of the selected ones +to the current list of preferences. The timestamp of all affected +self-signatures fill be advanced by one second. + toggle @@ -1509,6 +1530,14 @@ Don't change the permissions of a secret keyring back to user read/write only. Use this option only if you really know what you are doing. + +--preference-list &ParmString + +Set the list of preferences to &ParmString;, this list should be +a string similar to the one printed by the command "pref" in the edit +menu. + + diff --git a/g10/ChangeLog b/g10/ChangeLog index 5a29b0cfd..5325767db 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,17 @@ +2001-08-09 Werner Koch + + * g10.c (main): New option "--preference-list" + * keyedit.c (keyedit_menu): New commands "setpref" and "updpref". + (menu_set_preferences): New. + * keygen.c (keygen_set_std_prefs): New. + (set_one_pref): New. + (check_zip_algo): New. + (keygen_get_std_prefs): New. + (keygen_upd_std_prefs): New + (keygen_add_std_prefs): Move the pref setting code into the above fnc. + * build-packet.c (build_sig_subpkt): Updated the list of allowed + to update subpackets. + 2001-08-08 Werner Koch * packet.h (subpktarea_t): New. diff --git a/g10/build-packet.c b/g10/build-packet.c index ac21f8896..1235a451e 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -744,6 +744,9 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, else if (find_subpkt (sig->hashed, type, NULL, NULL) ) { switch (type) { case SIGSUBPKT_SIG_CREATED: + case SIGSUBPKT_PREF_SYM: + case SIGSUBPKT_PREF_HASH: + case SIGSUBPKT_PREF_COMPR: delete_sig_subpkt (sig->hashed, type); break; default: diff --git a/g10/g10.c b/g10/g10.c index 7e5f281f8..221b7a8f8 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -208,6 +208,7 @@ enum cmd_and_opt_values { aNull = 0, oNoSigCache, oNoSigCreateCheck, oPreservePermissions, + oPreferenceList, oEmu3DESS2KBug, /* will be removed in 1.1 */ oEmuMDEncodeBug, aTest }; @@ -408,6 +409,7 @@ static ARGPARSE_OPTS opts[] = { { oNoExpensiveTrustChecks, "no-expensive-trust-checks", 0, "@" }, { aDeleteSecretAndPublicKey, "delete-secret-and-public-key",256, "@" }, { oPreservePermissions, "preserve-permissions", 0, "@"}, + { oPreferenceList, "preference-list", 2, "@"}, { oEmu3DESS2KBug, "emulate-3des-s2k-bug", 0, "@"}, { oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"}, {0} }; @@ -619,6 +621,7 @@ main( int argc, char **argv ) char *def_digest_string = NULL; char *s2k_cipher_string = NULL; char *s2k_digest_string = NULL; + char *preference_list = NULL; int pwfd = -1; int with_fpr = 0; /* make an option out of --fingerprint */ #ifdef USE_SHM_COPROCESSING @@ -992,7 +995,7 @@ main( int argc, char **argv ) break; case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break; case oPreservePermissions: opt.preserve_permissions=1; break; - + case oPreferenceList: preference_list = pargs.r.ret_str; break; default : pargs.err = configfp? 1:2; break; } } @@ -1092,6 +1095,8 @@ main( int argc, char **argv ) log_error(_("invalid S2K mode; must be 0, 1 or 3\n")); } + if (preference_list && keygen_set_std_prefs (preference_list)) + log_error(_("invalid preferences\n")); if( log_get_errorcount(0) ) g10_exit(2); diff --git a/g10/helptext.c b/g10/helptext.c index b60339d3b..35feccc71 100644 --- a/g10/helptext.c +++ b/g10/helptext.c @@ -197,6 +197,12 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "a second one is available." )}, +{ "keyedit.updpref.okay", N_( + "Change the preferences of all user IDs (or just of the selected ones)\n" + "to the current list of preferences. The timestamp of all affected\n" + "self-signatures fill be advanced by one second.\n" +)}, + { "passphrase.enter", N_( "" diff --git a/g10/keyedit.c b/g10/keyedit.c index b388f7f5a..8f4dccf4c 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -51,6 +51,7 @@ static int menu_delsig( KBNODE pub_keyblock ); static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_set_primary_uid( KBNODE pub_keyblock, KBNODE sec_keyblock ); +static int menu_set_preferences( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_select_uid( KBNODE keyblock, int idx ); static int menu_select_key( KBNODE keyblock, int idx ); static int count_uids( KBNODE keyblock ); @@ -575,7 +576,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, cmdLSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, - cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, + cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdUPDPREF, cmdINVCMD, cmdNOP }; static struct { const char *name; enum cmdids id; @@ -612,6 +613,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, { N_("t" ) , cmdTOGGLE , 1,0,0, NULL }, { N_("pref") , cmdPREF , 0,1,0, N_("list preferences") }, { N_("showpref"), cmdSHOWPREF , 0,1,0, N_("list preferences") }, + { N_("setpref") , cmdSETPREF , 1,1,0, N_("set preference list") }, + { N_("updpref") , cmdUPDPREF , 1,1,0, N_("updated preferences") }, { N_("passwd") , cmdPASSWD , 1,1,0, N_("change the passphrase") }, { N_("trust") , cmdTRUST , 0,1,0, N_("change the ownertrust") }, { N_("revsig") , cmdREVSIG , 0,1,0, N_("revoke signatures") }, @@ -682,6 +685,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, cur_keyblock = keyblock; for(;;) { /* main loop */ int i, arg_number; + const char *arg_string = ""; char *p; tty_printf("\n"); @@ -725,6 +729,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, trim_spaces(answer); trim_spaces(p); arg_number = atoi(p); + arg_string = p; } for(i=0; cmds[i].name; i++ ) { @@ -952,6 +957,31 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, show_key_with_all_names( keyblock, 0, 0, 0, 2 ); break; + case cmdSETPREF: + keygen_set_std_prefs ( !*arg_string? "default" : arg_string ); + break; + + case cmdUPDPREF: + { + char *p = keygen_get_std_prefs (); + tty_printf (("Current preference list: %s\n"), p); + m_free (p); + } + if (cpr_get_answer_is_yes ("keyedit.updpref.okay", + count_selected_uids (keyblock)? + _("Really update the preferences" + " for the selected user IDs? "): + _("Really update the preferences? "))){ + + if ( menu_set_preferences (keyblock, sec_keyblock) ) { + update_trust_record (keyblock, 0, NULL); + merge_keys_and_selfsig (keyblock); + modified = 1; + redisplay = 1; + } + } + break; + case cmdNOP: break; @@ -1665,6 +1695,7 @@ change_primary_uid_cb ( PKT_signature *sig, void *opaque ) return 0; } + /* * Set the primary uid flag for the selected UID. We will also reset * all other primary uid flags. For this to work with have to update @@ -1765,6 +1796,82 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock ) } +/* + * Set preferences to new values for the selected user IDs + */ +static int +menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock ) +{ + PKT_secret_key *sk; /* copy of the main sk */ + PKT_public_key *main_pk; + PKT_user_id *uid; + KBNODE node; + u32 keyid[2]; + int selected, select_all; + int modified = 0; + + select_all = !count_selected_uids (pub_keyblock); + + node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); + sk = copy_secret_key( NULL, node->pkt->pkt.secret_key); + + /* Now we can actually change the self signature(s) */ + main_pk = NULL; + uid = NULL; + selected = 0; + for ( node=pub_keyblock; node; node = node->next ) { + if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + break; /* ready */ + + if ( node->pkt->pkttype == PKT_PUBLIC_KEY ) { + main_pk = node->pkt->pkt.public_key; + keyid_from_pk( main_pk, keyid ); + } + else if ( node->pkt->pkttype == PKT_USER_ID ) { + uid = node->pkt->pkt.user_id; + selected = select_all || (node->flag & NODFLG_SELUID); + } + else if ( main_pk && uid && selected + && node->pkt->pkttype == PKT_SIGNATURE ) { + PKT_signature *sig = node->pkt->pkt.signature; + if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] + && (uid && (sig->sig_class&~3) == 0x10) + && sig->version >= 4 ) { + /* This is a selfsignature which is to be replaced + * We have to ignore v3 signatures because they are + * not able to carry the preferences */ + PKT_signature *newsig; + PACKET *newpkt; + int rc; + + rc = update_keysig_packet (&newsig, sig, + main_pk, uid, + sk, + keygen_upd_std_prefs, + NULL ); + if( rc ) { + log_error ("update_keysig_packet failed: %s\n", + g10_errstr(rc)); + free_secret_key( sk ); + return 0; + } + /* replace the packet */ + newpkt = m_alloc_clear( sizeof *newpkt ); + newpkt->pkttype = PKT_SIGNATURE; + newpkt->pkt.signature = newsig; + free_packet( node->pkt ); + m_free( node->pkt ); + node->pkt = newpkt; + modified = 1; + } + } + } + + free_secret_key( sk ); + return modified; +} + + /**************** * Select one user id or remove all selection if index is 0. * Returns: True if the selection changed; diff --git a/g10/keygen.c b/g10/keygen.c index 5a9145bcb..693eb3329 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -35,6 +35,9 @@ #include "status.h" #include "i18n.h" +#define MAX_PREFS 30 + + enum para_name { pKEYTYPE, pKEYLENGTH, @@ -83,6 +86,15 @@ struct output_control_s { }; +static int prefs_initialized = 0; +static byte sym_prefs[MAX_PREFS]; +static int nsym_prefs; +static byte hash_prefs[MAX_PREFS]; +static int nhash_prefs; +static byte zip_prefs[MAX_PREFS]; +static int nzip_prefs; + + static void do_generate_keypair( struct para_data_s *para, struct output_control_s *outctrl ); static int write_keyblock( IOBUF out, KBNODE node ); @@ -124,6 +136,140 @@ keygen_add_key_expire( PKT_signature *sig, void *opaque ) } + +static int +set_one_pref (ulong val, int type, int (*cf)(int), byte *buf, int *nbuf) +{ + int i; + + if (!val || val > 127 || cf (val)) { + log_info (_("preference %c%lu is not valid\n"), type, val); + return -1; + } + for (i=0; i < *nbuf; i++ ) { + if (buf[i] == val) { + log_info (_("preference %c%lu duplicated\n"), type, val); + return -1; + } + } + if (*nbuf >= MAX_PREFS) { + log_info (_("too many `%c' preferences\n"), type); + return -1; + } + buf[(*nbuf)++] = val; + return 0; +} + +static int +check_zip_algo (int algo) +{ + return algo < 0 || algo > 2; +} + +/* + * Parse the supplied string and use it to set the standard preferences. + * The String is expected to be in a forma like the one printed by "prefs", + * something like: "S10 S3 H3 H2 Z2 Z1". Use NULL to set the default + * preferences. + * Returns: 0 = okay + */ +int +keygen_set_std_prefs (const char *string) +{ + byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; + int nsym=0, nhash=0, nzip=0; + ulong val; + const char *s, *s2; + int rc = 0; + + if (!string || !ascii_strcasecmp (string, "default")) + string = "S7 S10 S3 S4 H3 H2 Z2 Z1"; + else if (!ascii_strcasecmp (string, "none")) + string = ""; + + for (s=string; *s; s = s2) { + if ((*s=='s' || *s == 'S') && isdigit(s[1]) ) { + val = strtoul (++s, (char**)&s2, 10); + if (set_one_pref (val, 'S', check_cipher_algo, sym, &nsym)) + rc = -1; + } + else if ((*s=='h' || *s == 'H') && isdigit(s[1]) ) { + val = strtoul (++s, (char**)&s2, 10); + if (set_one_pref (val, 'H', check_digest_algo, hash, &nhash)) + rc = -1; + } + else if ((*s=='z' || *s == 'Z') && isdigit(s[1]) ) { + val = strtoul (++s, (char**)&s2, 10); + if (set_one_pref (val, 'Z', check_zip_algo, zip, &nzip)) + rc = -1; + } + else if (isspace (*s)) + s2 = s+1; + else { + log_info (_("invalid character in string\n")); + return -1; + } + } + + if (!rc) { + memcpy (sym_prefs, sym, (nsym_prefs=nsym)); + memcpy (hash_prefs, hash, (nhash_prefs=nhash)); + memcpy (zip_prefs, zip, (nzip_prefs=nzip)); + prefs_initialized = 1; + } + return rc; +} + + +/* + * Return a printable list of preferences. Caller must free. + */ +char * +keygen_get_std_prefs () +{ + char *buf; + int i; + + if (!prefs_initialized) + keygen_set_std_prefs (NULL); + + buf = m_alloc ( MAX_PREFS*3*5 + 1); + *buf = 0; + for (i=0; i < nsym_prefs; i++ ) + sprintf (buf+strlen(buf), "S%d ", sym_prefs[i]); + for (i=0; i < nhash_prefs; i++ ) + sprintf (buf+strlen(buf), "H%d ", hash_prefs[i]); + for (i=0; i < nzip_prefs; i++ ) + sprintf (buf+strlen(buf), "Z%d ", zip_prefs[i]); + + if (*buf) /* trim the trailing space */ + buf[strlen(buf)-1] = 0; + return buf; +} + + +int +keygen_upd_std_prefs( PKT_signature *sig, void *opaque ) +{ + if (!prefs_initialized) + keygen_set_std_prefs (NULL); + + if (nsym_prefs) + build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); + else + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); + if (nhash_prefs) + build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); + else + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH); + if (nzip_prefs) + build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs); + else + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); + return 0; +} + + /**************** * Add preference to the self signature packet. * This is only called for packets with version > 3. @@ -132,22 +278,9 @@ int keygen_add_std_prefs( PKT_signature *sig, void *opaque ) { byte buf[8]; - + keygen_add_key_expire( sig, opaque ); - - buf[0] = CIPHER_ALGO_RIJNDAEL; - buf[1] = CIPHER_ALGO_TWOFISH; - buf[2] = CIPHER_ALGO_CAST5; - buf[3] = CIPHER_ALGO_BLOWFISH; - build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 4 ); - - buf[0] = DIGEST_ALGO_RMD160; - buf[1] = DIGEST_ALGO_SHA1; - build_sig_subpkt( sig, SIGSUBPKT_PREF_HASH, buf, 2 ); - - buf[0] = 2; - buf[1] = 1; - build_sig_subpkt( sig, SIGSUBPKT_PREF_COMPR, buf, 2 ); + keygen_upd_std_prefs (sig, opaque); buf[0] = 0x80; /* no modify - It is reasonable that a key holder * has the possibility to reject signatures from users diff --git a/g10/main.h b/g10/main.h index 13ec8e6c6..353d5bd94 100644 --- a/g10/main.h +++ b/g10/main.h @@ -98,8 +98,11 @@ void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds, /*-- keygen.c --*/ u32 ask_expiredate(void); void generate_keypair( const char *fname ); +int keygen_set_std_prefs (const char *string); +char *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); +int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); /*-- openfile.c --*/ diff --git a/g10/sign.c b/g10/sign.c index f36838b4a..817099a84 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -925,7 +925,7 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, /**************** * Create a new signature packet based on an existing one. - * Only user ID signatureare supportted for now. + * Only user ID signaturs are supported for now. * TODO: Merge this with make_keysig_packet. */ int @@ -967,9 +967,9 @@ update_keysig_packet( PKT_signature **ret_sig, if ( sig->version >= 4 && mksubpkt) rc = (*mksubpkt)(sig, opaque); - /* we increasethe timestamp by one second so that a future import - of this key will replace the existing one. We make sure that - we don't produce a timestamp in the future */ + /* we increase the timestamp by one second so that a future import + of this key will replace the existing one. We also make sure that + we don't create a timestamp in the future */ sig->timestamp++; while (sig->timestamp >= make_timestamp()) sleep (1);