gpg: Add hidden key-edit subcommand "change-usage".
authorWerner Koch <wk@gnupg.org>
Sun, 14 Feb 2016 14:50:12 +0000 (15:50 +0100)
committerWerner Koch <wk@gnupg.org>
Sun, 14 Feb 2016 15:01:53 +0000 (16:01 +0100)
* g10/keyedit.c (cmdCHANGEUSAGE): New.
(cmds): Add command "change-usage".
(keyedit_menu): Handle that command.
(menu_changeusage): New.
* g10/keygen.c (keygen_add_key_flags): New.
(ask_key_flags): Add optional arg current.
--

Signed-off-by: Werner Koch <wk@gnupg.org>
g10/keyedit.c
g10/keygen.c
g10/main.h

index 30f52a4..19ddf29 100644 (file)
@@ -70,6 +70,7 @@ static int menu_clean (KBNODE keyblock, int self_only);
 static void menu_delkey (KBNODE pub_keyblock);
 static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive);
 static int menu_expire (KBNODE pub_keyblock);
+static int menu_changeusage (kbnode_t keyblock);
 static int menu_backsign (KBNODE pub_keyblock);
 static int menu_set_primary_uid (KBNODE pub_keyblock);
 static int menu_set_preferences (KBNODE pub_keyblock);
@@ -1362,7 +1363,7 @@ enum cmdids
   cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG,
   cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY,
   cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
-  cmdEXPIRE, cmdBACKSIGN,
+  cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN,
 #ifndef NO_TRUST_MODELS
   cmdENABLEKEY, cmdDISABLEKEY,
 #endif /*!NO_TRUST_MODELS*/
@@ -1393,6 +1394,7 @@ static struct
   { "key", cmdSELKEY, 0, N_("select subkey N")},
   { "check", cmdCHECK, 0, N_("check signatures")},
   { "c", cmdCHECK, 0, NULL},
+  { "change-usage", cmdCHANGEUSAGE, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "cross-certify", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "backsign", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "sign", cmdSIGN, KEYEDIT_NOT_SK | KEYEDIT_TAIL_MATCH,
@@ -2122,6 +2124,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            }
          break;
 
+       case cmdCHANGEUSAGE:
+         if (menu_changeusage (keyblock))
+           {
+             merge_keys_and_selfsig (keyblock);
+             modified = 1;
+             redisplay = 1;
+           }
+         break;
+
        case cmdBACKSIGN:
          if (menu_backsign (keyblock))
            {
@@ -4110,6 +4121,112 @@ menu_expire (KBNODE pub_keyblock)
 }
 
 
+/* Change the capability of a selected key.  This command should only
+ * be used to rectify badly created keys and as such is not suggested
+ * for general use.  */
+static int
+menu_changeusage (kbnode_t keyblock)
+{
+  int n1, rc;
+  int mainkey = 0;
+  PKT_public_key *main_pk, *sub_pk;
+  PKT_user_id *uid;
+  kbnode_t node;
+  u32 keyid[2];
+
+  n1 = count_selected_keys (keyblock);
+  if (n1 > 1)
+    {
+      tty_printf (_("You must select exactly one key.\n"));
+      return 0;
+    }
+  else if (n1)
+    tty_printf ("Changing usage of a subkey.\n");
+  else
+    {
+      tty_printf ("Changing usage of the primary key.\n");
+      mainkey = 1;
+    }
+
+  /* Now we can actually change the self-signature(s) */
+  main_pk = sub_pk = NULL;
+  uid = NULL;
+  for (node = 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)
+       {
+          if (node->flag & NODFLG_SELKEY)
+            sub_pk = node->pkt->pkt.public_key;
+          else
+            sub_pk = NULL;
+       }
+      else if (node->pkt->pkttype == PKT_USER_ID)
+       uid = node->pkt->pkt.user_id;
+      else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE
+              && (mainkey || sub_pk))
+       {
+         PKT_signature *sig = node->pkt->pkt.signature;
+         if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+             && ((mainkey && uid
+                  && uid->created && (sig->sig_class & ~3) == 0x10)
+                 || (!mainkey && sig->sig_class == 0x18))
+             && sig->flags.chosen_selfsig)
+           {
+             /* This is the self-signature which is to be replaced.  */
+             PKT_signature *newsig;
+             PACKET *newpkt;
+
+             if ((mainkey && main_pk->version < 4)
+                 || (!mainkey && sub_pk->version < 4))
+               {
+                 log_info ("You can't change the capabilities of a v3 key\n");
+                 return 0;
+               }
+
+              if (mainkey)
+                main_pk->pubkey_usage = ask_key_flags (main_pk->pubkey_algo, 0,
+                                                       main_pk->pubkey_usage);
+              else
+                sub_pk->pubkey_usage  = ask_key_flags (sub_pk->pubkey_algo, 1,
+                                                       sub_pk->pubkey_usage);
+
+             if (mainkey)
+               rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL,
+                                          main_pk, keygen_add_key_flags,
+                                          main_pk);
+             else
+               rc =
+                 update_keysig_packet (&newsig, sig, main_pk, NULL, sub_pk,
+                                       main_pk, keygen_add_key_flags, sub_pk);
+             if (rc)
+               {
+                 log_error ("make_keysig_packet failed: %s\n",
+                            gpg_strerror (rc));
+                 return 0;
+               }
+
+             /* Replace the packet.  */
+             newpkt = xmalloc_clear (sizeof *newpkt);
+             newpkt->pkttype = PKT_SIGNATURE;
+             newpkt->pkt.signature = newsig;
+             free_packet (node->pkt);
+             xfree (node->pkt);
+             node->pkt = newpkt;
+             sub_pk = NULL;
+              break;
+           }
+       }
+    }
+
+  return 1;
+}
+
+
 static int
 menu_backsign (KBNODE pub_keyblock)
 {
index 0f7a6a0..7b5a35b 100644 (file)
@@ -252,6 +252,18 @@ keygen_add_key_expire (PKT_signature *sig, void *opaque)
 }
 
 
+/* Add the key usage (i.e. key flags) in SIG from the public keys
+ * pubkey_usage field.  OPAQUE has the public key.  */
+int
+keygen_add_key_flags (PKT_signature *sig, void *opaque)
+{
+  PKT_public_key *pk = opaque;
+
+  do_add_key_flags (sig, pk->pubkey_usage);
+  return 0;
+}
+
+
 static int
 keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque)
 {
@@ -1646,9 +1658,10 @@ print_key_flags(int flags)
 }
 
 
-/* Returns the key flags */
-static unsigned int
-ask_key_flags(int algo,int subkey)
+/* Ask for the key flags and return them.  CURRENT gives the curren
+ * usage which should normally be given as 0. */
+unsigned int
+ask_key_flags (int algo, int subkey, unsigned int current)
 {
   /* TRANSLATORS: Please use only plain ASCII characters for the
      translation.  If this is not possible use single digits.  The
@@ -1663,7 +1676,6 @@ ask_key_flags(int algo,int subkey)
   const char *togglers=_("SsEeAaQq");
   char *answer=NULL;
   const char *s;
-  unsigned int current=0;
   unsigned int possible=openpgp_pk_algo_usage(algo);
 
   if ( strlen(togglers) != 8 )
@@ -1678,8 +1690,12 @@ ask_key_flags(int algo,int subkey)
     possible&=~PUBKEY_USAGE_CERT;
 
   /* Preload the current set with the possible set, minus
-     authentication, since nobody really uses auth yet. */
-  current=possible&~PUBKEY_USAGE_AUTH;
+     authentication if CURRENT has been given as 0.  If CURRENT has
+     been has non-zero we mask with all possible usages. */
+  if (current)
+    current &= possible;
+  else
+    current = (possible&~PUBKEY_USAGE_AUTH);
 
   for(;;)
     {
@@ -1922,13 +1938,13 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
       else if ((algo == 7 || !strcmp (answer, "dsa/*")) && opt.expert)
         {
           algo = PUBKEY_ALGO_DSA;
-          *r_usage = ask_key_flags (algo, addmode);
+          *r_usage = ask_key_flags (algo, addmode, 0);
           break;
        }
       else if ((algo == 8 || !strcmp (answer, "rsa/*")) && opt.expert)
         {
           algo = PUBKEY_ALGO_RSA;
-          *r_usage = ask_key_flags (algo, addmode);
+          *r_usage = ask_key_flags (algo, addmode, 0);
           break;
        }
       else if ((algo == 9 || !strcmp (answer, "ecc+ecc"))
@@ -1947,7 +1963,7 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
       else if ((algo == 11 || !strcmp (answer, "ecc/*")) && opt.expert)
         {
           algo = PUBKEY_ALGO_ECDSA;
-          *r_usage = ask_key_flags (algo, addmode);
+          *r_usage = ask_key_flags (algo, addmode, 0);
           break;
        }
       else if ((algo == 12 || !strcmp (answer, "ecc/e"))
@@ -1985,7 +2001,7 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
           xfree (keygrip);
           keygrip = answer;
           answer = NULL;
-          *r_usage = ask_key_flags (algo, addmode);
+          *r_usage = ask_key_flags (algo, addmode, 0);
           break;
        }
       else
index ec24426..863afa9 100644 (file)
@@ -280,12 +280,14 @@ void show_basic_key_info (KBNODE keyblock);
 u32 parse_expire_string(const char *string);
 u32 ask_expire_interval(int object,const char *def_expire);
 u32 ask_expiredate(void);
+unsigned int ask_key_flags (int algo, int subkey, unsigned int current);
 void quick_generate_keypair (ctrl_t ctrl, const char *uid);
 void generate_keypair (ctrl_t ctrl, int full, const char *fname,
                        const char *card_serialno, int card_backup_key);
 int keygen_set_std_prefs (const char *string,int personal);
 PKT_user_id *keygen_get_std_prefs (void);
 int keygen_add_key_expire( PKT_signature *sig, void *opaque );
+int keygen_add_key_flags (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 keygen_add_keyserver_url(PKT_signature *sig, void *opaque);