summaryrefslogtreecommitdiff
path: root/pinentry/pinentry.c
diff options
context:
space:
mode:
authorTom Barrett <tom@tombarrett.xyz>2022-04-18 19:01:14 +0200
committerTom Barrett <tom@tombarrett.xyz>2022-04-18 19:01:14 +0200
commitdc4e993b3d5223d758eeb72fac1424da492b58c2 (patch)
tree204f53b8dc83b42b27f976bfef4d7a23f085f628 /pinentry/pinentry.c
parenta4f7d150c7395faf72fb167ac333170668124866 (diff)
parentb514b660285b66ee1a2d72c4838f647332e4a0cc (diff)
Merge branch 'master' of core-a:/mnt/repos/public/pinentry-dmenu
Diffstat (limited to 'pinentry/pinentry.c')
-rw-r--r--pinentry/pinentry.c1011
1 files changed, 964 insertions, 47 deletions
diff --git a/pinentry/pinentry.c b/pinentry/pinentry.c
index f305a7d..b03b90c 100644
--- a/pinentry/pinentry.c
+++ b/pinentry/pinentry.c
@@ -1,28 +1,58 @@
/* pinentry.c - The PIN entry support library
- Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015 g10 Code GmbH
-
- This file is part of PINENTRY.
-
- PINENTRY 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 2 of the License, or
- (at your option) any later version.
-
- PINENTRY 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 <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015, 2016, 2021 g10 Code GmbH
+ *
+ * This file is part of PINENTRY.
+ *
+ * PINENTRY 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * PINENTRY 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/>.
+ * SPDX-License-Identifier: GPL-2.0+
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef HAVE_W32CE_SYSTEM
+# include <errno.h>
+#endif
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
+#ifndef HAVE_W32_SYSTEM
+# include <sys/utsname.h>
+#endif
+#ifndef HAVE_W32CE_SYSTEM
+# include <locale.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#include <limits.h>
+#ifdef HAVE_W32CE_SYSTEM
+# include <windows.h>
+#endif
+
+#undef WITH_UTF8_CONVERSION
+#if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
+# include <iconv.h>
+# define WITH_UTF8_CONVERSION 1
+#endif
#include <assuan.h>
+#include <strings.h>
#include "memory.h"
#include "secmem-util.h"
@@ -30,11 +60,39 @@
#include "pinentry.h"
#include "password-cache.h"
+#ifdef INSIDE_EMACS
+# include "pinentry-emacs.h"
+#endif
+#ifdef FALLBACK_CURSES
+# include "pinentry-curses.h"
+#endif
+
+#ifdef HAVE_W32CE_SYSTEM
+#define getpid() GetCurrentProcessId ()
+#endif
+
/* Keep the name of our program here. */
static char this_pgmname[50];
struct pinentry pinentry;
+
+static const char *flavor_flag;
+
+/* Because gtk_init removes the --display arg from the command lines
+ * and our command line parser is called after gtk_init (so that it
+ * does not see gtk specific options) we don't have a way to get hold
+ * of the --display option. Our solution is to remember --display in
+ * the call to pinentry_have_display and set it then in our
+ * parser. */
+static char *remember_display;
+
+/* Flag to remember whether a warning has been printed. */
+#ifdef WITH_UTF8_CONVERSION
+static int lc_ctype_unknown_warning;
+#endif
+
+
static void
pinentry_reset (int use_defaults)
{
@@ -42,7 +100,8 @@ pinentry_reset (int use_defaults)
Don't reset them. */
int grab = pinentry.grab;
char *ttyname = pinentry.ttyname;
- char *ttytype = pinentry.ttytype;
+ char *ttytype = pinentry.ttytype_l;
+ char *ttyalert = pinentry.ttyalert;
char *lc_ctype = pinentry.lc_ctype;
char *lc_messages = pinentry.lc_messages;
int allow_external_password_cache = pinentry.allow_external_password_cache;
@@ -50,7 +109,18 @@ pinentry_reset (int use_defaults)
char *default_cancel = pinentry.default_cancel;
char *default_prompt = pinentry.default_prompt;
char *default_pwmngr = pinentry.default_pwmngr;
+ char *default_cf_visi = pinentry.default_cf_visi;
+ char *default_tt_visi = pinentry.default_tt_visi;
+ char *default_tt_hide = pinentry.default_tt_hide;
+ char *default_capshint = pinentry.default_capshint;
char *touch_file = pinentry.touch_file;
+ unsigned long owner_pid = pinentry.owner_pid;
+ int owner_uid = pinentry.owner_uid;
+ char *owner_host = pinentry.owner_host;
+ int constraints_enforce = pinentry.constraints_enforce;
+ char *constraints_hint_short = pinentry.constraints_hint_short;
+ char *constraints_hint_long = pinentry.constraints_hint_long;
+ char *constraints_error_title = pinentry.constraints_error_title;
/* These options are set from the command line. Don't reset
them. */
@@ -64,21 +134,33 @@ pinentry_reset (int use_defaults)
pinentry_color_t color_so = pinentry.color_so;
int color_so_bright = pinentry.color_so_bright;
- int timout = pinentry.timeout;
+ int timeout = pinentry.timeout;
+
+ char *invisible_char = pinentry.invisible_char;
+
/* Free any allocated memory. */
if (use_defaults)
{
free (pinentry.ttyname);
- free (pinentry.ttytype);
+ free (pinentry.ttytype_l);
+ free (pinentry.ttyalert);
free (pinentry.lc_ctype);
free (pinentry.lc_messages);
free (pinentry.default_ok);
free (pinentry.default_cancel);
free (pinentry.default_prompt);
free (pinentry.default_pwmngr);
+ free (pinentry.default_cf_visi);
+ free (pinentry.default_tt_visi);
+ free (pinentry.default_tt_hide);
+ free (pinentry.default_capshint);
free (pinentry.touch_file);
+ free (pinentry.owner_host);
free (pinentry.display);
+ free (pinentry.constraints_hint_short);
+ free (pinentry.constraints_hint_long);
+ free (pinentry.constraints_error_title);
}
free (pinentry.title);
@@ -93,11 +175,18 @@ pinentry_reset (int use_defaults)
free (pinentry.repeat_error_string);
free (pinentry.quality_bar);
free (pinentry.quality_bar_tt);
+ free (pinentry.formatted_passphrase_hint);
free (pinentry.keyinfo);
+ free (pinentry.specific_err_info);
/* Reset the pinentry structure. */
memset (&pinentry, 0, sizeof (pinentry));
+ /* Restore options without a default we want to preserve. */
+ pinentry.invisible_char = invisible_char;
+
+ /* Restore other options or set defaults. */
+
if (use_defaults)
{
/* Pinentry timeout in seconds. */
@@ -111,13 +200,15 @@ pinentry_reset (int use_defaults)
pinentry.color_bg = PINENTRY_COLOR_DEFAULT;
pinentry.color_so = PINENTRY_COLOR_DEFAULT;
pinentry.color_so_bright = 0;
+
+ pinentry.owner_uid = -1;
}
- else
- /* Restore the options. */
+ else /* Restore the options. */
{
pinentry.grab = grab;
pinentry.ttyname = ttyname;
- pinentry.ttytype = ttytype;
+ pinentry.ttytype_l = ttytype;
+ pinentry.ttyalert = ttyalert;
pinentry.lc_ctype = lc_ctype;
pinentry.lc_messages = lc_messages;
pinentry.allow_external_password_cache = allow_external_password_cache;
@@ -125,7 +216,18 @@ pinentry_reset (int use_defaults)
pinentry.default_cancel = default_cancel;
pinentry.default_prompt = default_prompt;
pinentry.default_pwmngr = default_pwmngr;
+ pinentry.default_cf_visi = default_cf_visi;
+ pinentry.default_tt_visi = default_tt_visi;
+ pinentry.default_tt_hide = default_tt_hide;
+ pinentry.default_capshint = default_capshint;
pinentry.touch_file = touch_file;
+ pinentry.owner_pid = owner_pid;
+ pinentry.owner_uid = owner_uid;
+ pinentry.owner_host = owner_host;
+ pinentry.constraints_enforce = constraints_enforce;
+ pinentry.constraints_hint_short = constraints_hint_short;
+ pinentry.constraints_hint_long = constraints_hint_long;
+ pinentry.constraints_error_title = constraints_error_title;
pinentry.debug = debug;
pinentry.display = display;
@@ -137,7 +239,7 @@ pinentry_reset (int use_defaults)
pinentry.color_so = color_so;
pinentry.color_so_bright = color_so_bright;
- pinentry.timeout = timout;
+ pinentry.timeout = timeout;
}
}
@@ -154,7 +256,149 @@ pinentry_assuan_reset_handler (assuan_context_t ctx, char *line)
-static int lc_ctype_unknown_warning = 0;
+#ifdef WITH_UTF8_CONVERSION
+char *
+pinentry_utf8_to_local (const char *lc_ctype, const char *text)
+{
+ iconv_t cd;
+ const char *input = text;
+ size_t input_len = strlen (text) + 1;
+ char *output;
+ size_t output_len;
+ char *output_buf;
+ size_t processed;
+ char *old_ctype;
+ char *target_encoding;
+
+ /* If no locale setting could be determined, simply copy the
+ string. */
+ if (!lc_ctype)
+ {
+ if (! lc_ctype_unknown_warning)
+ {
+ fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
+ this_pgmname);
+ lc_ctype_unknown_warning = 1;
+ }
+ return strdup (text);
+ }
+
+ old_ctype = strdup (setlocale (LC_CTYPE, NULL));
+ if (!old_ctype)
+ return NULL;
+ setlocale (LC_CTYPE, lc_ctype);
+ target_encoding = nl_langinfo (CODESET);
+ if (!target_encoding)
+ target_encoding = "?";
+ setlocale (LC_CTYPE, old_ctype);
+ free (old_ctype);
+
+ /* This is overkill, but simplifies the iconv invocation greatly. */
+ output_len = input_len * MB_LEN_MAX;
+ output_buf = output = malloc (output_len);
+ if (!output)
+ return NULL;
+
+ cd = iconv_open (target_encoding, "UTF-8");
+ if (cd == (iconv_t) -1)
+ {
+ fprintf (stderr, "%s: can't convert from UTF-8 to %s: %s\n",
+ this_pgmname, target_encoding, strerror (errno));
+ free (output_buf);
+ return NULL;
+ }
+ processed = iconv (cd, (ICONV_CONST char **)&input, &input_len,
+ &output, &output_len);
+ iconv_close (cd);
+ if (processed == (size_t) -1 || input_len)
+ {
+ fprintf (stderr, "%s: error converting from UTF-8 to %s: %s\n",
+ this_pgmname, target_encoding, strerror (errno));
+ free (output_buf);
+ return NULL;
+ }
+ return output_buf;
+}
+#endif /*WITH_UTF8_CONVERSION*/
+
+
+/* Convert TEXT which is encoded according to LC_CTYPE to UTF-8. With
+ SECURE set to true, use secure memory for the returned buffer.
+ Return NULL on error. */
+#ifdef WITH_UTF8_CONVERSION
+char *
+pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure)
+{
+ char *old_ctype;
+ char *source_encoding;
+ iconv_t cd;
+ const char *input = text;
+ size_t input_len = strlen (text) + 1;
+ char *output;
+ size_t output_len;
+ char *output_buf;
+ size_t processed;
+
+ /* If no locale setting could be determined, simply copy the
+ string. */
+ if (!lc_ctype)
+ {
+ if (! lc_ctype_unknown_warning)
+ {
+ fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
+ this_pgmname);
+ lc_ctype_unknown_warning = 1;
+ }
+ output_buf = secure? secmem_malloc (input_len) : malloc (input_len);
+ if (output_buf)
+ strcpy (output_buf, input);
+ return output_buf;
+ }
+
+ old_ctype = strdup (setlocale (LC_CTYPE, NULL));
+ if (!old_ctype)
+ return NULL;
+ setlocale (LC_CTYPE, lc_ctype);
+ source_encoding = nl_langinfo (CODESET);
+ setlocale (LC_CTYPE, old_ctype);
+ free (old_ctype);
+
+ /* This is overkill, but simplifies the iconv invocation greatly. */
+ output_len = input_len * MB_LEN_MAX;
+ output_buf = output = secure? secmem_malloc (output_len):malloc (output_len);
+ if (!output)
+ return NULL;
+
+ cd = iconv_open ("UTF-8", source_encoding);
+ if (cd == (iconv_t) -1)
+ {
+ fprintf (stderr, "%s: can't convert from %s to UTF-8: %s\n",
+ this_pgmname, source_encoding? source_encoding : "?",
+ strerror (errno));
+ if (secure)
+ secmem_free (output_buf);
+ else
+ free (output_buf);
+ return NULL;
+ }
+ processed = iconv (cd, (ICONV_CONST char **)&input, &input_len,
+ &output, &output_len);
+ iconv_close (cd);
+ if (processed == (size_t) -1 || input_len)
+ {
+ fprintf (stderr, "%s: error converting from %s to UTF-8: %s\n",
+ this_pgmname, source_encoding? source_encoding : "?",
+ strerror (errno));
+ if (secure)
+ secmem_free (output_buf);
+ else
+ free (output_buf);
+ return NULL;
+ }
+ return output_buf;
+}
+#endif /*WITH_UTF8_CONVERSION*/
+
/* Copy TEXT or TEXTLEN to BUFFER and escape as required. Return a
pointer to the end of the new buffer. Note that BUFFER must be
@@ -183,6 +427,172 @@ copy_and_escape (char *buffer, const void *text, size_t textlen)
}
+/* Perform percent unescaping in STRING and return the new valid length
+ of the string. A terminating Nul character is inserted at the end of
+ the unescaped string.
+ */
+static size_t
+do_unescape_inplace (char *s)
+{
+ unsigned char *p, *p0;
+
+ p = p0 = s;
+ while (*s)
+ {
+ if (*s == '%' && s[1] && s[2])
+ {
+ s++;
+ *p++ = xtoi_2 (s);
+ s += 2;
+ }
+ else
+ *p++ = *s++;
+ }
+ *p = 0;
+
+ return (p - p0);
+}
+
+
+/* Return a malloced copy of the commandline for PID. If this is not
+ * possible NULL is returned. */
+#ifndef HAVE_W32_SYSTEM
+static char *
+get_cmdline (unsigned long pid)
+{
+ char buffer[200];
+ FILE *fp;
+ size_t i, n;
+
+ snprintf (buffer, sizeof buffer, "/proc/%lu/cmdline", pid);
+ buffer[sizeof buffer - 1] = 0;
+
+ fp = fopen (buffer, "rb");
+ if (!fp)
+ return NULL;
+ n = fread (buffer, 1, sizeof buffer - 1, fp);
+ if (n < sizeof buffer -1 && ferror (fp))
+ {
+ /* Some error occurred. */
+ fclose (fp);
+ return NULL;
+ }
+ fclose (fp);
+ if (n == 0)
+ return NULL;
+ /* Arguments are delimited by Nuls. We should do proper quoting but
+ * that can be a bit complicated, thus we simply replace the Nuls by
+ * spaces. */
+ for (i=0; i < n; i++)
+ if (!buffer[i] && i < n-1)
+ buffer[i] = ' ';
+ buffer[i] = 0; /* Make sure the last byte is the string terminator. */
+
+ return strdup (buffer);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+/* Atomically ask the kernel for information about process PID.
+ * Return a malloc'ed copy of the process name as long as the process
+ * uid matches UID. If it cannot determine that the process has uid
+ * UID, it returns NULL.
+ *
+ * This is not as informative as get_cmdline, but it verifies that the
+ * process does belong to the user in question.
+ */
+#ifndef HAVE_W32_SYSTEM
+static char *
+get_pid_name_for_uid (unsigned long pid, int uid)
+{
+ char buffer[400];
+ FILE *fp;
+ size_t end, n;
+ char *uidstr;
+
+ snprintf (buffer, sizeof buffer, "/proc/%lu/status", pid);
+ buffer[sizeof buffer - 1] = 0;
+
+ fp = fopen (buffer, "rb");
+ if (!fp)
+ return NULL;
+ n = fread (buffer, 1, sizeof buffer - 1, fp);
+ if (n < sizeof buffer -1 && ferror (fp))
+ {
+ /* Some error occurred. */
+ fclose (fp);
+ return NULL;
+ }
+ fclose (fp);
+ if (n == 0)
+ return NULL;
+ /* Fixme: Is it specified that "Name" is always the first line? For
+ * robustness I would prefer to have a real parser here. -wk */
+ if (strncmp (buffer, "Name:\t", 6))
+ return NULL;
+ end = strcspn (buffer + 6, "\n") + 6;
+ buffer[end] = 0;
+
+ /* check that uid matches what we expect */
+ uidstr = strstr (buffer + end + 1, "\nUid:\t");
+ if (!uidstr)
+ return NULL;
+ if (atoi (uidstr + 6) != uid)
+ return NULL;
+
+ return strdup (buffer + 6);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+/* Return a malloced string with the title. The caller mus free the
+ * string. If no title is available or the title string has an error
+ * NULL is returned. */
+char *
+pinentry_get_title (pinentry_t pe)
+{
+ char *title;
+
+ if (pe->title)
+ title = strdup (pe->title);
+#ifndef HAVE_W32_SYSTEM
+ else if (pe->owner_pid)
+ {
+ char buf[200];
+ struct utsname utsbuf;
+ char *pidname = NULL;
+ char *cmdline = NULL;
+
+ if (pe->owner_host &&
+ !uname (&utsbuf) && utsbuf.nodename &&
+ !strcmp (utsbuf.nodename, pe->owner_host))
+ {
+ pidname = get_pid_name_for_uid (pe->owner_pid, pe->owner_uid);
+ if (pidname)
+ cmdline = get_cmdline (pe->owner_pid);
+ }
+
+ if (pe->owner_host && (cmdline || pidname))
+ snprintf (buf, sizeof buf, "[%lu]@%s (%s)",
+ pe->owner_pid, pe->owner_host, cmdline ? cmdline : pidname);
+ else if (pe->owner_host)
+ snprintf (buf, sizeof buf, "[%lu]@%s",
+ pe->owner_pid, pe->owner_host);
+ else
+ snprintf (buf, sizeof buf, "[%lu] <unknown host>",
+ pe->owner_pid);
+ buf[sizeof buf - 1] = 0;
+ free (pidname);
+ free (cmdline);
+ title = strdup (buf);
+ }
+#endif /*!HAVE_W32_SYSTEM*/
+ else
+ title = strdup (this_pgmname);
+
+ return title;
+}
+
/* Run a quality inquiry for PASSPHRASE of LENGTH. (We need LENGTH
because not all backends might be able to return a proper
@@ -259,6 +669,122 @@ pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length)
}
+/* Run a checkpin inquiry */
+char *
+pinentry_inq_checkpin (pinentry_t pin, const char *passphrase, size_t length)
+{
+ assuan_context_t ctx = pin->ctx_assuan;
+ const char prefix[] = "INQUIRE CHECKPIN ";
+ char *command;
+ char *line;
+ size_t linelen;
+ int gotvalue = 0;
+ char *value = NULL;
+ int rc;
+
+ if (!ctx)
+ return 0; /* Can't run the callback. */
+
+ if (length > 300)
+ length = 300; /* Limit so that it definitely fits into an Assuan
+ line. */
+
+ command = secmem_malloc (strlen (prefix) + 3*length + 1);
+ if (!command)
+ return 0;
+ strcpy (command, prefix);
+ copy_and_escape (command + strlen(command), passphrase, length);
+ rc = assuan_write_line (ctx, command);
+ secmem_free (command);
+ if (rc)
+ {
+ fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
+ return 0;
+ }
+
+ for (;;)
+ {
+ do
+ {
+ rc = assuan_read_line (ctx, &line, &linelen);
+ if (rc)
+ {
+ fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
+ return 0;
+ }
+ }
+ while (*line == '#' || !linelen);
+ if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+ && (!line[3] || line[3] == ' '))
+ break; /* END command received*/
+ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
+ && (!line[3] || line[3] == ' '))
+ break; /* CAN command received*/
+ if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (!line[3] || line[3] == ' '))
+ break; /* ERR command received*/
+ if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
+ continue;
+ gotvalue = 1;
+ value = strdup (line + 2);
+ }
+
+ return value;
+}
+
+
+/* Run a genpin inquiry */
+char *
+pinentry_inq_genpin (pinentry_t pin)
+{
+ assuan_context_t ctx = pin->ctx_assuan;
+ const char prefix[] = "INQUIRE GENPIN";
+ char *line;
+ size_t linelen;
+ int gotvalue = 0;
+ char *value = NULL;
+ int rc;
+
+ if (!ctx)
+ return 0; /* Can't run the callback. */
+
+ rc = assuan_write_line (ctx, prefix);
+ if (rc)
+ {
+ fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
+ return 0;
+ }
+
+ for (;;)
+ {
+ do
+ {
+ rc = assuan_read_line (ctx, &line, &linelen);
+ if (rc)
+ {
+ fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
+ free (value);
+ return 0;
+ }
+ }
+ while (*line == '#' || !linelen);
+ if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+ && (!line[3] || line[3] == ' '))
+ break; /* END command received*/
+ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
+ && (!line[3] || line[3] == ' '))
+ break; /* CAN command received*/
+ if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (!line[3] || line[3] == ' '))
+ break; /* ERR command received*/
+ if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
+ continue;
+ gotvalue = 1;
+ value = strdup (line + 2);
+ }
+
+ return value;
+}
/* Try to make room for at least LEN bytes in the pinentry. Returns
new buffer on success and 0 on failure or when the old buffer is
@@ -375,10 +901,54 @@ pinentry_init (const char *pgmname)
int
pinentry_have_display (int argc, char **argv)
{
+ int found = 0;
+
for (; argc; argc--, argv++)
- if (!strcmp (*argv, "--display") || !strncmp (*argv, "--display=", 10))
- return 1;
- return 0;
+ {
+ if (!strcmp (*argv, "--display"))
+ {
+ if (argv[1] && !remember_display)
+ {
+ remember_display = strdup (argv[1]);
+ if (!remember_display)
+ {
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
+ exit (EXIT_FAILURE);
+ }
+ }
+ found = 1;
+ break;
+ }
+ else if (!strncmp (*argv, "--display=", 10))
+ {
+ if (!remember_display)
+ {
+ remember_display = strdup (*argv+10);
+ if (!remember_display)
+ {
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
+ exit (EXIT_FAILURE);
+ }
+ }
+ found = 1;
+ break;
+ }
+ }
+
+#ifndef HAVE_W32CE_SYSTEM
+ {
+ const char *s;
+ s = getenv ("DISPLAY");
+ if (s && *s)
+ found = 1;
+ }
+#endif
+
+ return found;
}
@@ -394,7 +964,7 @@ my_strusage( int level )
case 11: p = this_pgmname; break;
case 12: p = "pinentry"; break;
case 13: p = PACKAGE_VERSION; break;
- case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break;
+ case 14: p = "Copyright (C) 2016 g10 Code GmbH"; break;
case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
case 1:
case 40:
@@ -406,8 +976,11 @@ my_strusage( int level )
size_t n = 50 + strlen (this_pgmname);
str = malloc (n);
if (str)
- snprintf (str, n, "Usage: %s [options] (-h for help)",
- this_pgmname);
+ {
+ snprintf (str, n, "Usage: %s [options] (-h for help)",
+ this_pgmname);
+ str[n-1] = 0;
+ }
}
p = str;
}
@@ -494,6 +1067,7 @@ pinentry_parse_opts (int argc, char *argv[])
"Grab keyboard only while window is focused"),
ARGPARSE_s_u('W', "parent-wid", "Parent window ID (for positioning)"),
ARGPARSE_s_s('c', "colors", "|STRING|Set custom colors for ncurses"),
+ ARGPARSE_s_s('a', "ttyalert", "|STRING|Set the alert mode (none, beep or flash)"),
ARGPARSE_end()
};
ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
@@ -519,6 +1093,9 @@ pinentry_parse_opts (int argc, char *argv[])
pinentry.display = strdup (pargs.r.ret_str);
if (!pinentry.display)
{
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
exit (EXIT_FAILURE);
}
break;
@@ -526,13 +1103,19 @@ pinentry_parse_opts (int argc, char *argv[])
pinentry.ttyname = strdup (pargs.r.ret_str);
if (!pinentry.ttyname)
{
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
exit (EXIT_FAILURE);
}
break;
case 'N':
- pinentry.ttytype = strdup (pargs.r.ret_str);
- if (!pinentry.ttytype)
+ pinentry.ttytype_l = strdup (pargs.r.ret_str);
+ if (!pinentry.ttytype_l)
{
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
exit (EXIT_FAILURE);
}
break;
@@ -540,6 +1123,9 @@ pinentry_parse_opts (int argc, char *argv[])
pinentry.lc_ctype = strdup (pargs.r.ret_str);
if (!pinentry.lc_ctype)
{
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
exit (EXIT_FAILURE);
}
break;
@@ -547,6 +1133,9 @@ pinentry_parse_opts (int argc, char *argv[])
pinentry.lc_messages = strdup (pargs.r.ret_str);
if (!pinentry.lc_messages)
{
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
exit (EXIT_FAILURE);
}
break;
@@ -570,13 +1159,40 @@ pinentry_parse_opts (int argc, char *argv[])
pinentry.timeout = pargs.r.ret_int;
break;
+ case 'a':
+ pinentry.ttyalert = strdup (pargs.r.ret_str);
+ if (!pinentry.ttyalert)
+ {
+#ifndef HAVE_W32CE_SYSTEM
+ fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
+#endif
+ exit (EXIT_FAILURE);
+ }
+ break;
+
default:
pargs.err = ARGPARSE_PRINT_WARNING;
break;
}
}
+
+ if (!pinentry.display && remember_display)
+ {
+ pinentry.display = remember_display;
+ remember_display = NULL;
+ }
+}
+
+
+/* Set the optional flag used with getinfo. */
+void
+pinentry_set_flavor_flag (const char *string)
+{
+ flavor_flag = string;
}
+
+
static gpg_error_t
option_handler (assuan_context_t ctx, const char *key, const char *value)
@@ -589,6 +1205,12 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
pinentry.grab = 1;
else if (!strcmp (key, "debug-wait"))
{
+#ifndef HAVE_W32_SYSTEM
+ fprintf (stderr, "%s: waiting for debugger - my pid is %u ...\n",
+ this_pgmname, (unsigned int) getpid());
+ sleep (*value?atoi (value):5);
+ fprintf (stderr, "%s: ... okay\n", this_pgmname);
+#endif
}
else if (!strcmp (key, "display"))
{
@@ -608,10 +1230,18 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
}
else if (!strcmp (key, "ttytype"))
{
- if (pinentry.ttytype)
- free (pinentry.ttytype);
- pinentry.ttytype = strdup (value);
- if (!pinentry.ttytype)
+ if (pinentry.ttytype_l)
+ free (pinentry.ttytype_l);
+ pinentry.ttytype_l = strdup (value);
+ if (!pinentry.ttytype_l)
+ return gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "ttyalert"))
+ {
+ if (pinentry.ttyalert)
+ free (pinentry.ttyalert);
+ pinentry.ttyalert = strdup (value);
+ if (!pinentry.ttyalert)
return gpg_error_from_syserror ();
}
else if (!strcmp (key, "lc-ctype"))
@@ -630,6 +1260,46 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
if (!pinentry.lc_messages)
return gpg_error_from_syserror ();
}
+ else if (!strcmp (key, "owner"))
+ {
+ long along;
+ char *endp;
+
+ free (pinentry.owner_host);
+ pinentry.owner_host = NULL;
+ pinentry.owner_uid = -1;
+ pinentry.owner_pid = 0;
+
+ errno = 0;
+ along = strtol (value, &endp, 10);
+ if (along && !errno)
+ {
+ pinentry.owner_pid = (unsigned long)along;
+ if (*endp)
+ {
+ errno = 0;
+ if (*endp == '/') { /* we have a uid */
+ endp++;
+ along = strtol (endp, &endp, 10);
+ if (along >= 0 && !errno)
+ pinentry.owner_uid = (int)along;
+ }
+ if (endp)
+ {
+ while (*endp == ' ')
+ endp++;
+ if (*endp)
+ {
+ pinentry.owner_host = strdup (endp);
+ for (endp=pinentry.owner_host;
+ *endp && *endp != ' '; endp++)
+ ;
+ *endp = 0;
+ }
+ }
+ }
+ }
+ }
else if (!strcmp (key, "parent-wid"))
{
pinentry.parent_wid = atoi (value);
@@ -667,6 +1337,30 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
if (!pinentry.default_pwmngr)
return gpg_error_from_syserror ();
}
+ else if (!strcmp (key, "default-cf-visi"))
+ {
+ pinentry.default_cf_visi = strdup (value);
+ if (!pinentry.default_cf_visi)
+ return gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "default-tt-visi"))
+ {
+ pinentry.default_tt_visi = strdup (value);
+ if (!pinentry.default_tt_visi)
+ return gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "default-tt-hide"))
+ {
+ pinentry.default_tt_hide = strdup (value);
+ if (!pinentry.default_tt_hide)
+ return gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "default-capshint"))
+ {
+ pinentry.default_capshint = strdup (value);
+ if (!pinentry.default_capshint)
+ return gpg_error_from_syserror ();
+ }
else if (!strcmp (key, "allow-external-password-cache") && !*value)
{
pinentry.allow_external_password_cache = 1;
@@ -674,7 +1368,59 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
}
else if (!strcmp (key, "allow-emacs-prompt") && !*value)
{
- return gpg_error (GPG_ERR_NOT_SUPPORTED);
+#ifdef INSIDE_EMACS
+ pinentry_enable_emacs_cmd_handler ();
+#endif
+ }
+ else if (!strcmp (key, "invisible-char"))
+ {
+ if (pinentry.invisible_char)
+ free (pinentry.invisible_char);
+ pinentry.invisible_char = strdup (value);
+ if (!pinentry.invisible_char)
+ return gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "formatted-passphrase") && !*value)
+ {
+ pinentry.formatted_passphrase = 1;
+ }
+ else if (!strcmp (key, "formatted-passphrase-hint"))
+ {
+ if (pinentry.formatted_passphrase_hint)
+ free (pinentry.formatted_passphrase_hint);
+ pinentry.formatted_passphrase_hint = strdup (value);
+ if (!pinentry.formatted_passphrase_hint)
+ return gpg_error_from_syserror ();
+ do_unescape_inplace(pinentry.formatted_passphrase_hint);
+ }
+ else if (!strcmp (key, "constraints-enforce") && !*value)
+ pinentry.constraints_enforce = 1;
+ else if (!strcmp (key, "constraints-hint-short"))
+ {
+ if (pinentry.constraints_hint_short)
+ free (pinentry.constraints_hint_short);
+ pinentry.constraints_hint_short = strdup (value);
+ if (!pinentry.constraints_hint_short)
+ return gpg_error_from_syserror ();
+ do_unescape_inplace(pinentry.constraints_hint_short);
+ }
+ else if (!strcmp (key, "constraints-hint-long"))
+ {
+ if (pinentry.constraints_hint_long)
+ free (pinentry.constraints_hint_long);
+ pinentry.constraints_hint_long = strdup (value);
+ if (!pinentry.constraints_hint_long)
+ return gpg_error_from_syserror ();
+ do_unescape_inplace(pinentry.constraints_hint_long);
+ }
+ else if (!strcmp (key, "constraints-error-title"))
+ {
+ if (pinentry.constraints_error_title)
+ free (pinentry.constraints_error_title);
+ pinentry.constraints_error_title = strdup (value);
+ if (!pinentry.constraints_error_title)
+ return gpg_error_from_syserror ();
+ do_unescape_inplace(pinentry.constraints_error_title);
}
else
return gpg_error (GPG_ERR_UNKNOWN_OPTION);
@@ -702,6 +1448,28 @@ strcpy_escaped (char *d, const char *s)
}
+static void
+write_status_error (assuan_context_t ctx, pinentry_t pe)
+{
+ char buf[500];
+ const char *pgm;
+
+ pgm = strchr (this_pgmname, '-');
+ if (pgm && pgm[1])
+ pgm++;
+ else
+ pgm = this_pgmname;
+
+ snprintf (buf, sizeof buf, "%s.%s %d %s",
+ pgm,
+ pe->specific_err_loc? pe->specific_err_loc : "?",
+ pe->specific_err,
+ pe->specific_err_info? pe->specific_err_info : "");
+ buf[sizeof buf -1] = 0;
+ assuan_write_status (ctx, "ERROR", buf);
+}
+
+
static gpg_error_t
cmd_setdesc (assuan_context_t ctx, char *line)
{
@@ -947,6 +1715,53 @@ cmd_setqualitybar_tt (assuan_context_t ctx, char *line)
return 0;
}
+/* Set the tooltip to be used for a generate action. */
+static gpg_error_t
+cmd_setgenpin_tt (assuan_context_t ctx, char *line)
+{
+ char *newval;
+
+ (void)ctx;
+
+ if (*line)
+ {
+ newval = malloc (strlen (line) + 1);
+ if (!newval)
+ return gpg_error_from_syserror ();
+
+ strcpy_escaped (newval, line);
+ }
+ else
+ newval = NULL;
+ if (pinentry.genpin_tt)
+ free (pinentry.genpin_tt);
+ pinentry.genpin_tt = newval;
+ return 0;
+}
+
+/* Set the label to be used for a generate action. */
+static gpg_error_t
+cmd_setgenpin_label (assuan_context_t ctx, char *line)
+{
+ char *newval;
+
+ (void)ctx;
+
+ if (*line)
+ {
+ newval = malloc (strlen (line) + 1);
+ if (!newval)
+ return gpg_error_from_syserror ();
+
+ strcpy_escaped (newval, line);
+ }
+ else
+ newval = NULL;
+ if (pinentry.genpin_label)
+ free (pinentry.genpin_label);
+ pinentry.genpin_label = newval;
+ return 0;
+}
static gpg_error_t
cmd_getpin (assuan_context_t ctx, char *line)
@@ -975,10 +1790,14 @@ cmd_getpin (assuan_context_t ctx, char *line)
&& ! pinentry.error)
{
char *password;
+ int give_up_on_password_store = 0;
pinentry.tried_password_cache = 1;
- password = password_cache_lookup (pinentry.keyinfo);
+ password = password_cache_lookup (pinentry.keyinfo, &give_up_on_password_store);
+ if (give_up_on_password_store)
+ pinentry.allow_external_password_cache = 0;
+
if (password)
/* There is a cached password. Try it. */
{
@@ -1016,6 +1835,9 @@ cmd_getpin (assuan_context_t ctx, char *line)
}
pinentry.locale_err = 0;
pinentry.specific_err = 0;
+ pinentry.specific_err_loc = NULL;
+ free (pinentry.specific_err_info);
+ pinentry.specific_err_info = NULL;
pinentry.close_button = 0;
pinentry.repeat_okay = 0;
pinentry.one_button = 0;
@@ -1044,7 +1866,14 @@ cmd_getpin (assuan_context_t ctx, char *line)
{
pinentry_setbuffer_clear (&pinentry);
if (pinentry.specific_err)
- return pinentry.specific_err;
+ {
+ write_status_error (ctx, &pinentry);
+
+ if (gpg_err_code (pinentry.specific_err) == GPG_ERR_FULLY_CANCELED)
+ assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
+
+ return pinentry.specific_err;
+ }
return (pinentry.locale_err
? gpg_error (GPG_ERR_LOCALE_PROBLEM)
: gpg_error (GPG_ERR_CANCELED));
@@ -1092,6 +1921,9 @@ cmd_confirm (assuan_context_t ctx, char *line)
pinentry.close_button = 0;
pinentry.locale_err = 0;
pinentry.specific_err = 0;
+ pinentry.specific_err_loc = NULL;
+ free (pinentry.specific_err_info);
+ pinentry.specific_err_info = NULL;
pinentry.canceled = 0;
pinentry_setbuffer_clear (&pinentry);
result = (*pinentry_cmd_handler) (&pinentry);
@@ -1104,17 +1936,24 @@ cmd_confirm (assuan_context_t ctx, char *line)
if (pinentry.close_button)
assuan_write_status (ctx, "BUTTON_INFO", "close");
- if (result)
- return 0;
+ if (result > 0)
+ return 0; /* OK */
if (pinentry.specific_err)
- return pinentry.specific_err;
+ {
+ write_status_error (ctx, &pinentry);
+
+ if (gpg_err_code (pinentry.specific_err) == GPG_ERR_FULLY_CANCELED)
+ assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
+
+ return pinentry.specific_err;
+ }
if (pinentry.locale_err)
return gpg_error (GPG_ERR_LOCALE_PROBLEM);
if (pinentry.one_button)
- return 0;
+ return 0; /* OK */
if (pinentry.canceled)
return gpg_error (GPG_ERR_CANCELED);
@@ -1130,6 +1969,33 @@ cmd_message (assuan_context_t ctx, char *line)
return cmd_confirm (ctx, "--one-button");
}
+
+/* Return a staically allocated string with information on the mode,
+ * uid, and gid of DEVICE. On error "?" is returned if DEVICE is
+ * NULL, "-" is returned. */
+static const char *
+device_stat_string (const char *device)
+{
+#ifdef HAVE_STAT
+ static char buf[40];
+ struct stat st;
+
+ if (!device || !*device)
+ return "-";
+
+ if (stat (device, &st))
+ return "?"; /* Error */
+ snprintf (buf, sizeof buf, "%lo/%lu/%lu",
+ (unsigned long)st.st_mode,
+ (unsigned long)st.st_uid,
+ (unsigned long)st.st_gid);
+ return buf;
+#else
+ return "-";
+#endif
+}
+
+
/* GETINFO <what>
Multipurpose function to return a variety of information.
@@ -1137,23 +2003,67 @@ cmd_message (assuan_context_t ctx, char *line)
version - Return the version of the program.
pid - Return the process id of the server.
+ flavor - Return information about the used pinentry flavor
+ ttyinfo - Return DISPLAY, ttyinfo and an emacs pinentry status
*/
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
int rc;
+ const char *s;
+ char buffer[150];
if (!strcmp (line, "version"))
{
- const char *s = VERSION;
+ s = VERSION;
rc = assuan_send_data (ctx, s, strlen (s));
}
else if (!strcmp (line, "pid"))
{
- char numbuf[50];
- snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
- rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ snprintf (buffer, sizeof buffer, "%lu", (unsigned long)getpid ());
+ buffer[sizeof buffer -1] = 0;
+ rc = assuan_send_data (ctx, buffer, strlen (buffer));
+ }
+ else if (!strcmp (line, "flavor"))
+ {
+ if (!strncmp (this_pgmname, "pinentry-", 9) && this_pgmname[9])
+ s = this_pgmname + 9;
+ else
+ s = this_pgmname;
+
+ snprintf (buffer, sizeof buffer, "%s%s%s",
+ s,
+ flavor_flag? ":":"",
+ flavor_flag? flavor_flag : "");
+ buffer[sizeof buffer -1] = 0;
+ rc = assuan_send_data (ctx, buffer, strlen (buffer));
+ /* if (!rc) */
+ /* rc = assuan_write_status (ctx, "FEATURES", "tabbing foo bar"); */
+ }
+ else if (!strcmp (line, "ttyinfo"))
+ {
+ char emacs_status[10];
+#ifdef INSIDE_EMACS
+ snprintf (emacs_status, sizeof emacs_status,
+ "%d", pinentry_emacs_status ());
+#else
+ strcpy (emacs_status, "-");
+#endif
+ snprintf (buffer, sizeof buffer, "%s %s %s %s %lu/%lu %s",
+ pinentry.ttyname? pinentry.ttyname : "-",
+ pinentry.ttytype_l? pinentry.ttytype_l : "-",
+ pinentry.display? pinentry.display : "-",
+ device_stat_string (pinentry.ttyname),
+#ifdef HAVE_DOSISH_SYSTEM
+ 0l, 0l,
+#else
+ (unsigned long)geteuid (), (unsigned long)getegid (),
+#endif
+ emacs_status
+ );
+ buffer[sizeof buffer -1] = 0;
+ rc = assuan_send_data (ctx, buffer, strlen (buffer));
}
else
rc = gpg_error (GPG_ERR_ASS_PARAMETER);
@@ -1211,6 +2121,8 @@ register_commands (assuan_context_t ctx)
{ "MESSAGE", cmd_message },
{ "SETQUALITYBAR", cmd_setqualitybar },
{ "SETQUALITYBAR_TT", cmd_setqualitybar_tt },
+ { "SETGENPIN", cmd_setgenpin_label },
+ { "SETGENPIN_TT", cmd_setgenpin_tt },
{ "GETINFO", cmd_getinfo },
{ "SETTITLE", cmd_settitle },
{ "SETTIMEOUT", cmd_settimeout },
@@ -1238,8 +2150,10 @@ pinentry_loop2 (int infd, int outfd)
assuan_context_t ctx;
/* Extra check to make sure we have dropped privs. */
+#ifndef HAVE_DOSISH_SYSTEM
if (getuid() != geteuid())
abort ();
+#endif
rc = assuan_new (&ctx);
if (rc)
@@ -1270,6 +2184,9 @@ pinentry_loop2 (int infd, int outfd)
}
assuan_register_option_handler (ctx, option_handler);
+#if 0
+ assuan_set_log_stream (ctx, stderr);
+#endif
assuan_register_reset_notify (ctx, pinentry_assuan_reset_handler);
for (;;)