Some bug fixes of the last release
[gnupg.git] / g10 / keyedit.c
index 6f9c1c7..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 );
@@ -214,7 +215,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
     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;
 
@@ -426,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
@@ -440,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;
@@ -466,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 },
@@ -503,12 +544,16 @@ keyedit_menu( const char *username, STRLIST locusr )
            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 */
@@ -527,10 +572,12 @@ 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 )
@@ -601,7 +648,7 @@ keyedit_menu( const char *username, STRLIST locusr )
            }
            else
                tty_printf(_("Key not changed so no update needed.\n"));
-           rc = update_trust_record( keyblock );
+           rc = update_trust_record( keyblock, 0, NULL );
            if( rc )
                log_error(_("update of trust db failed: %s\n"),
                            g10_errstr(rc) );
@@ -659,7 +706,7 @@ keyedit_menu( const char *username, STRLIST locusr )
                sec_modified = modified = 1;
                /* must update the trustdb already here, so that preferences
                 * get listed correctly */
-               rc = update_trust_record( keyblock );
+               rc = update_trust_record( keyblock, 0, NULL );
                if( rc ) {
                    log_error(_("update of trust db failed: %s\n"),
                                g10_errstr(rc) );
@@ -719,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;
@@ -902,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 ) {
@@ -922,7 +980,6 @@ show_fingerprint( PKT_public_key *pk )
        }
     }
     tty_printf("\n");
-    m_free(array);
 }
 
 
@@ -1107,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;