summaryrefslogtreecommitdiffstats
path: root/g10/passphrase.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
commiteee068778cb28ecf3c14e1bf843a95547d72c42d (patch)
tree0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /g10/passphrase.c
parentInitial commit. (diff)
downloadgnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.tar.xz
gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.zip
Adding upstream version 2.2.40.upstream/2.2.40upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'g10/passphrase.c')
-rw-r--r--g10/passphrase.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/g10/passphrase.c b/g10/passphrase.c
new file mode 100644
index 0000000..f94b93c
--- /dev/null
+++ b/g10/passphrase.c
@@ -0,0 +1,581 @@
+/* passphrase.c - Get a passphrase
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005, 2006, 2007, 2009, 2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+#include "gpg.h"
+#include "../common/util.h"
+#include "options.h"
+#include "../common/ttyio.h"
+#include "keydb.h"
+#include "main.h"
+#include "../common/i18n.h"
+#include "../common/status.h"
+#include "call-agent.h"
+#include "../common/shareddefs.h"
+
+static char *fd_passwd = NULL;
+static char *next_pw = NULL;
+static char *last_pw = NULL;
+
+
+
+/* Pack an s2k iteration count into the form specified in 2440. If
+ we're in between valid values, round up. With value 0 return the
+ old default. */
+unsigned char
+encode_s2k_iterations (int iterations)
+{
+ gpg_error_t err;
+ unsigned char c=0;
+ unsigned char result;
+ unsigned int count;
+
+ if (!iterations)
+ {
+ unsigned long mycnt;
+
+ /* Ask the gpg-agent for a useful iteration count. */
+ err = agent_get_s2k_count (&mycnt);
+ if (err || mycnt < 65536)
+ {
+ /* Don't print an error if an older agent is used. */
+ if (err && gpg_err_code (err) != GPG_ERR_ASS_PARAMETER)
+ log_error (_("problem with the agent: %s\n"), gpg_strerror (err));
+ /* Default to 65536 which we used up to 2.0.13. */
+ return 96;
+ }
+ else if (mycnt >= 65011712)
+ return 255; /* Largest possible value. */
+ else
+ return encode_s2k_iterations ((int)mycnt);
+ }
+
+ if (iterations <= 1024)
+ return 0; /* Command line arg compatibility. */
+
+ if (iterations >= 65011712)
+ return 255;
+
+ /* Need count to be in the range 16-31 */
+ for (count=iterations>>6; count>=32; count>>=1)
+ c++;
+
+ result = (c<<4)|(count-16);
+
+ if (S2K_DECODE_COUNT(result) < iterations)
+ result++;
+
+ return result;
+}
+
+
+int
+have_static_passphrase()
+{
+ return (!!fd_passwd
+ && (opt.batch || opt.pinentry_mode == PINENTRY_MODE_LOOPBACK));
+}
+
+/* Return a static passphrase. The returned value is only valid as
+ long as no other passphrase related function is called. NULL may
+ be returned if no passphrase has been set; better use
+ have_static_passphrase first. */
+const char *
+get_static_passphrase (void)
+{
+ return fd_passwd;
+}
+
+
+/****************
+ * Set the passphrase to be used for the next query and only for the next
+ * one.
+ */
+void
+set_next_passphrase( const char *s )
+{
+ xfree(next_pw);
+ next_pw = NULL;
+ if ( s )
+ {
+ next_pw = xmalloc_secure( strlen(s)+1 );
+ strcpy (next_pw, s );
+ }
+}
+
+/****************
+ * Get the last passphrase used in passphrase_to_dek.
+ * Note: This removes the passphrase from this modules and
+ * the caller must free the result. May return NULL:
+ */
+char *
+get_last_passphrase()
+{
+ char *p = last_pw;
+ last_pw = NULL;
+ return p;
+}
+
+/* Here's an interesting question: since this passphrase was passed in
+ on the command line, is there really any point in using secure
+ memory for it? I'm going with 'yes', since it doesn't hurt, and
+ might help in some small way (swapping). */
+
+void
+set_passphrase_from_string(const char *pass)
+{
+ xfree (fd_passwd);
+ fd_passwd = xmalloc_secure(strlen(pass)+1);
+ strcpy (fd_passwd, pass);
+}
+
+
+void
+read_passphrase_from_fd( int fd )
+{
+ int i, len;
+ char *pw;
+
+ if (! gnupg_fd_valid (fd))
+ log_fatal ("passphrase-fd is invalid: %s\n", strerror (errno));
+
+ if ( !opt.batch && opt.pinentry_mode != PINENTRY_MODE_LOOPBACK)
+ { /* Not used but we have to do a dummy read, so that it won't end
+ up at the begin of the message if the quite usual trick to
+ prepend the passphtrase to the message is used. */
+ char buf[1];
+
+ while (!(read (fd, buf, 1) != 1 || *buf == '\n' ))
+ ;
+ *buf = 0;
+ return;
+ }
+
+ for (pw = NULL, i = len = 100; ; i++ )
+ {
+ if (i >= len-1 )
+ {
+ char *pw2 = pw;
+ len += 100;
+ pw = xmalloc_secure( len );
+ if( pw2 )
+ {
+ memcpy(pw, pw2, i );
+ xfree (pw2);
+ }
+ else
+ i=0;
+ }
+ if (read( fd, pw+i, 1) != 1 || pw[i] == '\n' )
+ break;
+ }
+ pw[i] = 0;
+ if (!opt.batch && opt.pinentry_mode != PINENTRY_MODE_LOOPBACK)
+ tty_printf("\b\b\b \n" );
+
+ xfree ( fd_passwd );
+ fd_passwd = pw;
+}
+
+
+/*
+ * Ask the GPG Agent for the passphrase.
+ * If NOCACHE is set the symmetric passpharse caching will not be used.
+ *
+ * If REPEAT is positive, a new passphrase is requested and the agent
+ * shall require REPEAT times repetitions of the entered passphrase.
+ * This is used for symmetric encryption.
+ *
+ * Note that TRYAGAIN_TEXT must not be translated. If CANCELED is not
+ * NULL, the function does set it to 1 if the user canceled the
+ * operation. If CACHEID is not NULL, it will be used as the cacheID
+ * for the gpg-agent; if is NULL and a key fingerprint can be
+ * computed, this will be used as the cacheid.
+ *
+ * For FLAGS see passphrase_to_dek;
+ */
+static char *
+passphrase_get (int newsymkey, int nocache, const char *cacheid, int repeat,
+ const char *tryagain_text, unsigned int flags, int *canceled)
+{
+ int rc;
+ char *pw = NULL;
+ char *orig_codeset;
+ const char *my_cacheid;
+ const char *desc;
+
+ if (canceled)
+ *canceled = 0;
+
+ orig_codeset = i18n_switchto_utf8 ();
+
+ if (!nocache && cacheid)
+ my_cacheid = cacheid;
+ else
+ my_cacheid = NULL;
+
+ if (tryagain_text)
+ tryagain_text = _(tryagain_text);
+
+ if ((flags & GETPASSWORD_FLAG_SYMDECRYPT))
+ desc = _("Please enter the passphrase for decryption.");
+ else
+ desc = _("Enter passphrase\n");
+
+ /* Here we have:
+ * REPEAT is set in create mode and if opt.passphrase_repeat is set.
+ * (Thus it is not a clean indication that we want a new passphrase).
+ * NOCACHE is set in create mode or if --no-symkey-cache is used.
+ * CACHEID is only set if caching shall be used.
+ * NEWSYMKEY has been added latter to make it clear that a new key
+ * is requested. The whole chain of API is a bit too complex since
+ * we we stripped things out over time; however, there is no time
+ * for a full state analysis and thus this new parameter.
+ */
+ rc = agent_get_passphrase (my_cacheid, tryagain_text, NULL,
+ desc,
+ newsymkey, repeat, nocache, &pw);
+
+ i18n_switchback (orig_codeset);
+
+
+ if (!rc)
+ ;
+ else if (gpg_err_code (rc) == GPG_ERR_CANCELED
+ || gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
+ {
+ log_info (_("cancelled by user\n") );
+ if (canceled)
+ *canceled = 1;
+ }
+ else
+ {
+ log_error (_("problem with the agent: %s\n"), gpg_strerror (rc));
+ /* Due to limitations in the API of the upper layers they
+ consider an error as no passphrase entered. This works in
+ most cases but not during key creation where this should
+ definitely not happen and let it continue without requiring a
+ passphrase. Given that now all the upper layers handle a
+ cancel correctly, we simply set the cancel flag now for all
+ errors from the agent. */
+ if (canceled)
+ *canceled = 1;
+
+ write_status_errcode ("get_passphrase", rc);
+ }
+
+ if (rc)
+ {
+ xfree (pw);
+ pw = NULL;
+ }
+ return pw;
+}
+
+
+/*
+ * Clear the cached passphrase with CACHEID.
+ */
+void
+passphrase_clear_cache (const char *cacheid)
+{
+ int rc;
+
+ rc = agent_clear_passphrase (cacheid);
+ if (rc)
+ log_error (_("problem with the agent: %s\n"), gpg_strerror (rc));
+}
+
+
+/* Return a new DEK object using the string-to-key specifier S2K.
+ * Returns NULL if the user canceled the passphrase entry and if
+ * CANCELED is not NULL, sets it to true.
+ *
+ * If CREATE is true a new passphrase sll be created. If NOCACHE is
+ * true the symmetric key caching will not be used.
+ *
+ * FLAG bits are:
+ * GETPASSWORD_FLAG_SYMDECRYPT := for symmetric decryption
+ */
+DEK *
+passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
+ int create, int nocache,
+ const char *tryagain_text, unsigned int flags,
+ int *canceled)
+{
+ char *pw = NULL;
+ DEK *dek;
+ STRING2KEY help_s2k;
+ int dummy_canceled;
+ char s2k_cacheidbuf[1+16+1];
+ char *s2k_cacheid = NULL;
+
+ if (!canceled)
+ canceled = &dummy_canceled;
+ *canceled = 0;
+
+ if (opt.no_symkey_cache)
+ nocache = 1; /* Force no symmetric key caching. */
+
+ if ( !s2k )
+ {
+ log_assert (create && !nocache);
+ /* This is used for the old rfc1991 mode
+ * Note: This must match the code in encode.c with opt.rfc1991 set */
+ memset (&help_s2k, 0, sizeof (help_s2k));
+ s2k = &help_s2k;
+ s2k->hash_algo = S2K_DIGEST_ALGO;
+ }
+
+ /* Create a new salt or what else to be filled into the s2k for a
+ new key. */
+ if (create && (s2k->mode == 1 || s2k->mode == 3))
+ {
+ gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
+ if ( s2k->mode == 3 )
+ {
+ /* We delay the encoding until it is really needed. This is
+ if we are going to dynamically calibrate it, we need to
+ call out to gpg-agent and that should not be done during
+ option processing in main(). */
+ if (!opt.s2k_count)
+ opt.s2k_count = encode_s2k_iterations (0);
+ s2k->count = opt.s2k_count;
+ }
+ }
+
+ /* If we do not have a passphrase available in NEXT_PW and status
+ information are request, we print them now. */
+ if ( !next_pw && is_status_enabled() )
+ {
+ char buf[50];
+
+ snprintf (buf, sizeof buf, "%d %d %d",
+ cipher_algo, s2k->mode, s2k->hash_algo );
+ write_status_text ( STATUS_NEED_PASSPHRASE_SYM, buf );
+ }
+
+ if ( next_pw )
+ {
+ /* Simply return the passphrase we already have in NEXT_PW. */
+ pw = next_pw;
+ next_pw = NULL;
+ }
+ else if ( have_static_passphrase () )
+ {
+ /* Return the passphrase we have stored in FD_PASSWD. */
+ pw = xmalloc_secure ( strlen(fd_passwd)+1 );
+ strcpy ( pw, fd_passwd );
+ }
+ else
+ {
+ if (!nocache && (s2k->mode == 1 || s2k->mode == 3))
+ {
+ memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
+ *s2k_cacheidbuf = 'S';
+ bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1);
+ s2k_cacheid = s2k_cacheidbuf;
+ }
+
+ if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
+ {
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "%u", 100);
+ write_status_text (STATUS_INQUIRE_MAXLEN, buf);
+ }
+
+ /* Divert to the gpg-agent. */
+ pw = passphrase_get (create, create && nocache, s2k_cacheid,
+ create? opt.passphrase_repeat : 0,
+ tryagain_text, flags, canceled);
+ if (*canceled)
+ {
+ xfree (pw);
+ write_status( STATUS_CANCELED_BY_USER );
+ return NULL;
+ }
+ }
+
+ if ( !pw || !*pw )
+ write_status( STATUS_MISSING_PASSPHRASE );
+
+ /* Hash the passphrase and store it in a newly allocated DEK object.
+ Keep a copy of the passphrase in LAST_PW for use by
+ get_last_passphrase(). */
+ dek = xmalloc_secure_clear ( sizeof *dek );
+ dek->algo = cipher_algo;
+ if ( (!pw || !*pw) && create)
+ dek->keylen = 0;
+ else
+ {
+ gpg_error_t err;
+
+ dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
+ if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key)))
+ BUG ();
+ err = gcry_kdf_derive (pw, strlen (pw),
+ s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K :
+ s2k->mode == 1? GCRY_KDF_SALTED_S2K :
+ /* */ GCRY_KDF_SIMPLE_S2K,
+ s2k->hash_algo, s2k->salt, 8,
+ S2K_DECODE_COUNT(s2k->count),
+ dek->keylen, dek->key);
+ if (err)
+ {
+ log_error ("gcry_kdf_derive failed: %s", gpg_strerror (err));
+ xfree (pw);
+ xfree (dek);
+ write_status( STATUS_MISSING_PASSPHRASE );
+ return NULL;
+ }
+ }
+ if (s2k_cacheid)
+ memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid);
+ xfree(last_pw);
+ last_pw = pw;
+ return dek;
+}
+
+
+/* Emit the USERID_HINT and the NEED_PASSPHRASE status messages.
+ MAINKEYID may be NULL. */
+void
+emit_status_need_passphrase (ctrl_t ctrl,
+ u32 *keyid, u32 *mainkeyid, int pubkey_algo)
+{
+ char buf[50];
+ char *us;
+
+ us = get_long_user_id_string (ctrl, keyid);
+ write_status_text (STATUS_USERID_HINT, us);
+ xfree (us);
+
+ snprintf (buf, sizeof buf, "%08lX%08lX %08lX%08lX %d 0",
+ (ulong)keyid[0],
+ (ulong)keyid[1],
+ (ulong)(mainkeyid? mainkeyid[0]:keyid[0]),
+ (ulong)(mainkeyid? mainkeyid[1]:keyid[1]),
+ pubkey_algo);
+
+ write_status_text (STATUS_NEED_PASSPHRASE, buf);
+}
+
+
+/* Return an allocated utf-8 string describing the key PK. If ESCAPED
+ is true spaces and control characters are percent or plus escaped.
+ MODE describes the use of the key description; use one of the
+ FORMAT_KEYDESC_ macros. */
+char *
+gpg_format_keydesc (ctrl_t ctrl, PKT_public_key *pk, int mode, int escaped)
+{
+ char *uid;
+ size_t uidlen;
+ const char *algo_name;
+ const char *timestr;
+ char *orig_codeset;
+ char *maink;
+ char *desc;
+ const char *prompt;
+ const char *trailer = "";
+ int is_subkey;
+
+ is_subkey = (pk->main_keyid[0] && pk->main_keyid[1]
+ && pk->keyid[0] != pk->main_keyid[0]
+ && pk->keyid[1] != pk->main_keyid[1]);
+ algo_name = openpgp_pk_algo_name (pk->pubkey_algo);
+ timestr = strtimestamp (pk->timestamp);
+ uid = get_user_id (ctrl, is_subkey? pk->main_keyid:pk->keyid, &uidlen, NULL);
+
+ orig_codeset = i18n_switchto_utf8 ();
+
+ if (is_subkey)
+ maink = xtryasprintf (_(" (main key ID %s)"), keystr (pk->main_keyid));
+ else
+ maink = NULL;
+
+ switch (mode)
+ {
+ case FORMAT_KEYDESC_NORMAL:
+ prompt = _("Please enter the passphrase to unlock the"
+ " OpenPGP secret key:");
+ break;
+ case FORMAT_KEYDESC_IMPORT:
+ prompt = _("Please enter the passphrase to import the"
+ " OpenPGP secret key:");
+ break;
+ case FORMAT_KEYDESC_EXPORT:
+ if (is_subkey)
+ prompt = _("Please enter the passphrase to export the"
+ " OpenPGP secret subkey:");
+ else
+ prompt = _("Please enter the passphrase to export the"
+ " OpenPGP secret key:");
+ break;
+ case FORMAT_KEYDESC_DELKEY:
+ if (is_subkey)
+ prompt = _("Do you really want to permanently delete the"
+ " OpenPGP secret subkey key:");
+ else
+ prompt = _("Do you really want to permanently delete the"
+ " OpenPGP secret key:");
+ trailer = "?";
+ break;
+ default:
+ prompt = "?";
+ break;
+ }
+
+ desc = xtryasprintf (_("%s\n"
+ "\"%.*s\"\n"
+ "%u-bit %s key, ID %s,\n"
+ "created %s%s.\n%s"),
+ prompt,
+ (int)uidlen, uid,
+ nbits_from_pk (pk), algo_name,
+ keystr (pk->keyid), timestr,
+ maink?maink:"", trailer);
+ xfree (maink);
+ xfree (uid);
+
+ i18n_switchback (orig_codeset);
+
+ if (escaped)
+ {
+ char *tmp = percent_plus_escape (desc);
+ xfree (desc);
+ desc = tmp;
+ }
+
+ return desc;
+}