From 74c1f30ad6616186f0ab9dbaf34db6c17b1e40c4 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 21 Mar 2017 14:47:21 +0100 Subject: [PATCH] gpg: New command --quick-set-primary-uid. * 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 --- doc/gpg.texi | 21 +++-- g10/gpg.c | 20 ++++- g10/keyedit.c | 225 +++++++++++++++++++++++++++++++------------------- g10/main.h | 2 + 4 files changed, 174 insertions(+), 94 deletions(-) diff --git a/doc/gpg.texi b/doc/gpg.texi index 0e107ecb5..37e1ff10a 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -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. diff --git a/g10/gpg.c b/g10/gpg.c index eeda60f83..b3d606bc5 100644 --- 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: diff --git a/g10/keyedit.c b/g10/keyedit.c index 2b0f45e03..9a7fe1308 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -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 diff --git a/g10/main.h b/g10/main.h index c9c345418..32d323b6b 100644 --- a/g10/main.h +++ b/g10/main.h @@ -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 --*/