gpg: New command --quick-adduid.
authorWerner Koch <wk@gnupg.org>
Fri, 8 May 2015 14:08:57 +0000 (16:08 +0200)
committerWerner Koch <wk@gnupg.org>
Fri, 8 May 2015 14:08:57 +0000 (16:08 +0200)
* g10/keygen.c (ask_user_id): Factor some code out to ...
(uid_already_in_keyblock): new.
(generate_user_id): Add arg UIDSTR.  Fix leaked P.
* g10/keyedit.c (menu_adduid): Add new arg uidstring.  Adjust caller.
(keyedit_quick_adduid): New.
* g10/gpg.c (aQuickAddUid): New.
(opts):  Add command --quick-adduid.
(main): Implement that.
--

GnuPG-bug-id: 1956
Signed-off-by: Werner Koch <wk@gnupg.org>
doc/gpg.texi
g10/gpg.c
g10/keyedit.c
g10/keygen.c
g10/main.h
g10/packet.h

index 2e72309..887a624 100644 (file)
@@ -568,7 +568,7 @@ This section explains the main commands for key management
 @ifset gpgtwoone
 @item --quick-gen-key @code{user-id}
 @opindex quick-gen-key
-This is simple command to generate a standard key with one user id.
+This is simple command to generate a standard key with one user id.
 In contrast to @option{--gen-key} the key is generated directly
 without the need to answer a bunch of prompts.  Unless the option
 @option{--yes} is given, the key creation will be canceled if the
@@ -945,6 +945,16 @@ Its intended use is to help unattended key signing by utilizing a list
 of verified fingerprints.
 @end ifset
 
+@ifset gpgtwoone
+@item --quick-adduid  @var{user-id} @var{new-user-id}
+@opindex quick-adduid
+This command adds a new user id to an existing key.  In contrast to
+the interactive sub-command @code{adduid} of @option{--edit-key} the
+@var{new-user-id} is added verbatim with only leading and trailing
+white space removed, it is expected to be UTF-8 encoded, and no checks
+on its form are applied.
+@end ifset
+
 @item --passwd @var{user_id}
 @opindex passwd
 Change the passphrase of the secret key belonging to the certificate
index 13d6884..5a8a662 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -116,6 +116,7 @@ enum cmd_and_opt_values
     aLSignKey,
     aQuickSignKey,
     aQuickLSignKey,
+    aQuickAddUid,
     aListConfig,
     aListGcryptConfig,
     aGPGConfList,
@@ -414,6 +415,8 @@ static ARGPARSE_OPTS opts[] = {
               N_("generate a new key pair")),
   ARGPARSE_c (aQuickKeygen, "quick-gen-key" ,
               N_("quickly generate a new key pair")),
+  ARGPARSE_c (aQuickAddUid,  "quick-adduid",
+              N_("quickly add a new user-id")),
   ARGPARSE_c (aFullKeygen,  "full-gen-key" ,
               N_("full featured key pair generation")),
   ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
@@ -2327,6 +2330,7 @@ main (int argc, char **argv)
          case aLSignKey:
          case aStore:
          case aQuickKeygen:
+         case aQuickAddUid:
          case aExportOwnerTrust:
          case aImportOwnerTrust:
           case aRebuildKeydbCaches:
@@ -3604,6 +3608,7 @@ main (int argc, char **argv)
       case aDeleteSecretKeys:
       case aDeleteSecretAndPublicKeys:
       case aQuickKeygen:
+      case aQuickAddUid:
       case aFullKeygen:
       case aKeygen:
       case aImport:
@@ -3924,6 +3929,18 @@ main (int argc, char **argv)
        }
        break;
 
+      case aQuickAddUid:
+        {
+          const char *uid, *newuid;
+
+          if (argc != 2)
+            wrong_args ("--quick-adduid USER-ID NEW-USER-ID");
+          uid = *argv++; argc--;
+          newuid = *argv++; argc--;
+          keyedit_quick_adduid (ctrl, uid, newuid);
+        }
+       break;
+
       case aFastImport:
         opt.import_options |= IMPORT_FAST;
       case aImport:
index 804eff1..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.
  *
@@ -59,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);
@@ -1757,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;
@@ -2288,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
@@ -3225,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;
@@ -3239,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)
@@ -3291,9 +3385,13 @@ 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);
index ccd01f9..fa5907d 100644 (file)
@@ -2446,6 +2446,24 @@ uid_from_string (const char *string)
 }
 
 
+/* Return true if the user id UID already exists in the keyblock.  */
+static int
+uid_already_in_keyblock (kbnode_t keyblock, const char *uid)
+{
+  PKT_user_id *uidpkt = uid_from_string (uid);
+  kbnode_t node;
+  int result = 0;
+
+  for (node=keyblock; node && !result; node=node->next)
+    if (!is_deleted_kbnode (node)
+        && node->pkt->pkttype == PKT_USER_ID
+        && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id))
+      result = 1;
+  free_user_id (uidpkt);
+  return result;
+}
+
+
 /* Ask for a user ID.  With a MODE of 1 an extra help prompt is
    printed for use during a new key creation.  If KEYBLOCK is not NULL
    the function prevents the creation of an already existing user
@@ -2583,17 +2601,11 @@ ask_user_id (int mode, int full, KBNODE keyblock)
 
         if (!fail && keyblock)
           {
-            PKT_user_id *uidpkt = uid_from_string (uid);
-            KBNODE node;
-
-            for (node=keyblock; node && !fail; node=node->next)
-              if (!is_deleted_kbnode (node)
-                  && node->pkt->pkttype == PKT_USER_ID
-                  && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id))
-               fail = 1;
-            if (fail)
-              tty_printf (_("Such a user ID already exists on this key!\n"));
-            free_user_id (uidpkt);
+            if (uid_already_in_keyblock (keyblock, uid))
+              {
+                tty_printf (_("Such a user ID already exists on this key!\n"));
+                fail = 1;
+              }
           }
 
        for(;;) {
@@ -2767,16 +2779,32 @@ do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root,
 
 /* Generate a new user id packet or return NULL if canceled.  If
    KEYBLOCK is not NULL the function prevents the creation of an
-   already existing user ID.  */
+   already existing user ID.  If UIDSTR is not NULL the user is not
+   asked but UIDSTR is used to create the user id packet; if the user
+   id already exists NULL is returned.  UIDSTR is expected to be utf-8
+   encoded and should have already been checked for a valid length
+   etc.  */
 PKT_user_id *
-generate_user_id (KBNODE keyblock)
+generate_user_id (KBNODE keyblock, const char *uidstr)
 {
+  PKT_user_id *uid;
   char *p;
 
-  p = ask_user_id (1, 1, keyblock);
-  if (!p)
-    return NULL;  /* Canceled. */
-  return uid_from_string (p);
+  if (uidstr)
+    {
+      if (uid_already_in_keyblock (keyblock, uidstr))
+        return NULL;  /* Already exists.  */
+      uid = uid_from_string (uidstr);
+    }
+  else
+    {
+      p = ask_user_id (1, 1, keyblock);
+      if (!p)
+        return NULL;  /* Canceled. */
+      uid = uid_from_string (p);
+      xfree (p);
+    }
+  return uid;
 }
 
 
index 01eeb7f..a89f711 100644 (file)
@@ -247,6 +247,8 @@ gpg_error_t delete_keys (strlist_t names, int secret, int allow_both);
 void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                   strlist_t commands, int quiet, int seckey_check );
 void keyedit_passwd (ctrl_t ctrl, const char *username);
+void keyedit_quick_adduid (ctrl_t ctrl, const char *username,
+                           const char *newuid);
 void keyedit_quick_sign (ctrl_t ctrl, const char *fpr,
                          strlist_t uids, strlist_t locusr, int local);
 void show_basic_key_info (KBNODE keyblock);
index ba43638..523178b 100644 (file)
@@ -544,6 +544,6 @@ int update_keysig_packet( PKT_signature **ret_sig,
                       void *opaque   );
 
 /*-- keygen.c --*/
-PKT_user_id *generate_user_id (KBNODE keyblock);
+PKT_user_id *generate_user_id (kbnode_t keyblock, const char *uidstr);
 
 #endif /*G10_PACKET_H*/