From 35b17550706c8b7479ae96654feb97c05263cfc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Thu, 21 Apr 2022 16:43:50 +0200 Subject: [PATCH] gpg: Look up user ID to revoke by UID hash * g10/keyedit.c (find_userid_by_namehash, find_userid): New. (keyedit_quick_revuid): Use find_userid() instead of iterating over the nodes of the keyblock. * tests/openpgp/quick-key-manipulation.scm: Add test for revoking a user ID specified by its hash. -- This makes it possible to specify the user ID to revoke as UID hash when calling --quick-revoke-uid. GnuPG-bug-id: 5936 --- g10/keyedit.c | 120 +++++++++++++++++------ tests/openpgp/quick-key-manipulation.scm | 6 ++ 2 files changed, 94 insertions(+), 32 deletions(-) diff --git a/g10/keyedit.c b/g10/keyedit.c index 9b76b53ca..e8d1dc7e7 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -2447,6 +2447,68 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid) } +/* Helper to find the UID node for namehash. On success, returns the UID node. + Otherwise, return NULL. */ +kbnode_t +find_userid_by_namehash (kbnode_t keyblock, const char *namehash) +{ + byte hash[NAMEHASH_LEN]; + kbnode_t node = NULL; + + if (!namehash) + goto leave; + + if (strlen (namehash) != NAMEHASH_LEN * 2) + goto leave; + + if (hex2bin (namehash, hash, NAMEHASH_LEN) < 0) + goto leave; + + for (node = keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + namehash_from_uid (node->pkt->pkt.user_id); + if (!memcmp (node->pkt->pkt.user_id->namehash, hash, NAMEHASH_LEN)) + break; + } + } + + leave: + return node; +} + + +/* Helper to find the UID node for uid. On success, returns the UID node. + Otherwise, return NULL. */ +kbnode_t +find_userid (kbnode_t keyblock, const char *uid) +{ + kbnode_t node = NULL; + size_t uidlen; + + if (!keyblock || !uid) + goto leave; + + /* First try to find UID by namehash. */ + node = find_userid_by_namehash (keyblock, uid); + if (node) + goto leave; + + uidlen = strlen (uid); + for (node = keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID + && uidlen == node->pkt->pkt.user_id->len + && !memcmp (node->pkt->pkt.user_id->name, uid, uidlen)) + break; + } + + leave: + return node; +} + + /* Unattended revocation of a keyid. USERNAME specifies the key. UIDTOREV is the user id revoke from the key. */ void @@ -2457,7 +2519,6 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev) kbnode_t keyblock = NULL; kbnode_t node; int modified = 0; - size_t revlen; size_t valid_uids; #ifdef HAVE_W32_SYSTEM @@ -2470,7 +2531,7 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev) if (err) goto leave; - /* Too make sure that we do not revoke the last valid UID, we first + /* To 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) @@ -2479,40 +2540,35 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev) && !node->pkt->pkt.user_id->flags.expired); /* Find the right UID. */ - revlen = strlen (uidtorev); - for (node = keyblock; node; node = node->next) + node = find_userid (keyblock, uidtorev); + if (node) { - if (node->pkt->pkttype == PKT_USER_ID - && revlen == node->pkt->pkt.user_id->len - && !memcmp (node->pkt->pkt.user_id->name, uidtorev, revlen)) + struct revocation_reason_info *reason; + + /* Make sure that we do not revoke the last valid UID. */ + if (valid_uids == 1 + && ! node->pkt->pkt.user_id->flags.revoked + && ! node->pkt->pkt.user_id->flags.expired) { - struct revocation_reason_info *reason; - - /* Make sure that we do not revoke the last valid UID. */ - if (valid_uids == 1 - && ! node->pkt->pkt.user_id->flags.revoked - && ! node->pkt->pkt.user_id->flags.expired) - { - log_error (_("cannot revoke the last valid user ID.\n")); - err = gpg_error (GPG_ERR_INV_USER_ID); - goto leave; - } - - reason = get_default_uid_revocation_reason (); - err = core_revuid (ctrl, keyblock, node, reason, &modified); - release_revocation_reason_info (reason); - if (err) - goto leave; - err = keydb_update_keyblock (ctrl, kdbhd, keyblock); - if (err) - { - log_error (_("update failed: %s\n"), gpg_strerror (err)); - goto leave; - } - - revalidation_mark (ctrl); + log_error (_("cannot revoke the last valid user ID.\n")); + err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; } + + reason = get_default_uid_revocation_reason (); + err = core_revuid (ctrl, keyblock, node, reason, &modified); + release_revocation_reason_info (reason); + if (err) + goto leave; + err = keydb_update_keyblock (ctrl, kdbhd, keyblock); + if (err) + { + log_error (_("update failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + revalidation_mark (ctrl); + goto leave; } err = gpg_error (GPG_ERR_NO_USER_ID); diff --git a/tests/openpgp/quick-key-manipulation.scm b/tests/openpgp/quick-key-manipulation.scm index 2023f17bd..6cdf19a34 100755 --- a/tests/openpgp/quick-key-manipulation.scm +++ b/tests/openpgp/quick-key-manipulation.scm @@ -34,6 +34,8 @@ (define alpha "Alpha ") (define bravo "Bravo ") (define charlie "Charlie ") +(define delta "Delta ") +(define deltahash "359DC5EFF98B14A58AAA615C638E8BD0CEDA537B") (define (key-data key) (filter (lambda (x) (or (string=? (car x) "pub") @@ -87,6 +89,10 @@ (info "Checking that we can revoke a user ID...") (call-check `(,@GPG --quick-revoke-uid ,(exact bravo) ,alpha)) +(info "Checking that we can revoke a user ID by its hash...") +(call-check `(,@GPG --quick-add-uid ,(exact bravo) ,delta)) +(call-check `(,@GPG --quick-revoke-uid ,(exact bravo) ,deltahash)) + (info "Checking that we get an error revoking a non-existent user ID.") (catch '() (call-check `(,@GPG --quick-revoke-uid ,(exact bravo) ,charlie))