aboutsummaryrefslogtreecommitdiffstats
path: root/src/m_pinentry/pinentry.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/m_pinentry/pinentry.cpp304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/m_pinentry/pinentry.cpp b/src/m_pinentry/pinentry.cpp
new file mode 100644
index 0000000..981aee3
--- /dev/null
+++ b/src/m_pinentry/pinentry.cpp
@@ -0,0 +1,304 @@
+/* pinentry.c - The PIN entry support library
+ * 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+
+ */
+
+#include "GFModuleCommonUtils.hpp"
+#include "GFSDKBasic.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef WINDOWS
+#include <errno.h>
+#endif
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifndef WINDOWS
+#include <sys/utsname.h>
+#endif
+#ifndef WINDOWS
+#include <locale.h>
+#endif
+#include <limits.h>
+#ifdef WINDOWS
+#include <windows.h>
+#endif
+
+#include <assuan.h>
+#include <qhash.h>
+
+#include "pinentry.h"
+
+#ifdef WINDOWS
+#define getpid() GetCurrentProcessId()
+#endif
+
+/* Keep the name of our program here. */
+static char this_pgmname[50];
+
+struct pinentry pinentry;
+
+static const char *flavor_flag;
+
+/* Return a malloced copy of the commandline for PID. If this is not
+ * possible NULL is returned. */
+#ifndef WINDOWS
+static char *get_cmdline(unsigned long pid) {
+ char buffer[200];
+ FILE *fp;
+ size_t i, n;
+
+ snprintf(buffer, sizeof buffer, "/proc/%lu/cmdline", pid);
+
+ 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 /*!WINDOWS*/
+
+/* 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 WINDOWS
+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);
+
+ 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;
+ buffer[n] = 0;
+ /* 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 /*!WINDOWS*/
+
+const char *pinentry_get_pgmname(void) { return this_pgmname; }
+
+/* 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 WINDOWS
+ else if (pe->owner_pid) {
+ char buf[200];
+ struct utsname utsbuf;
+ char *pidname = NULL;
+ char *cmdline = NULL;
+
+ if (pe->owner_host && !uname(&utsbuf) &&
+ !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);
+ free(pidname);
+ free(cmdline);
+ title = strdup(buf);
+ }
+#endif /*!WINDOWS*/
+ 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
+ C-string.). Returns: A value between -100 and 100 to give an
+ estimate of the passphrase's quality. Negative values are use if
+ the caller won't even accept that passphrase. Note that we expect
+ just one data line which should not be escaped in any represent a
+ numeric signed decimal value. Extra data is currently ignored but
+ should not be send at all. */
+int pinentry_inq_quality(const QString &passphrase) {
+ int score = 0;
+
+ score += std::min(40, static_cast<int>(passphrase.length()) * 2);
+
+ bool has_upper = false;
+ bool has_lower = false;
+ bool has_digit = false;
+ bool has_special = false;
+ for (const auto ch : passphrase) {
+ if (ch.isUpper()) has_upper = true;
+ if (ch.isLower()) has_lower = true;
+ if (ch.isDigit()) has_digit = true;
+ if (!ch.isLetterOrNumber()) has_special = true;
+ }
+
+ int const variety_count =
+ static_cast<int>(has_upper) + static_cast<int>(has_lower) +
+ static_cast<int>(has_digit) + static_cast<int>(has_special);
+ score += variety_count * 10;
+
+ for (auto i = 0; i < passphrase.length() - 1; ++i) {
+ if (passphrase[i] == passphrase[i + 1]) {
+ score -= 5;
+ }
+ }
+
+ QHash<QChar, int> char_count;
+ for (const auto ch : passphrase) {
+ char_count[ch]++;
+ }
+ for (auto &p : char_count) {
+ if (p > 1) {
+ score -= (p - 1) * 3;
+ }
+ }
+
+ QString const lower_password = passphrase.toLower();
+ if (lower_password.contains("password") ||
+ lower_password.contains("123456")) {
+ score -= 30;
+ }
+
+ return std::max(-100, std::min(100, score));
+}
+
+/* Run a genpin inquiry */
+char *pinentry_inq_genpin(pinentry_t pin) {
+ assuan_context_t ctx = (assuan_context_t)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
+ sufficient. */
+char *pinentry_setbufferlen(pinentry_t pin, int len) {
+ char *newp;
+
+ if (pin->pin_len)
+ assert(pin->pin);
+ else
+ assert(!pin->pin);
+
+ if (len < 2048) len = 2048;
+
+ if (len <= pin->pin_len) return pin->pin;
+
+ newp = SecureReallocAsType<char>(pin->pin, len);
+ if (newp) {
+ pin->pin = newp;
+ pin->pin_len = len;
+ } else {
+ GFFreeMemory(pin->pin);
+ pin->pin = 0;
+ pin->pin_len = 0;
+ }
+ return newp;
+}
+
+/* Set the optional flag used with getinfo. */
+void pinentry_set_flavor_flag(const char *string) { flavor_flag = string; }