Some bug fixes of the last release
[gnupg.git] / g10 / keyedit.c
index a0a6252..2d3a0d0 100644 (file)
@@ -48,6 +48,7 @@ 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 );
@@ -56,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 */
@@ -85,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;
 }
 
@@ -196,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;
@@ -210,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 );
@@ -225,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) )
@@ -255,7 +267,7 @@ 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, 0 );
        tty_printf("\n");
@@ -285,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;
@@ -301,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;
-    int yes;
-
-    /* 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;
-    }
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("%s: read problem: %s\n", username, g10_errstr(rc) );
-       goto leave;
+    if( upd_trust && primary_pk ) {
+       rc = clear_trust_checked_flag( primary_pk );
     }
 
-    /* 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 batchmode\n"));
-    else if( opt.batch && opt.answer_yes )
-       okay++;
-    else if( opt.batch )
-       log_error(_("can't do that in batchmode 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");
-
-       yes = cpr_get_answer_is_yes( secret? N_("delete_key.secret.okay")
-                          : N_("delete_key.okay"),
-                             _("Delete this key from the keyring? "));
-       if( !cpr_enabled() && secret && yes ) {
-           /* 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.
-            */
-           yes = cpr_get_answer_is_yes(N_("delete_key.secret.okay"),
-                        _("This is a secret key! - really delete? "));
-       }
-       if( yes )
-           okay++;
-    }
-
-
-    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.
@@ -457,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;
@@ -468,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 );
        }
     }
 
@@ -482,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"));
            }
@@ -527,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
@@ -541,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, cmdPREF,
+          cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE,
           cmdNOP };
     static struct { const char *name;
                    enum cmdids id;
@@ -567,6 +506,7 @@ 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 },
@@ -603,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 */
@@ -627,14 +572,18 @@ keyedit_menu( const char *username, STRLIST locusr )
            tty_printf("\n");
            redisplay = 0;
        }
-       m_free(answer);
-       answer = cpr_get(N_("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);
@@ -663,7 +612,7 @@ 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;
 
@@ -696,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:
@@ -752,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;
 
@@ -806,6 +766,16 @@ 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;
@@ -917,8 +887,10 @@ show_key_with_all_names( KBNODE keyblock, int only_marked,
                          expirestr_from_pk(pk) );
            if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
                tty_printf(" trust: %c/%c", otrust, trust );
-               if( with_fpr  )
+               if( with_fpr  ) {
+                   tty_printf("\n");
                    show_fingerprint( pk );
+               }
            }
            tty_printf("\n");
        }
@@ -987,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 ) {
@@ -1007,7 +980,6 @@ show_fingerprint( PKT_public_key *pk )
        }
     }
     tty_printf("\n");
-    m_free(array);
 }
 
 
@@ -1042,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;
     }
@@ -1052,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);
@@ -1191,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;