From aa99ebde778b7b563f35025f1b48954757f840be Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 21 Jan 2015 11:31:20 +0100 Subject: [PATCH] gpg: Re-enable the "Passphrase" parameter for batch key generation. * agent/command.c (cmd_genkey): Add option --inq-passwd. * agent/genkey.c (agent_genkey): Add new arg override_passphrase. * g10/call-agent.c (inq_genkey_parms): Handle NEWPASSWD keyword. (agent_genkey): Add arg optional arg "passphrase". * g10/keygen.c (common_gen, gen_elg, gen_dsa, gen_ecc) (gen_rsa, do_create): Add arg "passphrase" and pass it through. (do_generate_keypair): Make use of pPASSPHRASE. (release_parameter_list): Wipe out a passphrase parameter. Signed-off-by: Werner Koch --- NEWS | 3 +++ agent/agent.h | 3 ++- agent/command.c | 46 +++++++++++++++++++++++++++++++++++++------ agent/genkey.c | 43 +++++++++++++++++++++++++--------------- doc/gpg-agent.texi | 7 +++++++ doc/gpg.texi | 10 +++------- g10/call-agent.c | 23 ++++++++++++++++------ g10/call-agent.h | 1 + g10/keygen.c | 49 ++++++++++++++++++++++++++-------------------- 9 files changed, 128 insertions(+), 57 deletions(-) diff --git a/NEWS b/NEWS index f4a691876..6f171aa8b 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,9 @@ Noteworthy changes in version 2.1.2 (unreleased) * agent: When setting --default-cache-ttl the value for --max-cache-ttl is adjusted to be not lower than the former. + * gpg: The parameter 'Passphrase' for batch key generation works + again. + Noteworthy changes in version 2.1.1 (2014-12-16) ------------------------------------------------ diff --git a/agent/agent.h b/agent/agent.h index c7c65afa7..4be592526 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -396,7 +396,8 @@ gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, char **r_passphrase); int agent_genkey (ctrl_t ctrl, const char *cache_nonce, const char *keyparam, size_t keyparmlen, - int no_protection, int preset, membuf_t *outbuf); + int no_protection, const char *override_passphrase, + int preset, membuf_t *outbuf); gpg_error_t agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey, char **passphrase_addr); diff --git a/agent/command.c b/agent/command.c index da7e50857..d5644cbac 100644 --- a/agent/command.c +++ b/agent/command.c @@ -914,22 +914,23 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) static const char hlp_genkey[] = - "GENKEY [--no-protection] [--preset] []\n" + "GENKEY [--no-protection] [--preset] [--inq-passwd] []\n" "\n" "Generate a new key, store the secret part and return the public\n" "part. Here is an example transaction:\n" "\n" " C: GENKEY\n" " S: INQUIRE KEYPARAM\n" - " C: D (genkey (rsa (nbits 1024)))\n" + " C: D (genkey (rsa (nbits 2048)))\n" " C: END\n" " S: D (public-key\n" " S: D (rsa (n 326487324683264) (e 10001)))\n" " S: OK key created\n" "\n" "When the --preset option is used the passphrase for the generated\n" - "key will be added to the cache.\n" - "\n"; + "key will be added to the cache. When --inq-passwd is used an inquire\n" + "with the keyword NEWPASSWD is used to request the passphrase for the\n" + "new key.\n"; static gpg_error_t cmd_genkey (assuan_context_t ctx, char *line) { @@ -938,16 +939,20 @@ cmd_genkey (assuan_context_t ctx, char *line) int no_protection; unsigned char *value; size_t valuelen; + unsigned char *newpasswd = NULL; membuf_t outbuf; char *cache_nonce = NULL; int opt_preset; + int opt_inq_passwd; + size_t n; char *p; if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); - opt_preset = has_option (line, "--preset"); no_protection = has_option (line, "--no-protection"); + opt_preset = has_option (line, "--preset"); + opt_inq_passwd = has_option (line, "--inq-passwd"); line = skip_options (line); p = line; @@ -966,8 +971,37 @@ cmd_genkey (assuan_context_t ctx, char *line) init_membuf (&outbuf, 512); + /* If requested, ask for the password to be used for the key. If + this is not used the regular Pinentry mechanism is used. */ + if (opt_inq_passwd && !no_protection) + { + /* (N is used as a dummy) */ + assuan_begin_confidential (ctx); + rc = assuan_inquire (ctx, "NEWPASSWD", &newpasswd, &n, 256); + assuan_end_confidential (ctx); + if (rc) + goto leave; + if (!*newpasswd) + { + /* Empty password given - switch to no-protection mode. */ + xfree (newpasswd); + newpasswd = NULL; + no_protection = 1; + } + + } + rc = agent_genkey (ctrl, cache_nonce, (char*)value, valuelen, no_protection, - opt_preset, &outbuf); + newpasswd, opt_preset, &outbuf); + + leave: + if (newpasswd) + { + /* Assuan_inquire does not allow us to read into secure memory + thus we need to wipe it ourself. */ + wipememory (newpasswd, strlen (newpasswd)); + xfree (newpasswd); + } xfree (value); if (rc) clear_outbuf (&outbuf); diff --git a/agent/genkey.c b/agent/genkey.c index 91917f77b..d7b6007bf 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -410,14 +410,16 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, /* Generate a new keypair according to the parameters given in KEYPARAM. If CACHE_NONCE is given first try to lookup a passphrase using the cache nonce. If NO_PROTECTION is true the key will not - be protected by a passphrase. */ + be protected by a passphrase. If OVERRIDE_PASSPHRASE is true that + passphrase will be used for the new key. */ int agent_genkey (ctrl_t ctrl, const char *cache_nonce, const char *keyparam, size_t keyparamlen, int no_protection, - int preset, membuf_t *outbuf) + const char *override_passphrase, int preset, membuf_t *outbuf) { gcry_sexp_t s_keyparam, s_key, s_private, s_public; - char *passphrase; + char *passphrase_buffer = NULL; + const char *passphrase; int rc; size_t len; char *buf; @@ -430,27 +432,35 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, } /* Get the passphrase now, cause key generation may take a while. */ - if (no_protection || !cache_nonce) + if (override_passphrase) + passphrase = override_passphrase; + else if (no_protection || !cache_nonce) passphrase = NULL; else - passphrase = agent_get_cache (cache_nonce, CACHE_MODE_NONCE); + { + passphrase_buffer = agent_get_cache (cache_nonce, CACHE_MODE_NONCE); + passphrase = passphrase_buffer; + } if (passphrase || no_protection) - rc = 0; + ; else - rc = agent_ask_new_passphrase (ctrl, - _("Please enter the passphrase to%0A" - "protect your new key"), - &passphrase); - if (rc) - return rc; + { + rc = agent_ask_new_passphrase (ctrl, + _("Please enter the passphrase to%0A" + "protect your new key"), + &passphrase_buffer); + if (rc) + return rc; + passphrase = passphrase_buffer; + } rc = gcry_pk_genkey (&s_key, s_keyparam ); gcry_sexp_release (s_keyparam); if (rc) { log_error ("key generation failed: %s\n", gpg_strerror (rc)); - xfree (passphrase); + xfree (passphrase_buffer); return rc; } @@ -460,7 +470,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, { log_error ("key generation failed: invalid return value\n"); gcry_sexp_release (s_key); - xfree (passphrase); + xfree (passphrase_buffer); return gpg_error (GPG_ERR_INV_DATA); } s_public = gcry_sexp_find_token (s_key, "public-key", 0); @@ -469,7 +479,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, log_error ("key generation failed: invalid return value\n"); gcry_sexp_release (s_private); gcry_sexp_release (s_key); - xfree (passphrase); + xfree (passphrase_buffer); return gpg_error (GPG_ERR_INV_DATA); } gcry_sexp_release (s_key); s_key = NULL; @@ -503,7 +513,8 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, } } } - xfree (passphrase); + xfree (passphrase_buffer); + passphrase_buffer = NULL; passphrase = NULL; gcry_sexp_release (s_private); if (rc) diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 36bd0c29f..7ac441f29 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -1134,6 +1134,13 @@ The @option{--no-protection} option may be used to prevent prompting for a passphrase to protect the secret key while leaving the secret key unprotected. The @option{--preset} option may be used to add the passphrase to the cache using the default cache parameters. + +The @option{--inq-passwd} option may be used to create the key with a +supplied passphrase. When used the agent does an inquiry with the +keyword @code{NEWPASSWD} to retrieve that passphrase. This option +takes precedence over @option{--no-protection}; however if the client +sends a empty (zero-length) passphrase, this is identical to +@option{--no-protection}. @end ifset @node Agent IMPORT diff --git a/doc/gpg.texi b/doc/gpg.texi index 71ffaf8a8..6921fd998 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -3341,17 +3341,13 @@ ignored and instead the usual passphrase dialog is used. This does not make sense for batch key generation; however the unattended key generation feature is also used by GUIs and this feature relinquishes the GUI from implementing its own passphrase entry code. These are -global control statements and affect all future key genrations. +global control statements and affect all future key generations. @end ifclear @ifset gpgtwoone This option is a no-op for GnuPG 2.1 and later. @end ifset - @item %no-protection -Since GnuPG version 2.1 it is not anymore possible to specify a -passphrase for unattended key generation. The passphrase command is -simply ignored and @samp{%ask-passpharse} is thus implicitly enabled. Using this option allows the creation of keys without any passphrase protection. This option is mainly intended for regression tests. @@ -3409,8 +3405,8 @@ by running the command @samp{gpg2 --gpgconf-list}". Key usage lists for a subkey; similar to @samp{Key-Usage}. @item Passphrase: @var{string} -If you want to specify a passphrase for the secret key, -enter it here. Default is not to use any passphrase. +If you want to specify a passphrase for the secret key, enter it here. +Default is to use the Pinentry dialog to ask for a passphrase. @item Name-Real: @var{name} @itemx Name-Comment: @var{comment} diff --git a/g10/call-agent.c b/g10/call-agent.c index a98a177ad..dc9d1575a 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1,7 +1,6 @@ /* call-agent.c - Divert GPG operations to the agent. - * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008, 2009, - * 2010, 2011, 2013 Free Software Foundation, Inc. - * Copyright (C) 2013, 2014 Werner Koch + * Copyright (C) 2001-2003, 2006-2011, 2013 Free Software Foundation, Inc. + * Copyright (C) 2013-2015 Werner Koch * * This file is part of GnuPG. * @@ -90,6 +89,7 @@ struct genkey_parm_s { struct default_inq_parm_s *dflt; const char *keyparms; + const char *passphrase; }; struct import_key_parm_s @@ -1737,6 +1737,11 @@ inq_genkey_parms (void *opaque, const char *line) err = assuan_send_data (parm->dflt->ctx, parm->keyparms, strlen (parm->keyparms)); } + else if (has_leading_keyword (line, "NEWPASSWD") && parm->passphrase) + { + err = assuan_send_data (parm->dflt->ctx, + parm->passphrase, strlen (parm->passphrase)); + } else err = default_inq_cb (parm->dflt, line); @@ -1747,10 +1752,13 @@ inq_genkey_parms (void *opaque, const char *line) /* Call the agent to generate a new key. KEYPARMS is the usual S-expression giving the parameters of the key. gpg-agent passes it gcry_pk_genkey. If NO_PROTECTION is true the agent is advised not - to protect the generated key. */ + to protect the generated key. If NO_PROTECTION is not set and + PASSPHRASE is not NULL the agent is requested to protect the key + with that passphrase instead of asking for one. */ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr, - const char *keyparms, int no_protection, gcry_sexp_t *r_pubkey) + const char *keyparms, int no_protection, + const char *passphrase, gcry_sexp_t *r_pubkey) { gpg_error_t err; struct genkey_parm_s gk_parm; @@ -1778,8 +1786,11 @@ agent_genkey (ctrl_t ctrl, char **cache_nonce_addr, init_membuf (&data, 1024); gk_parm.dflt = &dfltparm; gk_parm.keyparms = keyparms; + gk_parm.passphrase = passphrase; snprintf (line, sizeof line, "GENKEY%s%s%s", - no_protection? " --no-protection":"", + no_protection? " --no-protection" : + passphrase ? " --inq-passwd" : + /* */ "", cache_nonce_addr && *cache_nonce_addr? " ":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:""); cn_parm.cache_nonce_addr = cache_nonce_addr; diff --git a/g10/call-agent.h b/g10/call-agent.h index bcb5ae9f5..9c104e88e 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -154,6 +154,7 @@ gpg_error_t agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, /* Generate a new key. */ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr, const char *keyparms, int no_protection, + const char *passphrase, gcry_sexp_t *r_pubkey); /* Read a public key. */ diff --git a/g10/keygen.c b/g10/keygen.c index fa466a8b8..a3dbed8db 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,7 +1,6 @@ /* keygen.c - generate a key pair - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - * 2007, 2009, 2010, 2011 Free Software Foundation, Inc. - * Copyright (C) 2014 Werner Koch + * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. + * Copyright (C) 2014, 2015 Werner Koch * * This file is part of GnuPG. * @@ -1287,7 +1286,7 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, const char *hexkeygrip, static int common_gen (const char *keyparms, int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, - int keygen_flags, char **cache_nonce_addr) + int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; PACKET *pkt; @@ -1295,7 +1294,9 @@ common_gen (const char *keyparms, int algo, const char *algoelem, gcry_sexp_t s_key; err = agent_genkey (NULL, cache_nonce_addr, keyparms, - !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), &s_key); + !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), + passphrase, + &s_key); if (err) { log_error ("agent_genkey failed: %s\n", gpg_strerror (err) ); @@ -1353,7 +1354,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, static int gen_elg (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, - int keygen_flags, char **cache_nonce_addr) + int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; char *keyparms; @@ -1394,7 +1395,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, { err = common_gen (keyparms, algo, "pgy", pub_root, timestamp, expireval, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } @@ -1408,7 +1409,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, static gpg_error_t gen_dsa (unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, - int keygen_flags, char **cache_nonce_addr) + int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; unsigned int qbits; @@ -1481,7 +1482,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, { err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } @@ -1496,7 +1497,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, static gpg_error_t gen_ecc (int algo, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, - int keygen_flags, char **cache_nonce_addr) + int keygen_flags, const char *passphrase, char **cache_nonce_addr) { gpg_error_t err; char *keyparms; @@ -1531,7 +1532,7 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, { err = common_gen (keyparms, algo, "", pub_root, timestamp, expireval, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } @@ -1545,7 +1546,7 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, static int gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, - int keygen_flags, char **cache_nonce_addr) + int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; char *keyparms; @@ -1586,7 +1587,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, { err = common_gen (keyparms, algo, "ne", pub_root, timestamp, expireval, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } @@ -2724,7 +2725,7 @@ do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled) static int do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root, u32 timestamp, u32 expiredate, int is_subkey, - int keygen_flags, char **cache_nonce_addr) + int keygen_flags, const char *passphrase, char **cache_nonce_addr) { gpg_error_t err; @@ -2739,18 +2740,18 @@ do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root, if (algo == PUBKEY_ALGO_ELGAMAL_E) err = gen_elg (algo, nbits, pub_root, timestamp, expiredate, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); else if (algo == PUBKEY_ALGO_DSA) err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, - keygen_flags, cache_nonce_addr); + keygen_flags, passphrase, cache_nonce_addr); else BUG(); @@ -2792,6 +2793,8 @@ release_parameter_list (struct para_data_s *r) for (; r ; r = r2) { r2 = r->next; + if (r->key == pPASSPHRASE && *r->u.value) + wipememory (r->u.value, strlen (r->u.value)); xfree (r); } } @@ -3966,7 +3969,9 @@ do_generate_keypair (struct para_data_s *para, pub_root, timestamp, get_parameter_u32( para, pKEYEXPIRE ), 0, - outctrl->keygen_flags, &cache_nonce); + outctrl->keygen_flags, + get_parameter_value (para, pPASSPHRASE), + &cache_nonce); else err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, ×tamp, @@ -4018,7 +4023,9 @@ do_generate_keypair (struct para_data_s *para, pub_root, timestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, - outctrl->keygen_flags, &cache_nonce); + outctrl->keygen_flags, + get_parameter_value (para, pPASSPHRASE), + &cache_nonce); /* Get the pointer to the generated public subkey packet. */ if (!err) { @@ -4241,7 +4248,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) keyblock, cur_time, expire, 1); else err = do_create (algo, nbits, curve, - keyblock, cur_time, expire, 1, 0, NULL); + keyblock, cur_time, expire, 1, 0, NULL, NULL); if (err) goto leave;