From b55b72bb815ad5870456b89c3a011fa00991b4a8 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 21 Jul 2017 14:12:55 +0200 Subject: [PATCH] gpg: Extend --quick-set-expire to allow subkey expiration setting. * g10/keyedit.c (keyedit_quick_set_expire): Add new arg subkeyfprs. (menu_expire): Rename arg force_mainkey to unattended and allow unattended changing of subkey expiration. * g10/gpg.c (main): Extend --quick-set-expire. Signed-off-by: Werner Koch --- doc/gpg.texi | 12 +++-- g10/gpg.c | 6 +-- g10/keyedit.c | 125 +++++++++++++++++++++++++++++++++++++++++--------- g10/keyedit.h | 3 +- 4 files changed, 117 insertions(+), 29 deletions(-) diff --git a/doc/gpg.texi b/doc/gpg.texi index 8ee745cd8..1b5dee0b5 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -663,10 +663,16 @@ supplied passphrase is used for the new key and the agent does not ask for it. To create a key without any protection @code{--passphrase ''} may be used. -@item --quick-set-expire @code{fpr} @code{expire} +@item --quick-set-expire @var{fpr} @var{expire} [*|@var{subfprs}] @opindex quick-set-expire -Directly set the expiration time of the primary key to @code{expire}. -To remove the expiration time @code{0} can be used. +With two arguments given, directly set the expiration time of the +primary key identified by @var{fpr} to @var{expire}. To remove the +expiration time @code{0} can be used. With three arguments and the +third given as an asterisk, the expiration time of all non-revoked and +not yet expired subkeys are set to @var{expire}. With more than two +arguments and a list of fingerprints given for @var{subfprs}, all +non-revoked subkeys matching these fingerprints are set to +@var{expire}. @item --quick-add-key @code{fpr} [@code{algo} [@code{usage} [@code{expire}]]] diff --git a/g10/gpg.c b/g10/gpg.c index 70b8011a5..e32e14a9d 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -4491,11 +4491,11 @@ main (int argc, char **argv) { const char *x_fpr, *x_expire; - if (argc != 2) - wrong_args ("--quick-set-exipre FINGERPRINT EXPIRE"); + if (argc < 2) + wrong_args ("--quick-set-exipre FINGERPRINT EXPIRE [SUBKEY-FPRS]"); x_fpr = *argv++; argc--; x_expire = *argv++; argc--; - keyedit_quick_set_expire (ctrl, x_fpr, x_expire); + keyedit_quick_set_expire (ctrl, x_fpr, x_expire, argv); } break; diff --git a/g10/keyedit.c b/g10/keyedit.c index 29fe4667a..0a90cf5b7 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -73,7 +73,7 @@ static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, int self_only); static void menu_delkey (KBNODE pub_keyblock); static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive); static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, - int force_mainkey, u32 newexpiration); + int unattended, u32 newexpiration); static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock); static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock); @@ -2808,18 +2808,24 @@ keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, } -/* Unattended expiration setting function for the main key. - * +/* Unattended expiration setting function for the main key. If + * SUBKEYFPRS is not NULL and SUBKEYSFPRS[0] is neither NULL, it is + * expected to be an array of fingerprints for subkeys to change. It + * may also be an array which just one item "*" to indicate that all + * keys shall be set to that expiration date. */ void -keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr) +keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr, + char **subkeyfprs) { gpg_error_t err; - kbnode_t keyblock; + kbnode_t keyblock, node; KEYDB_HANDLE kdbhd; int modified = 0; PKT_public_key *pk; u32 expire; + int primary_only = 0; + int idx; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ @@ -2846,7 +2852,6 @@ keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr) goto leave; } - expire = parse_expire_string (expirestr); if (expire == (u32)-1 ) { @@ -2857,8 +2862,78 @@ keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr) if (expire) expire += make_timestamp (); + /* Check whether a subkey's expiration time shall be changed or the + * expiration time of all keys. */ + if (!subkeyfprs || !subkeyfprs[0]) + primary_only = 1; + else if ( !strcmp (subkeyfprs[0], "*") && !subkeyfprs[1]) + { + /* Change all subkeys keys which have not been revoked and are + * not yet expired. */ + merge_keys_and_selfsig (ctrl, keyblock); + for (node = keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + && (pk = node->pkt->pkt.public_key) + && !pk->flags.revoked + && !pk->has_expired) + node->flag |= NODFLG_SELKEY; + } + } + else + { + /* Change specified subkeys. */ + KEYDB_SEARCH_DESC desc; + byte fprbin[MAX_FINGERPRINT_LEN]; + size_t fprlen; + + err = 0; + merge_keys_and_selfsig (ctrl, keyblock); + for (idx=0; subkeyfprs[idx]; idx++) + { + int any = 0; + + /* Parse the fingerprint. */ + if (classify_user_id (subkeyfprs[idx], &desc, 1) + || !(desc.mode == KEYDB_SEARCH_MODE_FPR + || desc.mode == KEYDB_SEARCH_MODE_FPR20)) + { + log_error (_("\"%s\" is not a proper fingerprint\n"), + subkeyfprs[idx] ); + if (!err) + err = gpg_error (GPG_ERR_INV_NAME); + continue; + } + + /* Set the flag for the matching non revoked subkey. */ + for (node = keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + && (pk = node->pkt->pkt.public_key) + && !pk->flags.revoked ) + { + fingerprint_from_pk (pk, fprbin, &fprlen); + if (fprlen == 20 && !memcmp (fprbin, desc.u.fpr, 20)) + { + node->flag |= NODFLG_SELKEY; + any = 1; + } + } + } + if (!any) + { + log_error (_("subkey \"%s\" not found\n"), subkeyfprs[idx]); + if (!err) + err = gpg_error (GPG_ERR_NOT_FOUND); + } + } + + if (err) + goto leave; + } + /* Set the new expiration date. */ - err = menu_expire (ctrl, keyblock, 1, expire); + err = menu_expire (ctrl, keyblock, primary_only? 1 : 2, expire); if (gpg_err_code (err) == GPG_ERR_TRUE) modified = 1; else if (err) @@ -4283,30 +4358,34 @@ fail: /* With FORCE_MAINKEY cleared this function handles the interactive - * menu option "expire". With FORCE_MAINKEY set this functions only + * menu option "expire". With UNATTENDED set to 1 this function only * sets the expiration date of the primary key to NEWEXPIRATION and - * avoid all interactivity. Retirns 0 if nothing was done, + * avoid all interactivity; with a value of 2 only the flagged subkeys + * are set to NEWEXPIRATION. Returns 0 if nothing was done, * GPG_ERR_TRUE if the key was modified, or any other error code. */ static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, - int force_mainkey, u32 newexpiration) + int unattended, u32 newexpiration) { int signumber, rc; u32 expiredate; - int mainkey = 0; + int only_mainkey; /* Set if only the mainkey is to be updated. */ PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; kbnode_t node; u32 keyid[2]; - if (force_mainkey) + if (unattended) { - mainkey = 1; + only_mainkey = (unattended == 1); expiredate = newexpiration; } else { - int n1 = count_selected_keys (pub_keyblock); + int n1; + + only_mainkey = 0; + n1 = count_selected_keys (pub_keyblock); if (n1 > 1) { if (!cpr_get_answer_is_yes @@ -4320,7 +4399,7 @@ menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, else { tty_printf (_("Changing expiration time for the primary key.\n")); - mainkey = 1; + only_mainkey = 1; no_primary_warning (pub_keyblock); } @@ -4342,8 +4421,10 @@ menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { - if ((node->flag & NODFLG_SELKEY) && !force_mainkey) + if ((node->flag & NODFLG_SELKEY) && unattended != 1) { + /* The flag is set and we do not want to set the + * expiration date only for the main key. */ sub_pk = node->pkt->pkt.public_key; sub_pk->expiredate = expiredate; } @@ -4353,14 +4434,14 @@ menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, else if (node->pkt->pkttype == PKT_USER_ID) uid = node->pkt->pkt.user_id; else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE - && (mainkey || sub_pk)) + && (only_mainkey || sub_pk)) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] - && ((mainkey && uid + && ((only_mainkey && uid && uid->created && (sig->sig_class & ~3) == 0x10) - || (!mainkey && sig->sig_class == 0x18)) + || (!only_mainkey && sig->sig_class == 0x18)) && sig->flags.chosen_selfsig) { /* This is a self-signature which is to be replaced. */ @@ -4369,15 +4450,15 @@ menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, signumber++; - if ((mainkey && main_pk->version < 4) - || (!mainkey && sub_pk->version < 4)) + if ((only_mainkey && main_pk->version < 4) + || (!only_mainkey && sub_pk->version < 4)) { log_info (_("You can't change the expiration date of a v3 key\n")); return gpg_error (GPG_ERR_LEGACY_KEY); } - if (mainkey) + if (only_mainkey) rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_key_expire, diff --git a/g10/keyedit.h b/g10/keyedit.h index d4c963222..23a126bc3 100644 --- a/g10/keyedit.h +++ b/g10/keyedit.h @@ -46,7 +46,8 @@ void keyedit_quick_revuid (ctrl_t ctrl, const char *username, 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); + const char *fpr, const char *expirestr, + char **subkeyfprs); void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid); void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock);