gpg: Prepare for listing last_update and key origin data.
[gnupg.git] / g10 / keyedit.c
index 660e8bf..9a7fe13 100644 (file)
@@ -1,6 +1,6 @@
 /* keyedit.c - Edit properties of a key
  * Copyright (C) 1998-2010 Free Software Foundation, Inc.
- * Copyright (C) 1998-2016 Werner Koch
+ * Copyright (C) 1998-2017 Werner Koch
  * Copyright (C) 2015, 2016 g10 Code GmbH
  *
  * This file is part of GnuPG.
@@ -1164,7 +1164,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
                       uidnode->flag &= ~NODFLG_MARK_A;
                       uidnode = NULL;
                     }
-                 else if (uidnode->pkt->pkt.user_id->is_revoked)
+                 else if (uidnode->pkt->pkt.user_id->flags.revoked)
                    {
                      tty_fprintf (fp, _("User ID \"%s\" is revoked."), user);
 
@@ -1192,7 +1192,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
                          tty_fprintf (fp, _("  Unable to sign.\n"));
                        }
                    }
-                 else if (uidnode->pkt->pkt.user_id->is_expired)
+                 else if (uidnode->pkt->pkt.user_id->flags.expired)
                    {
                      tty_fprintf (fp, _("User ID \"%s\" is expired."), user);
 
@@ -2860,36 +2860,28 @@ 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)
+/* Helper for quick commands to find the keyblock for USERNAME.
+ * Returns on success the key database handle at R_KDBHD and the
+ * keyblock at R_KEYBLOCK.  */
+static gpg_error_t
+quick_find_keyblock (ctrl_t ctrl, const char *username,
+                     KEYDB_HANDLE *r_kdbhd, kbnode_t *r_keyblock)
 {
   gpg_error_t err;
   KEYDB_HANDLE kdbhd = NULL;
-  KEYDB_SEARCH_DESC desc;
   kbnode_t keyblock = NULL;
+  KEYDB_SEARCH_DESC desc;
   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 (ctrl);
-#endif
+  *r_kdbhd = NULL;
+  *r_keyblock = NULL;
 
   /* Search the key; we don't want the whole getkey stuff here.  */
   kdbhd = keydb_new ();
   if (!kdbhd)
     {
       /* Note that keydb_new has already used log_error.  */
+      err = gpg_error_from_syserror ();
       goto leave;
     }
 
@@ -2917,24 +2909,65 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
 
       if (!err)
         {
-          /* We require the secret primary key to add a UID.  */
+          /* We require the secret primary key to set the primary UID.  */
           node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
-          if (!node)
-            BUG ();
+          log_assert (node);
           err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key);
         }
     }
+  else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    err = gpg_error (GPG_ERR_NO_PUBKEY);
+
   if (err)
     {
-      log_error (_("secret key \"%s\" not found: %s\n"),
+      log_error (_("key \"%s\" not found: %s\n"),
                  username, gpg_strerror (err));
       goto leave;
     }
 
   fix_keyblock (&keyblock);
-
   merge_keys_and_selfsig (keyblock);
 
+  *r_keyblock = keyblock;
+  keyblock = NULL;
+  *r_kdbhd = kdbhd;
+  kdbhd = NULL;
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+  return err;
+}
+
+
+/* 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;
+  kbnode_t keyblock = NULL;
+  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 (ctrl);
+#endif
+
+  /* Search the key; we don't want the whole getkey stuff here.  */
+  err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock);
+  if (err)
+    goto leave;
+
   if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring))
     {
       err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
@@ -2954,6 +2987,7 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
   keydb_release (kdbhd);
 }
 
+
 /* Unattended revocation of a keyid.  USERNAME specifies the
    key. UIDTOREV is the user id revoke from the key.  */
 void
@@ -2961,7 +2995,6 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
 {
   gpg_error_t err;
   KEYDB_HANDLE kdbhd = NULL;
-  KEYDB_SEARCH_DESC desc;
   kbnode_t keyblock = NULL;
   kbnode_t node;
   int modified = 0;
@@ -2974,65 +3007,20 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
 #endif
 
   /* Search the key; we don't want the whole getkey stuff here.  */
-  kdbhd = keydb_new ();
-  if (!kdbhd)
-    {
-      /* Note that keydb_new has already used log_error.  */
-      goto leave;
-    }
-
-  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 revoke a UID.  */
-          node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
-          if (!node)
-            BUG ();
-          err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key);
-        }
-    }
+  err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock);
   if (err)
-    {
-      log_error (_("secret key \"%s\" not found: %s\n"),
-                 username, gpg_strerror (err));
-      goto leave;
-    }
-
-  fix_keyblock (&keyblock);
-  merge_keys_and_selfsig (keyblock);
+    goto leave;
 
   /* Too make sure that we do not revoke the last valid UID, we first
      count how many valid UIDs there are.  */
   valid_uids = 0;
   for (node = keyblock; node; node = node->next)
-    valid_uids +=
-      node->pkt->pkttype == PKT_USER_ID
-      && ! node->pkt->pkt.user_id->is_revoked
-      && ! node->pkt->pkt.user_id->is_expired;
+    valid_uids += (node->pkt->pkttype == PKT_USER_ID
+                   && !node->pkt->pkt.user_id->flags.revoked
+                   && !node->pkt->pkt.user_id->flags.expired);
 
+  /* Find the right UID. */
   revlen = strlen (uidtorev);
-  /* find the right UID */
   for (node = keyblock; node; node = node->next)
     {
       if (node->pkt->pkttype == PKT_USER_ID
@@ -3043,10 +3031,11 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
 
           /* Make sure that we do not revoke the last valid UID.  */
           if (valid_uids == 1
-              && ! node->pkt->pkt.user_id->is_revoked
-              && ! node->pkt->pkt.user_id->is_expired)
+              && ! node->pkt->pkt.user_id->flags.revoked
+              && ! node->pkt->pkt.user_id->flags.expired)
             {
-              log_error (_("Cannot revoke the last valid user ID.\n"));
+              log_error (_("cannot revoke the last valid user ID.\n"));
+              err = gpg_error (GPG_ERR_INV_USER_ID);
               goto leave;
             }
 
@@ -3054,11 +3043,7 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
           err = core_revuid (ctrl, keyblock, node, reason, &modified);
           release_revocation_reason_info (reason);
           if (err)
-            {
-              log_error (_("User ID revocation failed: %s\n"),
-                         gpg_strerror (err));
-              goto leave;
-            }
+            goto leave;
           err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
           if (err)
             {
@@ -3066,13 +3051,81 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
               goto leave;
             }
 
-          if (update_trust)
-            revalidation_mark ();
+          revalidation_mark ();
+          goto leave;
+        }
+    }
+  err = gpg_error (GPG_ERR_NO_USER_ID);
+
+
+ leave:
+  if (err)
+    log_error (_("revoking the user ID failed: %s\n"), gpg_strerror (err));
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
+/* Unattended setting of the primary uid.  USERNAME specifies the key.
+   PRIMARYUID is the user id which shall be primary.  */
+void
+keyedit_quick_set_primary (ctrl_t ctrl, const char *username,
+                           const char *primaryuid)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE kdbhd = NULL;
+  kbnode_t keyblock = NULL;
+  kbnode_t node;
+  size_t primaryuidlen;
+  int any;
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock);
+  if (err)
+    goto leave;
+
+  /* Find and mark the UID - we mark only the first valid one. */
+  primaryuidlen = strlen (primaryuid);
+  any = 0;
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype == PKT_USER_ID
+          && !any
+          && !node->pkt->pkt.user_id->flags.revoked
+          && !node->pkt->pkt.user_id->flags.expired
+          && primaryuidlen == node->pkt->pkt.user_id->len
+          && !memcmp (node->pkt->pkt.user_id->name, primaryuid, primaryuidlen))
+        {
+          node->flag |= NODFLG_SELUID;
+          any = 1;
+        }
+      else
+        node->flag &= ~NODFLG_SELUID;
+    }
+
+  if (!any)
+    err = gpg_error (GPG_ERR_NO_USER_ID);
+  else if (menu_set_primary_uid (keyblock))
+    {
+      merge_keys_and_selfsig (keyblock);
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
           goto leave;
         }
+      revalidation_mark ();
     }
+  else
+    err = gpg_error (GPG_ERR_GENERAL);
 
-  log_error (_("User ID revocation failed: %s\n"), gpg_strerror (GPG_ERR_NOT_FOUND));
+  if (err)
+    log_error (_("setting the primary user ID failed: %s\n"),
+               gpg_strerror (err));
 
  leave:
   release_kbnode (keyblock);
@@ -3735,9 +3788,9 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
          else
            es_fputs ("uid:", fp);
 
-         if (uid->is_revoked)
+         if (uid->flags.revoked)
            es_fputs ("r::::::::", fp);
-         else if (uid->is_expired)
+         else if (uid->flags.expired)
            es_fputs ("e::::::::", fp);
          else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
            es_fputs ("::::::::", fp);
@@ -3785,11 +3838,11 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
          es_putc (':', fp);
          /* flags */
          es_fprintf (fp, "%d,", i);
-         if (uid->is_primary)
+         if (uid->flags.primary)
            es_putc ('p', fp);
-         if (uid->is_revoked)
+         if (uid->flags.revoked)
            es_putc ('r', fp);
-         if (uid->is_expired)
+         if (uid->flags.expired)
            es_putc ('e', fp);
          if ((node->flag & NODFLG_SELUID))
            es_putc ('s', fp);
@@ -3835,7 +3888,7 @@ show_names (ctrl_t ctrl, estream_t fp,
                tty_fprintf (fp, "     ");
              else if (node->flag & NODFLG_SELUID)
                tty_fprintf (fp, "(%d)* ", i);
-             else if (uid->is_primary)
+             else if (uid->flags.primary)
                tty_fprintf (fp, "(%d). ", i);
              else
                tty_fprintf (fp, "(%d)  ", i);
@@ -4167,9 +4220,9 @@ show_basic_key_info (KBNODE keyblock)
          ++i;
 
          tty_printf ("     ");
-         if (uid->is_revoked)
+         if (uid->flags.revoked)
            tty_printf ("[%s] ", _("revoked"));
-         else if (uid->is_expired)
+         else if (uid->flags.expired)
            tty_printf ("[%s] ", _("expired"));
          tty_print_utf8_string (uid->name, uid->len);
          tty_printf ("\n");
@@ -4277,7 +4330,7 @@ no_primary_warning (KBNODE keyblock)
        {
          uid_count++;
 
-         if (node->pkt->pkt.user_id->is_primary == 2)
+         if (node->pkt->pkt.user_id->flags.primary == 2)
            {
              have_primary = 1;
              break;
@@ -4478,7 +4531,7 @@ menu_deluid (KBNODE pub_keyblock)
            {
              /* Only cause a trust update if we delete a
                 non-revoked user id */
-             if (!node->pkt->pkt.user_id->is_revoked)
+             if (!node->pkt->pkt.user_id->flags.revoked)
                update_trust = 1;
              delete_kbnode (node);
            }
@@ -4598,9 +4651,9 @@ menu_clean (KBNODE keyblock, int self_only)
            {
              const char *reason;
 
-             if (uidnode->pkt->pkt.user_id->is_revoked)
+             if (uidnode->pkt->pkt.user_id->flags.revoked)
                reason = _("revoked");
-             else if (uidnode->pkt->pkt.user_id->is_expired)
+             else if (uidnode->pkt->pkt.user_id->flags.expired)
                reason = _("expired");
              else
                reason = _("invalid");
@@ -5205,7 +5258,7 @@ change_primary_uid_cb (PKT_signature * sig, void *opaque)
 
 /*
  * Set the primary uid flag for the selected UID.  We will also reset
- * all other primary uid flags.  For this to work with have to update
+ * all other primary uid flags.  For this to work we have to update
  * all the signature timestamps.  If we would do this with the current
  * time, we lose quite a lot of information, so we use a kludge to
  * do this: Just increment the timestamp by one second which is
@@ -6335,7 +6388,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
       /* Are we revoking our own uid? */
       if (primary_pk->keyid[0] == sig->keyid[0] &&
          primary_pk->keyid[1] == sig->keyid[1])
-       unode->pkt->pkt.user_id->is_revoked = 1;
+       unode->pkt->pkt.user_id->flags.revoked = 1;
       pkt = xmalloc_clear (sizeof *pkt);
       pkt->pkttype = PKT_SIGNATURE;
       pkt->pkt.signature = sig;
@@ -6369,7 +6422,7 @@ core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node,
     {
       PKT_user_id *uid = node->pkt->pkt.user_id;
 
-      if (uid->is_revoked)
+      if (uid->flags.revoked)
         {
           char *user = utf8_to_native (uid->name, uid->len, 0);
           log_info (_("user ID \"%s\" is already revoked\n"), user);
@@ -6429,7 +6482,7 @@ core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node,
                 update_trust = 1;
 #endif /*!NO_TRUST_MODELS*/
 
-              node->pkt->pkt.user_id->is_revoked = 1;
+              node->pkt->pkt.user_id->flags.revoked = 1;
               if (modified)
                 *modified = 1;
             }
@@ -6471,8 +6524,8 @@ menu_revuid (ctrl_t ctrl, kbnode_t pub_keyblock)
   for (node = pub_keyblock; node; node = node->next)
     valid_uids +=
       node->pkt->pkttype == PKT_USER_ID
-      && ! node->pkt->pkt.user_id->is_revoked
-      && ! node->pkt->pkt.user_id->is_expired;
+      && ! node->pkt->pkt.user_id->flags.revoked
+      && ! node->pkt->pkt.user_id->flags.expired;
 
  reloop: /* (better this way because we are modifying the keyring) */
   for (node = pub_keyblock; node; node = node->next)
@@ -6482,8 +6535,8 @@ menu_revuid (ctrl_t ctrl, kbnode_t pub_keyblock)
 
         /* Make sure that we do not revoke the last valid UID.  */
         if (valid_uids == 1
-            && ! node->pkt->pkt.user_id->is_revoked
-            && ! node->pkt->pkt.user_id->is_expired)
+            && ! node->pkt->pkt.user_id->flags.revoked
+            && ! node->pkt->pkt.user_id->flags.expired)
           {
             log_error (_("Cannot revoke the last valid user ID.\n"));
             goto leave;