Some bug fixes of the last release
[gnupg.git] / g10 / keyedit.c
index f61916b..2d3a0d0 100644 (file)
 #include "status.h"
 #include "i18n.h"
 
+static void show_prefs( KBNODE keyblock, PKT_user_id *uid );
 static void show_key_with_all_names( KBNODE keyblock,
-               int only_marked, int with_fpr, int with_subkeys );
+           int only_marked, int with_fpr, int with_subkeys, int with_prefs );
 static void show_key_and_fingerprint( KBNODE keyblock );
 static void show_fingerprint( PKT_public_key *pk );
 static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock );
 static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_select_uid( KBNODE keyblock, int index );
 static int menu_select_key( KBNODE keyblock, int index );
 static int count_uids( KBNODE keyblock );
@@ -55,7 +57,7 @@ static int count_keys_with_flag( KBNODE keyblock, unsigned flag );
 static int count_selected_uids( KBNODE keyblock );
 static int count_selected_keys( KBNODE keyblock );
 
-
+#define CONTROL_D ('D' - 'A' + 1)
 
 #define NODFLG_BADSIG (1<<0)  /* bad signature */
 #define NODFLG_NOKEY  (1<<1)  /* no public key */
@@ -84,6 +86,9 @@ get_keyblock_byname( KBNODE *keyblock, KBPOS *kbpos, const char *username )
     rc = read_keyblock( kbpos, keyblock );
     if( rc )
        log_error("%s: keyblock read problem: %s\n", username, g10_errstr(rc));
+    else
+       merge_keys_and_selfsig( *keyblock );
+
     return rc;
 }
 
@@ -195,7 +200,6 @@ check_all_keysigs( KBNODE keyblock, int only_selected )
 }
 
 
-
 /****************
  * Loop over all locusr and and sign the uids after asking.
  * If no user id is marked, all user ids will be signed;
@@ -209,9 +213,11 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
     int rc = 0;
     SK_LIST sk_list = NULL;
     SK_LIST sk_rover = NULL;
+    PKT_secret_key *sk = NULL;
     KBNODE node, uidnode;
-    PKT_public_key *primary_pk;
+    PKT_public_key *primary_pk=NULL;
     int select_all = !count_selected_uids(keyblock);
+    int upd_trust = 0;
 
     /* build a list of all signators */
     rc=build_sk_list( locusr, &sk_list, 0, 1 );
@@ -224,7 +230,14 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
        size_t n;
        char *p;
 
-       keyid_from_sk( sk_rover->sk, sk_keyid );
+       /* we have to use a copy of the sk, because make_keysig_packet
+        * may remove the protection from sk and if we did other
+        * changes to the secret key, we would save the unprotected
+        * version */
+       if( sk )
+           free_secret_key(sk);
+       sk = copy_secret_key( NULL, sk_rover->sk );
+       keyid_from_sk( sk, sk_keyid );
        /* set mark A for all selected user ids */
        for( node=keyblock; node; node = node->next ) {
            if( select_all || (node->flag & NODFLG_SELUID) )
@@ -254,24 +267,20 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
                                                  (ulong)sk_keyid[1] );
            continue;
        }
-       /* Ask whether we realy should sign these user id(s) */
+       /* Ask whether we really should sign these user id(s) */
        tty_printf("\n");
-       show_key_with_all_names( keyblock, 1, 1, 0 );
+       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: \""));
        p = get_user_id( sk_keyid, &n );
        tty_print_string( p, n );
+       m_free(p); p = NULL;
        tty_printf("\"\n\n");
-       m_free(p);
-       p = cpr_get("sign_uid.really", _("Really sign? "));
-       cpr_kill_prompt();
-       if( !answer_is_yes(p) ) {
-           m_free(p);
-           continue; /* No */
-       }
-       m_free(p);
+
+       if( !cpr_get_answer_is_yes(N_("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;
@@ -288,13 +297,14 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
                rc = make_keysig_packet( &sig, primary_pk,
                                               node->pkt->pkt.user_id,
                                               NULL,
-                                              sk_rover->sk,
+                                              sk,
                                               0x10, 0, NULL, NULL );
                if( rc ) {
                    log_error(_("signing failed: %s\n"), g10_errstr(rc));
                    goto leave;
                }
                *ret_modified = 1; /* we changed the keyblock */
+               upd_trust = 1;
 
                pkt = m_alloc_clear( sizeof *pkt );
                pkt->pkttype = PKT_SIGNATURE;
@@ -304,133 +314,20 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
            }
        }
     } /* end loop over signators */
-
-  leave:
-    release_sk_list( sk_list );
-    return rc;
-}
-
-
-
-
-/****************
- * Delete a public or secret key from a keyring.
- */
-int
-delete_key( const char *username, int secret )
-{
-    int rc = 0;
-    KBNODE keyblock = NULL;
-    KBNODE node;
-    KBPOS kbpos;
-    PKT_public_key *pk = NULL;
-    PKT_secret_key *sk = NULL;
-    u32 keyid[2];
-    int okay=0;
-
-    /* search the userid */
-    rc = secret? find_secret_keyblock_byname( &kbpos, username )
-              : find_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error(_("%s: user not found\n"), username );
-       goto leave;
+    if( upd_trust && primary_pk ) {
+       rc = clear_trust_checked_flag( primary_pk );
     }
 
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("%s: read problem: %s\n", username, g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, secret? PKT_SECRET_KEY:PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error("Oops; key not found anymore!\n");
-       rc = G10ERR_GENERAL;
-       goto leave;
-    }
-
-    if( secret ) {
-       sk = node->pkt->pkt.secret_key;
-       keyid_from_sk( sk, keyid );
-    }
-    else {
-       pk = node->pkt->pkt.public_key;
-       keyid_from_pk( pk, keyid );
-       rc = seckey_available( keyid );
-       if( !rc ) {
-           log_error(_(
-           "there is a secret key for this public key!\n"));
-           log_info(_(
-           "use option \"--delete-secret-key\" to delete it first.\n"));
-           rc = -1;
-       }
-       else if( rc != G10ERR_NO_SECKEY )
-           log_error("%s: get secret key: %s\n", username, g10_errstr(rc) );
-       else
-           rc = 0;
-    }
-
-    if( rc )
-       rc = 0;
-    else if( opt.batch && secret )
-       log_error(_("can't do that in batch-mode\n"));
-    else if( opt.batch && opt.answer_yes )
-       okay++;
-    else if( opt.batch )
-       log_error(_("can't do that in batch-mode without \"--yes\"\n"));
-    else {
-       char *p;
-       size_t n;
-
-       if( secret )
-           tty_printf("sec  %4u%c/%08lX %s   ",
-                     nbits_from_sk( sk ),
-                     pubkey_letter( sk->pubkey_algo ),
-                     keyid[1], datestr_from_sk(sk) );
-       else
-           tty_printf("pub  %4u%c/%08lX %s   ",
-                     nbits_from_pk( pk ),
-                     pubkey_letter( pk->pubkey_algo ),
-                     keyid[1], datestr_from_pk(pk) );
-       p = get_user_id( keyid, &n );
-       tty_print_string( p, n );
-       m_free(p);
-       tty_printf("\n\n");
-
-       p = cpr_get( secret? "delete_key.secret.really":"delete_key.really",
-                       _("Delete this key from the keyring? "));
-       cpr_kill_prompt();
-       if( !cpr_enabled() && secret && answer_is_yes(p)) {
-           /* I think it is not required to check a passphrase; if
-            * the user is so stupid as to let others access his secret keyring
-            * (and has no backup) - it is up him to read some very
-            * basic texts about security.
-            */
-           m_free(p);
-           p = tty_get(_("This is a secret key! - really delete? "));
-       }
-       if( answer_is_yes(p) )
-           okay++;
-       m_free(p);
-    }
-
-
-    if( okay ) {
-       rc = delete_keyblock( &kbpos );
-       if( rc ) {
-           log_error("delete_keyblock failed: %s\n", g10_errstr(rc) );
-           goto leave;
-       }
-    }
 
   leave:
-    release_kbnode( keyblock );
+    release_sk_list( sk_list );
+    if( sk )
+       free_secret_key(sk);
     return rc;
 }
 
 
+
 /****************
  * Change the passphrase of the primary and all secondary keys.
  * We use only one passphrase for all keys.
@@ -460,7 +357,7 @@ change_passphrase( KBNODE keyblock )
        break;
       default:
        tty_printf(_("Key is protected.\n"));
-       rc = check_secret_key( sk );
+       rc = check_secret_key( sk, 0 );
        if( !rc )
            passphrase = get_last_passphrase();
        break;
@@ -471,7 +368,7 @@ change_passphrase( KBNODE keyblock )
        if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
            PKT_secret_key *subsk = node->pkt->pkt.secret_key;
            set_next_passphrase( passphrase );
-           rc = check_secret_key( subsk );
+           rc = check_secret_key( subsk, 0 );
        }
     }
 
@@ -485,9 +382,9 @@ change_passphrase( KBNODE keyblock )
 
        set_next_passphrase( NULL );
        for(;;) {
-           s2k->mode = 1;
-           s2k->hash_algo = DIGEST_ALGO_RMD160;
-           dek = passphrase_to_dek( NULL, CIPHER_ALGO_BLOWFISH, s2k, 2 );
+           s2k->mode = opt.s2k_mode;
+           s2k->hash_algo = opt.s2k_digest_algo;
+           dek = passphrase_to_dek( NULL, opt.s2k_cipher_algo, s2k, 2 );
            if( !dek ) {
                tty_printf(_("passphrase not correctly repeated; try again.\n"));
            }
@@ -495,7 +392,7 @@ change_passphrase( KBNODE keyblock )
                rc = 0;
                tty_printf(_( "You don't want a passphrase -"
                            " this is probably a *bad* idea!\n\n"));
-               if( cpr_get_answer_is_yes("change_passwd.empty",
+               if( cpr_get_answer_is_yes(N_("change_passwd.empty.okay"),
                               _("Do you really want to do this? ")))
                    changed++;
                break;
@@ -530,7 +427,46 @@ change_passphrase( KBNODE keyblock )
 }
 
 
+/****************
+ * There are some keys out (due to a bug in gnupg), where the sequence
+ * of the packets is wrong.  This function fixes that.
+ * Returns: true if the keyblock has fixed.
+ */
+static int
+fix_keyblock( KBNODE keyblock )
+{
+    KBNODE node, last, subkey;
+    int fixed=0;
+
+    /* locate key signatures of class 0x10..0x13 behind sub key packets */
+    for( subkey=last=NULL, node = keyblock; node;
+                                           last=node, node = node->next ) {
+       switch( node->pkt->pkttype ) {
+         case PKT_PUBLIC_SUBKEY:
+         case PKT_SECRET_SUBKEY:
+           if( !subkey )
+               subkey = last; /* actually it is the one before the subkey */
+           break;
+         case PKT_SIGNATURE:
+           if( subkey ) {
+               PKT_signature *sig = node->pkt->pkt.signature;
+               if( sig->sig_class >= 0x10 && sig->sig_class <= 0x13 ) {
+                   log_info(_(
+                       "moving a key signature to the correct place\n"));
+                   last->next = node->next;
+                   node->next = subkey->next;
+                   subkey->next = node;
+                   node = last;
+                   fixed=1;
+               }
+           }
+           break;
+         default: break;
+       }
+    }
 
+    return fixed;
+}
 
 /****************
  * Menu driven key editor
@@ -544,7 +480,7 @@ keyedit_menu( const char *username, STRLIST locusr )
     enum cmdids { cmdNONE = 0,
           cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
           cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY,
-          cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST,
+          cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE,
           cmdNOP };
     static struct { const char *name;
                    enum cmdids id;
@@ -570,9 +506,11 @@ keyedit_menu( const char *username, STRLIST locusr )
        { N_("deluid")  , cmdDELUID , 0, N_("delete user id") },
        { N_("addkey")  , cmdADDKEY , 1, N_("add a secondary key") },
        { N_("delkey")  , cmdDELKEY , 0, N_("delete a secondary key") },
+       { N_("expire")  , cmdEXPIRE , 1, N_("change the expire date") },
        { N_("toggle")  , cmdTOGGLE , 1, N_("toggle between secret "
                                            "and public key listing") },
        { N_("t"     )  , cmdTOGGLE , 1, NULL },
+       { N_("pref")    , cmdPREF  , 0, N_("list preferences") },
        { N_("passwd")  , cmdPASSWD , 1, N_("change the passphrase") },
        { N_("trust")   , cmdTRUST , 0, N_("change the ownertrust") },
 
@@ -592,7 +530,7 @@ keyedit_menu( const char *username, STRLIST locusr )
 
 
     if( opt.batch ) {
-       log_error(_("can't do that in batch-mode\n"));
+       log_error(_("can't do that in batchmode\n"));
        goto leave;
     }
 
@@ -605,12 +543,17 @@ keyedit_menu( const char *username, STRLIST locusr )
                                            username, g10_errstr(rc));
            goto leave;
        }
+       merge_keys_and_selfsig( sec_keyblock );
+       if( fix_keyblock( sec_keyblock ) )
+           sec_modified++;
     }
 
     /* and now get the public key */
     rc = get_keyblock_byname( &keyblock, &keyblockpos, username );
     if( rc )
        goto leave;
+    if( fix_keyblock( keyblock ) )
+       modified++;
 
     if( sec_keyblock ) { /* check that they match */
        /* FIXME: check that they both match */
@@ -625,18 +568,22 @@ keyedit_menu( const char *username, STRLIST locusr )
 
        tty_printf("\n");
        if( redisplay ) {
-           show_key_with_all_names( cur_keyblock, 0, 0, 1 );
+           show_key_with_all_names( cur_keyblock, 0, 0, 1, 0 );
            tty_printf("\n");
            redisplay = 0;
        }
-       m_free(answer);
-       answer = cpr_get("keyedit.cmd", _("Command> "));
-       cpr_kill_prompt();
-       trim_spaces(answer);
+       do {
+           m_free(answer);
+           answer = cpr_get(N_("keyedit.cmd"), _("Command> "));
+           cpr_kill_prompt();
+           trim_spaces(answer);
+       } while( *answer == '#' );
 
        arg_number = 0;
        if( !*answer )
            cmd = cmdLIST;
+       else if( *answer == CONTROL_D )
+           cmd = cmdQUIT;
        else if( isdigit( *answer ) ) {
            cmd = cmdSELUID;
            arg_number = atoi(answer);
@@ -665,26 +612,30 @@ keyedit_menu( const char *username, STRLIST locusr )
                if( cmds[i].need_sk && !sec_keyblock )
                    ; /* skip if we do not have the secret key */
                else if( cmds[i].desc )
-                   tty_printf("%-10s %s\n", cmds[i].name, cmds[i].desc );
+                   tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
            }
            break;
 
          case cmdQUIT:
-           if( !modified )
+           if( !modified && !sec_modified )
                goto leave;
-           if( !cpr_get_answer_is_yes("keyedit.save",_("Save changes? ")) ) {
+           if( !cpr_get_answer_is_yes(N_("keyedit.save.okay"),
+                                       _("Save changes? ")) ) {
                if( cpr_enabled()
-                   || tty_get_answer_is_yes(_("Quit without saving? ")) )
+                   || cpr_get_answer_is_yes(N_("keyedit.cancel.okay"),
+                                            _("Quit without saving? ")) )
                    goto leave;
                break;
            }
            /* fall thru */
          case cmdSAVE:
-           if( modified ) {
-               rc = update_keyblock( &keyblockpos, keyblock );
-               if( rc ) {
-                   log_error(_("update failed: %s\n"), g10_errstr(rc) );
-                   break;
+           if( modified || sec_modified  ) {
+               if( modified ) {
+                   rc = update_keyblock( &keyblockpos, keyblock );
+                   if( rc ) {
+                       log_error(_("update failed: %s\n"), g10_errstr(rc) );
+                       break;
+                   }
                }
                if( sec_modified ) {
                    rc = update_keyblock( &sec_keyblockpos, sec_keyblock );
@@ -694,10 +645,13 @@ keyedit_menu( const char *username, STRLIST locusr )
                        break;
                    }
                }
-               /* FIXME: UPDATE/INVALIDATE trustdb !! */
            }
            else
                tty_printf(_("Key not changed so no update needed.\n"));
+           rc = update_trust_record( keyblock, 0, NULL );
+           if( rc )
+               log_error(_("update of trust db failed: %s\n"),
+                           g10_errstr(rc) );
            goto leave;
 
          case cmdLIST:
@@ -727,7 +681,8 @@ keyedit_menu( const char *username, STRLIST locusr )
 
          case cmdSIGN: /* sign (only the public key) */
            if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) {
-               if( !tty_get_answer_is_yes(_("Really sign all user ids? ")) ) {
+               if( !cpr_get_answer_is_yes(N_("keyedit.sign_all.okay"),
+                                          _("Really sign all user ids? ")) ) {
                    tty_printf(_("Hint: Select the user ids to sign\n"));
                    break;
                }
@@ -749,6 +704,14 @@ keyedit_menu( const char *username, STRLIST locusr )
            if( menu_adduid( keyblock, sec_keyblock ) ) {
                redisplay = 1;
                sec_modified = modified = 1;
+               /* must update the trustdb already here, so that preferences
+                * get listed correctly */
+               rc = update_trust_record( keyblock, 0, NULL );
+               if( rc ) {
+                   log_error(_("update of trust db failed: %s\n"),
+                               g10_errstr(rc) );
+                   rc = 0;
+               }
            }
            break;
 
@@ -759,7 +722,8 @@ keyedit_menu( const char *username, STRLIST locusr )
                    tty_printf(_("You must select at least one user id.\n"));
                else if( count_uids(keyblock) - n1 < 1 )
                    tty_printf(_("You can't delete the last user id!\n"));
-               else if( tty_get_answer_is_yes(
+               else if( cpr_get_answer_is_yes(
+                           N_("keyedit.remove.uid.okay"),
                        n1 > 1? _("Really remove all selected user ids? ")
                              : _("Really remove this user id? ")
                       ) ) {
@@ -785,7 +749,8 @@ keyedit_menu( const char *username, STRLIST locusr )
 
                if( !(n1=count_selected_keys( keyblock )) )
                    tty_printf(_("You must select at least one key.\n"));
-               else if( sec_keyblock && !tty_get_answer_is_yes(
+               else if( sec_keyblock && !cpr_get_answer_is_yes(
+                           N_("keyedit.remove.subkey.okay"),
                       n1 > 1?
                        _("Do you really want to delete the selected keys? "):
                        _("Do you really want to delete this key? ")
@@ -801,13 +766,23 @@ keyedit_menu( const char *username, STRLIST locusr )
            }
            break;
 
+         case cmdEXPIRE:
+           if( menu_expire( keyblock, sec_keyblock ) ) {
+               merge_keys_and_selfsig( sec_keyblock );
+               merge_keys_and_selfsig( keyblock );
+               sec_modified = 1;
+               modified = 1;
+               redisplay = 1;
+           }
+           break;
+
          case cmdPASSWD:
            if( change_passphrase( sec_keyblock ) )
                sec_modified = 1;
            break;
 
          case cmdTRUST:
-           show_key_with_all_names( keyblock, 0, 0, 1 );
+           show_key_with_all_names( keyblock, 0, 0, 1, 0 );
            tty_printf("\n");
            if( edit_ownertrust( find_kbnode( keyblock,
                      PKT_PUBLIC_KEY )->pkt->pkt.public_key->local_id, 1 ) )
@@ -816,6 +791,10 @@ keyedit_menu( const char *username, STRLIST locusr )
             * are updated immediately */
            break;
 
+         case cmdPREF:
+           show_key_with_all_names( keyblock, 0, 0, 0, 1 );
+           break;
+
          case cmdNOP:
            break;
 
@@ -833,6 +812,45 @@ keyedit_menu( const char *username, STRLIST locusr )
 }
 
 
+/****************
+ * show preferences of a public keyblock.
+ */
+static void
+show_prefs( KBNODE keyblock, PKT_user_id *uid )
+{
+    KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
+    PKT_public_key *pk;
+    byte *p;
+    int i;
+    size_t n;
+    byte namehash[20];
+
+    if( !node )
+       return; /* is a secret keyblock */
+    pk = node->pkt->pkt.public_key;
+    if( !pk->local_id ) {
+       log_error("oops: no LID\n");
+       return;
+    }
+
+    rmd160_hash_buffer( namehash, uid->name, uid->len );
+
+    p = get_pref_data( pk->local_id, namehash, &n );
+    if( !p )
+       return;
+
+    tty_printf("    ");
+    for(i=0; i < n; i+=2 ) {
+       if( p[i] )
+           tty_printf( " %c%d", p[i] == PREFTYPE_SYM    ? 'S' :
+                                p[i] == PREFTYPE_HASH   ? 'H' :
+                                p[i] == PREFTYPE_COMPR  ? 'Z' : '?', p[i+1]);
+    }
+    tty_printf("\n");
+
+    m_free(p);
+}
+
 
 /****************
  * Display the key a the user ids, if only_marked is true, do only
@@ -840,7 +858,7 @@ keyedit_menu( const char *username, STRLIST locusr )
  */
 static void
 show_key_with_all_names( KBNODE keyblock, int only_marked,
-                        int with_fpr, int with_subkeys )
+                        int with_fpr, int with_subkeys, int with_prefs )
 {
     KBNODE node;
     int i;
@@ -850,6 +868,15 @@ show_key_with_all_names( KBNODE keyblock, int only_marked,
        if( node->pkt->pkttype == PKT_PUBLIC_KEY
            || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) {
            PKT_public_key *pk = node->pkt->pkt.public_key;
+           int otrust=0, trust=0;
+
+           if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+               /* do it here, so that debug messages don't clutter the
+                * output */
+               trust = query_trust_info(pk);
+               otrust = get_ownertrust_info( pk->local_id );
+           }
+
            tty_printf("%s%c %4u%c/%08lX  created: %s expires: %s",
                          node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
                          (node->flag & NODFLG_SELKEY)? '*':' ',
@@ -859,12 +886,11 @@ show_key_with_all_names( KBNODE keyblock, int only_marked,
                          datestr_from_pk(pk),
                          expirestr_from_pk(pk) );
            if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
-               int otrust, trust;
-               trust = query_trust_info(pk);
-               otrust = get_ownertrust_info( pk->local_id );
                tty_printf(" trust: %c/%c", otrust, trust );
-               if( with_fpr  )
+               if( with_fpr  ) {
+                   tty_printf("\n");
                    show_fingerprint( pk );
+               }
            }
            tty_printf("\n");
        }
@@ -896,6 +922,8 @@ show_key_with_all_names( KBNODE keyblock, int only_marked,
                   tty_printf("(%d)  ", i);
                tty_print_string( uid->name, uid->len );
                tty_printf("\n");
+               if( with_prefs )
+                   show_prefs( keyblock, uid );
            }
        }
     }
@@ -931,10 +959,11 @@ show_key_and_fingerprint( KBNODE keyblock )
 static void
 show_fingerprint( PKT_public_key *pk )
 {
-    byte *array, *p;
+    byte array[MAX_FINGERPRINT_LEN], *p;
     size_t i, n;
 
-    p = array = fingerprint_from_pk( pk, NULL, &n );
+    fingerprint_from_pk( pk, array, &n );
+    p = array;
     tty_printf("             Fingerprint:");
     if( n == 20 ) {
        for(i=0; i < n ; i++, i++, p += 2 ) {
@@ -951,7 +980,6 @@ show_fingerprint( PKT_public_key *pk )
        }
     }
     tty_printf("\n");
-    m_free(array);
 }
 
 
@@ -986,7 +1014,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock )
        pub_where = NULL;
     for( node = sec_keyblock; node; sec_where = node, node = node->next ) {
        if( node->pkt->pkttype == PKT_SECRET_KEY )
-           sk = node->pkt->pkt.secret_key;
+           sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
        else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
            break;
     }
@@ -996,6 +1024,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock )
 
     rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0,
                             keygen_add_std_prefs, sk );
+    free_secret_key( sk );
     if( rc ) {
        log_error("signing failed: %s\n", g10_errstr(rc) );
        free_user_id(uid);
@@ -1135,6 +1164,117 @@ menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
 }
 
 
+
+static int
+menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+    int n1, rc;
+    u32 expiredate;
+    int mainkey=0;
+    PKT_secret_key *sk;    /* copy of the main sk */
+    PKT_public_key *main_pk, *sub_pk;
+    PKT_user_id *uid;
+    KBNODE node;
+    u32 keyid[2];
+
+    if( count_selected_keys( sec_keyblock ) ) {
+       tty_printf(_("Please remove selections from the secret keys.\n"));
+       return 0;
+    }
+
+    n1 = count_selected_keys( pub_keyblock );
+    if( n1 > 1 ) {
+       tty_printf(_("Please select at most one secondary key.\n"));
+       return 0;
+    }
+    else if( n1 )
+       tty_printf(_("Changing exiration time for a secondary key.\n"));
+    else {
+       tty_printf(_("Changing exiration time for the primary key.\n"));
+       mainkey=1;
+    }
+
+    expiredate = ask_expiredate();
+    /* fixme: check that expiredate is > key creation date */
+
+    /* get the secret key , make a copy and set the expiration time into
+     * that key (because keygen_add-key-expire expects it there)
+     */
+    node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+    sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+    sk->expiredate = expiredate;
+
+    /* Now we can actually change the self signature(s) */
+    main_pk = sub_pk = NULL;
+    uid = NULL;
+    for( node=pub_keyblock; node; node = node->next ) {
+       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_PUBLIC_SUBKEY
+                && (node->flag & NODFLG_SELKEY ) )
+           sub_pk = node->pkt->pkt.public_key;
+       else if( node->pkt->pkttype == PKT_USER_ID )
+           uid = node->pkt->pkt.user_id;
+       else if( main_pk && node->pkt->pkttype == PKT_SIGNATURE ) {
+           PKT_signature *sig = node->pkt->pkt.signature;
+           if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+               && (    (mainkey && uid && (sig->sig_class&~3) == 0x10)
+                    || (!mainkey && sig->sig_class == 0x18)  ) ) {
+               /* this is a selfsignature which should be replaced */
+               PKT_signature *newsig;
+               PACKET *newpkt;
+               KBNODE sn;
+
+               /* find the corresponding secret self-signature */
+               for( sn=sec_keyblock; sn; sn = sn->next ) {
+                   if( sn->pkt->pkttype == PKT_SIGNATURE
+                       && !cmp_signatures( sn->pkt->pkt.signature, sig ) )
+                       break;
+               }
+               if( !sn )
+                   log_info(_("No corresponding signature in secret ring\n"));
+
+               /* create new self signature */
+               if( mainkey )
+                   rc = make_keysig_packet( &newsig, main_pk, uid, NULL,
+                                            sk, 0x13, 0,
+                                            keygen_add_std_prefs, sk );
+               else
+                   rc = make_keysig_packet( &newsig, main_pk, NULL, sub_pk,
+                                            sk, 0x18, 0,
+                                            keygen_add_key_expire, sk );
+               if( rc ) {
+                   log_error("make_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;
+               if( sn ) {
+                   newpkt = m_alloc_clear( sizeof *newpkt );
+                   newpkt->pkttype = PKT_SIGNATURE;
+                   newpkt->pkt.signature = copy_signature( NULL, newsig );
+                   free_packet( sn->pkt );
+                   m_free( sn->pkt );
+                   sn->pkt = newpkt;
+               }
+           }
+       }
+    }
+
+    free_secret_key( sk );
+    return 1;
+}
+
+
 /****************
  * Select one user id or remove all selection if index is 0.
  * Returns: True if the selection changed;