gpg: Fix wrong output in list mode.
[gnupg.git] / g10 / keyedit.c
index a8e6f5d..be15b09 100644 (file)
@@ -1,7 +1,6 @@
-/* keyedit.c - keyedit stuff
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- *               2008, 2009, 2010 Free Software Foundation, Inc.
- * Copyright (C) 2013, 2014 Werner Koch
+/* keyedit.c - Edit properties of a key
+ * Copyright (C) 1998-2010 Free Software Foundation, Inc.
+ * Copyright (C) 1998-2015 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -31,7 +30,6 @@
 # include <readline/readline.h>
 #endif
 
-#define JNLIB_NEED_LOG_LOGV
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
@@ -60,7 +58,8 @@ static void show_key_with_all_names (estream_t fp,
                                      int nowarn);
 static void show_key_and_fingerprint (KBNODE keyblock);
 static void subkey_expire_warning (kbnode_t keyblock);
-static int menu_adduid (KBNODE keyblock, int photo, const char *photo_name);
+static int menu_adduid (KBNODE keyblock, int photo, const char *photo_name,
+                        const char *uidstr);
 static void menu_deluid (KBNODE pub_keyblock);
 static int menu_delsig (KBNODE pub_keyblock);
 static int menu_clean (KBNODE keyblock, int self_only);
@@ -128,20 +127,21 @@ print_and_check_one_sig_colon (KBNODE keyblock, KBNODE node,
   /* TODO: Make sure a cached sig record here still has the pk that
      issued it.  See also keylist.c:list_keyblock_print */
 
-  switch ((rc = check_key_signature (keyblock, node, is_selfsig)))
+  rc = check_key_signature (keyblock, node, is_selfsig);
+  switch (gpg_err_code (rc))
     {
     case 0:
       node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR);
       sigrc = '!';
       break;
-    case G10ERR_BAD_SIGN:
+    case GPG_ERR_BAD_SIGNATURE:
       node->flag = NODFLG_BADSIG;
       sigrc = '-';
       if (inv_sigs)
        ++ * inv_sigs;
       break;
-    case G10ERR_NO_PUBKEY:
-    case G10ERR_UNU_PUBKEY:
+    case GPG_ERR_NO_PUBKEY:
+    case GPG_ERR_UNUSABLE_PUBKEY:
       node->flag = NODFLG_NOKEY;
       sigrc = '?';
       if (no_key)
@@ -157,23 +157,23 @@ print_and_check_one_sig_colon (KBNODE keyblock, KBNODE node,
 
   if (sigrc != '?' || print_without_key)
     {
-      printf ("sig:%c::%d:%08lX%08lX:%lu:%lu:",
-             sigrc, sig->pubkey_algo, (ulong) sig->keyid[0],
-             (ulong) sig->keyid[1], (ulong) sig->timestamp,
-             (ulong) sig->expiredate);
+      es_printf ("sig:%c::%d:%08lX%08lX:%lu:%lu:",
+                 sigrc, sig->pubkey_algo, (ulong) sig->keyid[0],
+                 (ulong) sig->keyid[1], (ulong) sig->timestamp,
+                 (ulong) sig->expiredate);
 
       if (sig->trust_depth || sig->trust_value)
-       printf ("%d %d", sig->trust_depth, sig->trust_value);
+       es_printf ("%d %d", sig->trust_depth, sig->trust_value);
 
-      printf (":");
+      es_printf (":");
 
       if (sig->trust_regexp)
        es_write_sanitized (es_stdout,
                            sig->trust_regexp, strlen (sig->trust_regexp),
                            ":", NULL);
 
-      printf ("::%02x%c\n", sig->sig_class,
-             sig->flags.exportable ? 'x' : 'l');
+      es_printf ("::%02x%c\n", sig->sig_class,
+                 sig->flags.exportable ? 'x' : 'l');
 
       if (opt.show_subpackets)
        print_subpackets_colon (sig);
@@ -199,20 +199,21 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
   /* TODO: Make sure a cached sig record here still has the pk that
      issued it.  See also keylist.c:list_keyblock_print */
 
-  switch ((rc = check_key_signature (keyblock, node, is_selfsig)))
+  rc = check_key_signature (keyblock, node, is_selfsig);
+  switch (gpg_err_code (rc))
     {
     case 0:
       node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR);
       sigrc = '!';
       break;
-    case G10ERR_BAD_SIGN:
+    case GPG_ERR_BAD_SIGNATURE:
       node->flag = NODFLG_BADSIG;
       sigrc = '-';
       if (inv_sigs)
        ++ * inv_sigs;
       break;
-    case G10ERR_NO_PUBKEY:
-    case G10ERR_UNU_PUBKEY:
+    case GPG_ERR_NO_PUBKEY:
+    case GPG_ERR_UNUSABLE_PUBKEY:
       node->flag = NODFLG_NOKEY;
       sigrc = '?';
       if (no_key)
@@ -245,7 +246,7 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
        tty_printf (" %s", expirestr_from_sig (sig));
       tty_printf ("  ");
       if (sigrc == '%')
-       tty_printf ("[%s] ", g10_errstr (rc));
+       tty_printf ("[%s] ", gpg_strerror (rc));
       else if (sigrc == '?')
        ;
       else if (*is_selfsig)
@@ -1039,7 +1040,7 @@ sign_uids (estream_t fp,
                                          NULL);
              if (rc)
                {
-                 log_error (_("signing failed: %s\n"), g10_errstr (rc));
+                 log_error (_("signing failed: %s\n"), gpg_strerror (rc));
                  goto leave;
                }
 
@@ -1154,7 +1155,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
           if (err)
             log_log ((gpg_err_code (err) == GPG_ERR_CANCELED
                       || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
-                     ? JNLIB_LOG_INFO : JNLIB_LOG_ERROR,
+                     ? GPGRT_LOG_INFO : GPGRT_LOG_ERROR,
                      _("key %s: error changing passphrase: %s\n"),
                        keystr_with_sub (keyid, subid),
                        gpg_strerror (err));
@@ -1180,7 +1181,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
  * Note:  This function does not work if there is more than one user ID.
  */
 static int
-fix_keyblock (KBNODE keyblock)
+fix_key_signature_order (KBNODE keyblock)
 {
   KBNODE node, last, subkey;
   int fixed = 0;
@@ -1220,6 +1221,27 @@ fix_keyblock (KBNODE keyblock)
 }
 
 
+/* Fix various problems in the keyblock.  Returns true if the keyblock
+   was changed.  Note that a pointer to the keyblock must be given and
+   the function may change it (i.e. replacing the first node).  */
+static int
+fix_keyblock (kbnode_t *keyblockp)
+{
+  int changed = 0;
+
+  if (fix_key_signature_order (*keyblockp))
+    changed++;
+  if (collapse_uids (keyblockp))
+    changed++;
+  reorder_keyblock (*keyblockp);
+  /* If we modified the keyblock, make sure the flags are right. */
+  if (changed)
+    merge_keys_and_selfsig (*keyblockp);
+
+  return changed;
+}
+
+
 static int
 parse_sign_type (const char *str, int *localsig, int *nonrevokesig,
                 int *trustsig)
@@ -1448,6 +1470,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
   char *answer = NULL;
   int redisplay = 1;
   int modified = 0;
+  int sec_shadowing = 0;
   int run_subkey_warnings = 0;
   int toggle;
   int have_commands = !!commands;
@@ -1480,15 +1503,9 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
       log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err));
       goto leave;
     }
-  if (fix_keyblock (keyblock))
-    modified++;
-  if (collapse_uids (&keyblock))
+
+  if (fix_keyblock (&keyblock))
     modified++;
-  reorder_keyblock (keyblock);
-  /* We modified the keyblock, so let's make sure the flags are
-     right. */
-  if (modified)
-    merge_keys_and_selfsig (keyblock);
 
   /* See whether we have a matching secret key.  */
   if (seckey_check)
@@ -1740,7 +1757,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          photo = 1;
          /* fall through */
        case cmdADDUID:
-         if (menu_adduid (keyblock, photo, arg_string))
+         if (menu_adduid (keyblock, photo, arg_string, NULL))
            {
              update_trust = 1;
              redisplay = 1;
@@ -1834,8 +1851,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0))
                  {
                    redisplay = 1;
-                   /* Only the secret key has been modified; thus
-                       there is no need to set the modified flag.  */
+                   sec_shadowing = 1;
                  }
              }
          }
@@ -1883,11 +1899,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char *) fname);
            if (!err && pkt->pkttype != PKT_SECRET_KEY
                && pkt->pkttype != PKT_SECRET_SUBKEY)
-             err = G10ERR_NO_SECKEY;
+             err = GPG_ERR_NO_SECKEY;
            if (err)
              {
                tty_printf (_("Error reading backup key from '%s': %s\n"),
-                           fname, g10_errstr (err));
+                           fname, gpg_strerror (err));
                free_packet (pkt);
                xfree (pkt);
                break;
@@ -1921,7 +1937,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                if (card_store_subkey (node, 0))
                  {
                    redisplay = 1;
-                   /* FIXME:sec_modified = 1;*/
+                   sec_shadowing = 1;
                  }
              }
            release_kbnode (node);
@@ -2180,7 +2196,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
        case cmdQUIT:
          if (have_commands)
            goto leave;
-         if (!modified)
+         if (!modified && !sec_shadowing)
            goto leave;
          if (!cpr_get_answer_is_yes ("keyedit.save.okay",
                                      _("Save changes? (y/N) ")))
@@ -2198,11 +2214,22 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
               err = keydb_update_keyblock (kdbhd, keyblock);
               if (err)
                 {
-                  log_error (_("update failed: %s\n"), g10_errstr (err));
+                  log_error (_("update failed: %s\n"), gpg_strerror (err));
                   break;
                 }
            }
-         else
+
+         if (sec_shadowing)
+           {
+             err = agent_scd_learn (NULL, 1);
+             if (err)
+                {
+                  log_error (_("update failed: %s\n"), gpg_strerror (err));
+                  break;
+                }
+           }
+
+         if (!modified && !sec_shadowing)
            tty_printf (_("Key not changed so no update needed.\n"));
 
          if (update_trust)
@@ -2261,6 +2288,93 @@ leave:
 }
 
 
+/* Unattended adding of a new keyid.  USERNAME specifies the
+   key. NEWUID is the new user id to add to the key.  */
+void
+keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE kdbhd = NULL;
+  KEYDB_SEARCH_DESC desc;
+  kbnode_t keyblock = NULL;
+  kbnode_t node;
+  char *uidstring = NULL;
+
+  uidstring = xstrdup (newuid);
+  trim_spaces (uidstring);
+  if (!*uidstring)
+    {
+      log_error ("%s\n", gpg_strerror (GPG_ERR_INV_USER_ID));
+      goto leave;
+    }
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale ();
+#endif
+
+  /* Search the key; we don't want the whole getkey stuff here.  */
+  kdbhd = keydb_new ();
+  err = classify_user_id (username, &desc, 1);
+  if (!err)
+    err = keydb_search (kdbhd, &desc, 1, NULL);
+  if (!err)
+    {
+      err = keydb_get_keyblock (kdbhd, &keyblock);
+      if (err)
+        {
+          log_error (_("error reading keyblock: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+      /* Now with the keyblock retrieved, search again to detect an
+         ambiguous specification.  We need to save the found state so
+         that we can do an update later.  */
+      keydb_push_found_state (kdbhd);
+      err = keydb_search (kdbhd, &desc, 1, NULL);
+      if (!err)
+        err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+      else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+        err = 0;
+      keydb_pop_found_state (kdbhd);
+
+      if (!err)
+        {
+          /* We require the secret primary key to add a UID.  */
+          node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
+          if (!node)
+            BUG ();
+          err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key);
+        }
+    }
+  if (err)
+    {
+      log_error (_("secret key \"%s\" not found: %s\n"),
+                 username, gpg_strerror (err));
+      goto leave;
+    }
+
+  fix_keyblock (&keyblock);
+
+  if (menu_adduid (keyblock, 0, NULL, uidstring))
+    {
+      err = keydb_update_keyblock (kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+
+      if (update_trust)
+        revalidation_mark ();
+    }
+
+ leave:
+  xfree (uidstring);
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
 /* Unattended key signing function.  If the key specifified by FPR is
    availabale and FPR is the primary fingerprint all user ids of the
    user ids of the key are signed using the default signing key.  If
@@ -2304,11 +2418,6 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
       log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err));
       goto leave;
     }
-  if (fix_keyblock (keyblock))
-    modified++;
-  if (collapse_uids (&keyblock))
-    modified++;
-  reorder_keyblock (keyblock);
 
   /* Check that the primary fingerprint has been given. */
   {
@@ -2337,9 +2446,8 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
       }
   }
 
-  /* If we modified the keyblock, make sure the flags are right. */
-  if (modified)
-    merge_keys_and_selfsig (keyblock);
+  if (fix_keyblock (&keyblock))
+    modified++;
 
   /* Give some info in verbose.  */
   if (opt.verbose)
@@ -3204,11 +3312,15 @@ subkey_expire_warning (kbnode_t keyblock)
 
 
 /*
- * Ask for a new user id, add the self-signature and update the keyblock.
- * Return true if there is a new user id
+ * Ask for a new user id, add the self-signature, and update the
+ * keyblock.  If UIDSTRING is not NULL the user ID is generated
+ * unattended using that string.  UIDSTRING is expected to be utf-8
+ * encoded and white space trimmed.  Returns true if there is a new
+ * user id.
  */
 static int
-menu_adduid (KBNODE pub_keyblock, int photo, const char *photo_name)
+menu_adduid (kbnode_t pub_keyblock, int photo, const char *photo_name,
+             const char *uidstring)
 {
   PKT_user_id *uid;
   PKT_public_key *pk = NULL;
@@ -3218,6 +3330,9 @@ menu_adduid (KBNODE pub_keyblock, int photo, const char *photo_name)
   KBNODE pub_where = NULL;
   gpg_error_t err;
 
+  if (photo && uidstring)
+    return 0;  /* Not allowed.  */
+
   for (node = pub_keyblock; node; pub_where = node, node = node->next)
     {
       if (node->pkt->pkttype == PKT_PUBLIC_KEY)
@@ -3270,15 +3385,19 @@ menu_adduid (KBNODE pub_keyblock, int photo, const char *photo_name)
       uid = generate_photo_id (pk, photo_name);
     }
   else
-    uid = generate_user_id (pub_keyblock);
+    uid = generate_user_id (pub_keyblock, uidstring);
   if (!uid)
-    return 0;
+    {
+      if (uidstring)
+        log_error ("%s", _("Such a user ID already exists on this key!\n"));
+      return 0;
+    }
 
   err = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x13, 0, 0, 0,
                             keygen_add_std_prefs, pk, NULL);
   if (err)
     {
-      log_error ("signing failed: %s\n", g10_errstr (err));
+      log_error ("signing failed: %s\n", gpg_strerror (err));
       free_user_id (uid);
       return 0;
     }
@@ -3577,7 +3696,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
       if (rc)
        {
          log_error (_("key \"%s\" not found: %s\n"), answer,
-                    g10_errstr (rc));
+                    gpg_strerror (rc));
          xfree (answer);
          continue;
        }
@@ -3662,7 +3781,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
                           keygen_add_revkey, &revkey, NULL);
   if (rc)
     {
-      log_error ("signing failed: %s\n", g10_errstr (rc));
+      log_error ("signing failed: %s\n", gpg_strerror (rc));
       goto fail;
     }
 
@@ -3766,7 +3885,7 @@ menu_expire (KBNODE pub_keyblock)
              if (rc)
                {
                  log_error ("make_keysig_packet failed: %s\n",
-                            g10_errstr (rc));
+                            gpg_strerror (rc));
                  return 0;
                }
 
@@ -3882,13 +4001,13 @@ menu_backsign (KBNODE pub_keyblock)
          else
            {
              log_error ("update_keysig_packet failed: %s\n",
-                        g10_errstr (rc));
+                        gpg_strerror (rc));
              break;
            }
        }
       else
        {
-         log_error ("make_backsig failed: %s\n", g10_errstr (rc));
+         log_error ("make_backsig failed: %s\n", gpg_strerror (rc));
          break;
        }
     }
@@ -4024,7 +4143,7 @@ menu_set_primary_uid (KBNODE pub_keyblock)
                      if (rc)
                        {
                          log_error ("update_keysig_packet failed: %s\n",
-                                    g10_errstr (rc));
+                                    gpg_strerror (rc));
                          return 0;
                        }
                      /* replace the packet */
@@ -4113,7 +4232,7 @@ menu_set_preferences (KBNODE pub_keyblock)
                  if (rc)
                    {
                      log_error ("update_keysig_packet failed: %s\n",
-                                g10_errstr (rc));
+                                gpg_strerror (rc));
                      return 0;
                    }
                  /* replace the packet */
@@ -4165,7 +4284,7 @@ menu_set_keyserver_url (const char *url, KBNODE pub_keyblock)
     {
       struct keyserver_spec *keyserver = NULL;
       /* Sanity check the format */
-      keyserver = parse_keyserver_uri (answer, 1, NULL, 0);
+      keyserver = parse_keyserver_uri (answer, 1);
       xfree (answer);
       if (!keyserver)
        {
@@ -4248,7 +4367,7 @@ menu_set_keyserver_url (const char *url, KBNODE pub_keyblock)
                  if (rc)
                    {
                      log_error ("update_keysig_packet failed: %s\n",
-                                g10_errstr (rc));
+                                gpg_strerror (rc));
                      xfree (uri);
                      return 0;
                    }
@@ -4447,7 +4566,7 @@ menu_set_notation (const char *string, KBNODE pub_keyblock)
                  if (rc)
                    {
                      log_error ("update_keysig_packet failed: %s\n",
-                                g10_errstr (rc));
+                                gpg_strerror (rc));
                      free_notation (notation);
                      xfree (user);
                      return 0;
@@ -4733,11 +4852,11 @@ ask_revoke_sig (KBNODE keyblock, KBNODE node)
        printf ("uat:::::::::%u %lu", uid->numattribs, uid->attrib_len);
       else
        {
-         printf ("uid:::::::::");
+         es_printf ("uid:::::::::");
          es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
        }
 
-      printf ("\n");
+      es_printf ("\n");
 
       print_and_check_one_sig_colon (keyblock, node, NULL, NULL, NULL, NULL,
                                     1);
@@ -4953,7 +5072,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
       free_public_key (signerkey);
       if (rc)
        {
-         log_error (_("signing failed: %s\n"), g10_errstr (rc));
+         log_error (_("signing failed: %s\n"), gpg_strerror (rc));
          release_revocation_reason_info (reason);
          return changed;
        }
@@ -5044,7 +5163,7 @@ menu_revuid (KBNODE pub_keyblock)
                                     sign_mk_attrib, &attrib, NULL);
            if (rc)
              {
-               log_error (_("signing failed: %s\n"), g10_errstr (rc));
+               log_error (_("signing failed: %s\n"), gpg_strerror (rc));
                goto leave;
              }
            else
@@ -5108,7 +5227,7 @@ menu_revkey (KBNODE pub_keyblock)
                           revocation_reason_build_cb, reason, NULL);
   if (rc)
     {
-      log_error (_("signing failed: %s\n"), g10_errstr (rc));
+      log_error (_("signing failed: %s\n"), gpg_strerror (rc));
       goto scram;
     }
 
@@ -5169,7 +5288,7 @@ menu_revsubkey (KBNODE pub_keyblock)
                                    NULL);
          if (rc)
            {
-             log_error (_("signing failed: %s\n"), g10_errstr (rc));
+             log_error (_("signing failed: %s\n"), gpg_strerror (rc));
              release_revocation_reason_info (reason);
              return changed;
            }