gpg: New command --quick-set-primary-uid.
authorWerner Koch <wk@gnupg.org>
Tue, 21 Mar 2017 13:47:21 +0000 (14:47 +0100)
committerWerner Koch <wk@gnupg.org>
Tue, 21 Mar 2017 14:09:30 +0000 (15:09 +0100)
* g10/gpg.c (aQuickSetPrimaryUid): New const.
(opts): New command --quick-set-primary-uid.
(main): Implement it.
* g10/keyedit.c (keyedit_quick_adduid): Factor some code out to ...
(quick_find_keyblock): new func.
(keyedit_quick_revuid): Use quick_find_keyblock.
(keyedit_quick_set_primary): New.

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

index 0e107ec..37e1ff1 100644 (file)
@@ -1096,19 +1096,28 @@ on its form are applied.
 
 @item --quick-revoke-uid  @var{user-id} @var{user-id-to-revoke}
 @opindex quick-revoke-uid
-This command revokes a User ID on an existing key.  It cannot be used
-to revoke the last User ID on key (some non-revoked User ID must
+This command revokes a user ID on an existing key.  It cannot be used
+to revoke the last user ID on key (some non-revoked user ID must
 remain), with revocation reason ``User ID is no longer valid''.  If
 you want to specify a different revocation reason, or to supply
 supplementary revocation text, you should use the interactive
 sub-command @code{revuid} of @option{--edit-key}.
 
-@item --change-passphrase @var{user_id}
+@item --quick-set-primary-uid  @var{user-id} @var{primary-user-id}
+@opindex quick-set-primary-uid
+This command sets or updates the primary user ID flag on an existing
+key.  @var{user-id} specifies the key and @var{primary-user-id} the
+user ID which shall be flagged as the primary user ID.  The primary
+user ID flag is removed from all other user ids and the timestamp of
+all affected self-signatures is set one second ahead.
+
+
+@item --change-passphrase @var{user-id}
 @opindex change-passphrase
-@itemx --passwd @var{user_id}
+@itemx --passwd @var{user-id}
 @opindex passwd
 Change the passphrase of the secret key belonging to the certificate
-specified as @var{user_id}.  This is a shortcut for the sub-command
+specified as @var{user-id}.  This is a shortcut for the sub-command
 @code{passwd} of the edit key menu.
 
 @end table
@@ -1767,7 +1776,7 @@ when verifying signatures made by keys that are not on the local
 keyring.
 
 If the method "wkd" is included in the list of methods given to
-@option{auto-key-locate}, the Signer's User ID is part of the
+@option{auto-key-locate}, the signer's user ID is part of the
 signature, and the option @option{--disable-signer-uid} is not used,
 the "wkd" method may also be used to retrieve a key.
 
index eeda60f..b3d606b 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1,7 +1,7 @@
 /* gpg.c - The GnuPG utility (main for gpg)
  * Copyright (C) 1998-2011 Free Software Foundation, Inc.
- * Copyright (C) 1997-2016 Werner Koch
- * Copyright (C) 2015-2016 g10 Code GmbH
+ * Copyright (C) 1997-2017 Werner Koch
+ * Copyright (C) 2015-2017 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -124,6 +124,7 @@ enum cmd_and_opt_values
     aQuickAddKey,
     aQuickRevUid,
     aQuickSetExpire,
+    aQuickSetPrimaryUid,
     aListConfig,
     aListGcryptConfig,
     aGPGConfList,
@@ -460,6 +461,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_c (aQuickRevUid,  "quick-revuid", "@"),
   ARGPARSE_c (aQuickSetExpire,  "quick-set-expire",
               N_("quickly set a new expiration date")),
+  ARGPARSE_c (aQuickSetPrimaryUid,  "quick-set-primary-uid", "@"),
   ARGPARSE_c (aFullKeygen,  "full-generate-key" ,
               N_("full featured key pair generation")),
   ARGPARSE_c (aFullKeygen,  "full-gen-key", "@"),
@@ -2581,6 +2583,7 @@ main (int argc, char **argv)
          case aQuickAddKey:
          case aQuickRevUid:
          case aQuickSetExpire:
+         case aQuickSetPrimaryUid:
          case aExportOwnerTrust:
          case aImportOwnerTrust:
           case aRebuildKeydbCaches:
@@ -4002,6 +4005,7 @@ main (int argc, char **argv)
       case aQuickAddUid:
       case aQuickAddKey:
       case aQuickRevUid:
+      case aQuickSetPrimaryUid:
       case aFullKeygen:
       case aKeygen:
       case aImport:
@@ -4445,6 +4449,18 @@ main (int argc, char **argv)
         }
        break;
 
+      case aQuickSetPrimaryUid:
+        {
+          const char *uid, *primaryuid;
+
+          if (argc != 2)
+            wrong_args ("--quick-set-primary-uid USER-ID PRIMARY-USER-ID");
+          uid = *argv++; argc--;
+          primaryuid = *argv++; argc--;
+          keyedit_quick_set_primary (ctrl, uid, primaryuid);
+        }
+       break;
+
       case aFastImport:
         opt.import_options |= IMPORT_FAST;
       case aImport:
index 2b0f45e..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.
@@ -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->flags.revoked
-      && ! node->pkt->pkt.user_id->flags.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
@@ -3046,7 +3034,8 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
               && ! 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);
 
-  log_error (_("User ID revocation failed: %s\n"), gpg_strerror (GPG_ERR_NOT_FOUND));
+
+ 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);
+
+  if (err)
+    log_error (_("setting the primary user ID failed: %s\n"),
+               gpg_strerror (err));
 
  leave:
   release_kbnode (keyblock);
@@ -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
index c9c3454..32d323b 100644 (file)
@@ -300,6 +300,8 @@ void keyedit_quick_sign (ctrl_t ctrl, const char *fpr,
                          strlist_t uids, strlist_t locusr, int local);
 void keyedit_quick_set_expire (ctrl_t ctrl,
                                const char *fpr, const char *expirestr);
+void keyedit_quick_set_primary (ctrl_t ctrl, const char *username,
+                                const char *primaryuid);
 void show_basic_key_info (KBNODE keyblock);
 
 /*-- keygen.c --*/