This is will provide an interface for other mechanisms to be used to
query the user for information, such as usernames, passwords, etc.
It has also been a goal to make it possible to query for all the
information in one call and not do it sequencially as before.
[v5 - Ensure password prompt is only displayed if we should read
from stdin ]
[v4 - add a simple wrapper combining query_user_{init,add,exec}()
- change disapproved &= syntax ]
[v3 - Avoid the dynamic list, use a static list of QUERY_USER_NUMSLOTS
- The list of query_user data is now a global variable
- Replaced query_user_init() with query_user_clear()
- Make query_user_add() a void function
- Rebased against master/600dd9a16fc61 ]
[v2 - Removed the QUERY_USER_FOREACH macro
- Avoided using underscore prefix in function names
- Make query_user_init() do M_FATAL and become a void function
instead of returning false in these unlikely situations ]
Signed-off-by: David Sommerseth <davids@openvpn.net>
Acked-by: Selva Nair <selva.nair@gmail.com>
Message-Id: 1472233732-27074-1-git-send-email-davids@openvpn.net
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg00137.html
| ... | ... |
@@ -6,6 +6,8 @@ |
| 6 | 6 |
* packet compression. |
| 7 | 7 |
* |
| 8 | 8 |
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> |
| 9 |
+ * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> |
|
| 10 |
+ * Copyright (C) 2016 David Sommerseth <davids@openvpn.net> |
|
| 9 | 11 |
* |
| 10 | 12 |
* This program is free software; you can redistribute it and/or modify |
| 11 | 13 |
* it under the terms of the GNU General Public License version 2 |
| ... | ... |
@@ -38,219 +40,43 @@ |
| 38 | 38 |
#include <systemd/sd-daemon.h> |
| 39 | 39 |
#endif |
| 40 | 40 |
|
| 41 |
-#ifdef WIN32 |
|
| 42 | 41 |
|
| 43 |
-#include "win32.h" |
|
| 42 |
+struct _query_user query_user[QUERY_USER_NUMSLOTS]; /* GLOBAL */ |
|
| 44 | 43 |
|
| 45 |
-/* |
|
| 46 |
- * Get input from console. |
|
| 47 |
- * |
|
| 48 |
- * Return false on input error, or if service |
|
| 49 |
- * exit event is signaled. |
|
| 50 |
- */ |
|
| 51 |
- |
|
| 52 |
-static bool |
|
| 53 |
-get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity) |
|
| 54 |
-{
|
|
| 55 |
- HANDLE in = INVALID_HANDLE_VALUE; |
|
| 56 |
- HANDLE err = INVALID_HANDLE_VALUE; |
|
| 57 |
- DWORD len = 0; |
|
| 58 |
- |
|
| 59 |
- ASSERT (prompt); |
|
| 60 |
- ASSERT (input); |
|
| 61 |
- ASSERT (capacity > 0); |
|
| 62 |
- |
|
| 63 |
- input[0] = '\0'; |
|
| 64 |
- |
|
| 65 |
- in = GetStdHandle (STD_INPUT_HANDLE); |
|
| 66 |
- err = get_orig_stderr (); |
|
| 67 |
- |
|
| 68 |
- if (in != INVALID_HANDLE_VALUE |
|
| 69 |
- && err != INVALID_HANDLE_VALUE |
|
| 70 |
- && !win32_service_interrupt (&win32_signal) |
|
| 71 |
- && WriteFile (err, prompt, strlen (prompt), &len, NULL)) |
|
| 72 |
- {
|
|
| 73 |
- bool is_console = (GetFileType (in) == FILE_TYPE_CHAR); |
|
| 74 |
- DWORD flags_save = 0; |
|
| 75 |
- int status = 0; |
|
| 76 |
- WCHAR *winput; |
|
| 77 |
- |
|
| 78 |
- if (is_console) |
|
| 79 |
- {
|
|
| 80 |
- if (GetConsoleMode (in, &flags_save)) |
|
| 81 |
- {
|
|
| 82 |
- DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; |
|
| 83 |
- if (echo) |
|
| 84 |
- flags |= ENABLE_ECHO_INPUT; |
|
| 85 |
- SetConsoleMode (in, flags); |
|
| 86 |
- } |
|
| 87 |
- else |
|
| 88 |
- is_console = 0; |
|
| 89 |
- } |
|
| 90 |
- |
|
| 91 |
- if (is_console) |
|
| 92 |
- {
|
|
| 93 |
- winput = malloc (capacity * sizeof (WCHAR)); |
|
| 94 |
- if (winput == NULL) |
|
| 95 |
- return false; |
|
| 96 |
- |
|
| 97 |
- status = ReadConsoleW (in, winput, capacity, &len, NULL); |
|
| 98 |
- WideCharToMultiByte (CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); |
|
| 99 |
- free (winput); |
|
| 100 |
- } |
|
| 101 |
- else |
|
| 102 |
- status = ReadFile (in, input, capacity, &len, NULL); |
|
| 103 |
- |
|
| 104 |
- string_null_terminate (input, (int)len, capacity); |
|
| 105 |
- chomp (input); |
|
| 106 |
- |
|
| 107 |
- if (!echo) |
|
| 108 |
- WriteFile (err, "\r\n", 2, &len, NULL); |
|
| 109 |
- if (is_console) |
|
| 110 |
- SetConsoleMode (in, flags_save); |
|
| 111 |
- if (status && !win32_service_interrupt (&win32_signal)) |
|
| 112 |
- return true; |
|
| 113 |
- } |
|
| 114 |
- |
|
| 115 |
- return false; |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-#endif |
|
| 119 |
- |
|
| 120 |
-#ifdef HAVE_GETPASS |
|
| 121 |
- |
|
| 122 |
-static FILE * |
|
| 123 |
-open_tty (const bool write) |
|
| 124 |
-{
|
|
| 125 |
- FILE *ret; |
|
| 126 |
- ret = fopen ("/dev/tty", write ? "w" : "r");
|
|
| 127 |
- if (!ret) |
|
| 128 |
- ret = write ? stderr : stdin; |
|
| 129 |
- return ret; |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-static void |
|
| 133 |
-close_tty (FILE *fp) |
|
| 134 |
-{
|
|
| 135 |
- if (fp != stderr && fp != stdin) |
|
| 136 |
- fclose (fp); |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-#endif |
|
| 140 | 44 |
|
| 141 |
-#ifdef ENABLE_SYSTEMD |
|
| 142 |
- |
|
| 143 |
-/* |
|
| 144 |
- * is systemd running |
|
| 145 |
- */ |
|
| 146 |
- |
|
| 147 |
-static bool |
|
| 148 |
-check_systemd_running () |
|
| 45 |
+void query_user_clear() |
|
| 149 | 46 |
{
|
| 150 |
- struct stat c; |
|
| 151 |
- |
|
| 152 |
- /* We simply test whether the systemd cgroup hierarchy is |
|
| 153 |
- * mounted, as well as the systemd-ask-password executable |
|
| 154 |
- * being available */ |
|
| 47 |
+ int i; |
|
| 155 | 48 |
|
| 156 |
- return (sd_booted() > 0) |
|
| 157 |
- && (stat(SYSTEMD_ASK_PASSWORD_PATH, &c) == 0); |
|
| 158 |
- |
|
| 159 |
-} |
|
| 160 |
- |
|
| 161 |
-static bool |
|
| 162 |
-get_console_input_systemd (const char *prompt, const bool echo, char *input, const int capacity) |
|
| 163 |
-{
|
|
| 164 |
- int std_out; |
|
| 165 |
- bool ret = false; |
|
| 166 |
- struct argv argv; |
|
| 167 |
- |
|
| 168 |
- argv_init (&argv); |
|
| 169 |
- argv_printf (&argv, SYSTEMD_ASK_PASSWORD_PATH); |
|
| 170 |
- argv_printf_cat (&argv, "%s", prompt); |
|
| 171 |
- |
|
| 172 |
- if ((std_out = openvpn_popen (&argv, NULL)) < 0) {
|
|
| 173 |
- return false; |
|
| 174 |
- } |
|
| 175 |
- |
|
| 176 |
- memset (input, 0, capacity); |
|
| 177 |
- if (read (std_out, input, capacity-1) > 0) |
|
| 178 |
- {
|
|
| 179 |
- chomp (input); |
|
| 180 |
- ret = true; |
|
| 49 |
+ for( i = 0; i < QUERY_USER_NUMSLOTS; i++ ) {
|
|
| 50 |
+ CLEAR(query_user[i]); |
|
| 181 | 51 |
} |
| 182 |
- close (std_out); |
|
| 183 |
- |
|
| 184 |
- argv_reset (&argv); |
|
| 185 |
- |
|
| 186 |
- return ret; |
|
| 187 | 52 |
} |
| 188 | 53 |
|
| 189 | 54 |
|
| 190 |
-#endif |
|
| 191 |
- |
|
| 192 |
-/* |
|
| 193 |
- * Get input from console |
|
| 194 |
- */ |
|
| 195 |
-bool |
|
| 196 |
-get_console_input (const char *prompt, const bool echo, char *input, const int capacity) |
|
| 55 |
+void query_user_add(char *prompt, size_t prompt_len, |
|
| 56 |
+ char *resp, size_t resp_len, |
|
| 57 |
+ bool echo) |
|
| 197 | 58 |
{
|
| 198 |
- bool ret = false; |
|
| 199 |
- ASSERT (prompt); |
|
| 200 |
- ASSERT (input); |
|
| 201 |
- ASSERT (capacity > 0); |
|
| 202 |
- input[0] = '\0'; |
|
| 203 |
- |
|
| 204 |
-#ifdef ENABLE_SYSTEMD |
|
| 205 |
- if (check_systemd_running ()) |
|
| 206 |
- return get_console_input_systemd (prompt, echo, input, capacity); |
|
| 207 |
-#endif |
|
| 59 |
+ int i; |
|
| 208 | 60 |
|
| 209 |
-#if defined(WIN32) |
|
| 210 |
- return get_console_input_win32 (prompt, echo, input, capacity); |
|
| 211 |
-#elif defined(HAVE_GETPASS) |
|
| 61 |
+ /* Ensure input is sane. All these must be present otherwise it is |
|
| 62 |
+ * a programming error. |
|
| 63 |
+ */ |
|
| 64 |
+ ASSERT( prompt_len > 0 && prompt != NULL && resp_len > 0 && resp != NULL ); |
|
| 212 | 65 |
|
| 213 |
- /* did we --daemon'ize before asking for passwords? |
|
| 214 |
- * (in which case neither stdin or stderr are connected to a tty and |
|
| 215 |
- * /dev/tty can not be open()ed anymore) |
|
| 216 |
- */ |
|
| 217 |
- if ( !isatty(0) && !isatty(2) ) |
|
| 218 |
- {
|
|
| 219 |
- int fd = open( "/dev/tty", O_RDWR ); |
|
| 220 |
- if ( fd < 0 ) |
|
| 221 |
- { msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a controlling tty nor systemd - can't ask for '%s'. If you used --daemon, you need to use --askpass to make passphrase-protected keys work, and you can not use --auth-nocache.", prompt ); }
|
|
| 222 |
- close(fd); |
|
| 223 |
- } |
|
| 224 |
- |
|
| 225 |
- if (echo) |
|
| 226 |
- {
|
|
| 227 |
- FILE *fp; |
|
| 228 |
- |
|
| 229 |
- fp = open_tty (true); |
|
| 230 |
- fprintf (fp, "%s", prompt); |
|
| 231 |
- fflush (fp); |
|
| 232 |
- close_tty (fp); |
|
| 233 |
- |
|
| 234 |
- fp = open_tty (false); |
|
| 235 |
- if (fgets (input, capacity, fp) != NULL) |
|
| 236 |
- {
|
|
| 237 |
- chomp (input); |
|
| 238 |
- ret = true; |
|
| 66 |
+ /* Seek to the last unused slot */ |
|
| 67 |
+ for (i = 0; i < QUERY_USER_NUMSLOTS; i++) {
|
|
| 68 |
+ if( query_user[i].prompt == NULL ) {
|
|
| 69 |
+ break; |
|
| 239 | 70 |
} |
| 240 |
- close_tty (fp); |
|
| 241 | 71 |
} |
| 242 |
- else |
|
| 243 |
- {
|
|
| 244 |
- char *gp = getpass (prompt); |
|
| 245 |
- if (gp) |
|
| 246 |
- {
|
|
| 247 |
- strncpynt (input, gp, capacity); |
|
| 248 |
- memset (gp, 0, strlen (gp)); |
|
| 249 |
- ret = true; |
|
| 250 |
- } |
|
| 251 |
- } |
|
| 252 |
-#else |
|
| 253 |
- msg (M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); |
|
| 254 |
-#endif |
|
| 255 |
- return ret; |
|
| 72 |
+ ASSERT( i < QUERY_USER_NUMSLOTS ); /* Unlikely, but we want to panic if it happens */ |
|
| 73 |
+ |
|
| 74 |
+ /* Save the information needed for the user interaction */ |
|
| 75 |
+ query_user[i].prompt = prompt; |
|
| 76 |
+ query_user[i].prompt_len = prompt_len; |
|
| 77 |
+ query_user[i].response = resp; |
|
| 78 |
+ query_user[i].response_len = resp_len; |
|
| 79 |
+ query_user[i].echo = echo; |
|
| 256 | 80 |
} |
| ... | ... |
@@ -6,6 +6,8 @@ |
| 6 | 6 |
* packet compression. |
| 7 | 7 |
* |
| 8 | 8 |
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> |
| 9 |
+ * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> |
|
| 10 |
+ * Copyright (C) 2016 David Sommerseth <davids@openvpn.net> |
|
| 9 | 11 |
* |
| 10 | 12 |
* This program is free software; you can redistribute it and/or modify |
| 11 | 13 |
* it under the terms of the GNU General Public License version 2 |
| ... | ... |
@@ -27,7 +29,90 @@ |
| 27 | 27 |
|
| 28 | 28 |
#include "basic.h" |
| 29 | 29 |
|
| 30 |
-bool |
|
| 31 |
-get_console_input (const char *prompt, const bool echo, char *input, const int capacity); |
|
| 30 |
+/** |
|
| 31 |
+ * Configuration setup for declaring what kind of information to ask a user for |
|
| 32 |
+ */ |
|
| 33 |
+struct _query_user {
|
|
| 34 |
+ char *prompt; /**< Prompt to present to the user */ |
|
| 35 |
+ size_t prompt_len; /**< Lenght of the prompt string */ |
|
| 36 |
+ char *response; /**< The user's response */ |
|
| 37 |
+ size_t response_len; /**< Lenght the of the user reposone */ |
|
| 38 |
+ bool echo; /**< True: The user should see what is being typed, otherwise mask it */ |
|
| 39 |
+}; |
|
| 40 |
+ |
|
| 41 |
+#define QUERY_USER_NUMSLOTS 10 |
|
| 42 |
+extern struct _query_user query_user[]; /**< Global variable, declared in console.c */ |
|
| 43 |
+ |
|
| 44 |
+/** |
|
| 45 |
+ * Wipes all data put into all of the query_user structs |
|
| 46 |
+ * |
|
| 47 |
+ */ |
|
| 48 |
+void query_user_clear (); |
|
| 49 |
+ |
|
| 50 |
+ |
|
| 51 |
+/** |
|
| 52 |
+ * Adds an item to ask the user for |
|
| 53 |
+ * |
|
| 54 |
+ * @param prompt Prompt to display to the user |
|
| 55 |
+ * @param prompt_len Length of the prompt string |
|
| 56 |
+ * @param resp String containing the user response |
|
| 57 |
+ * @param resp_len Lenght of the response string |
|
| 58 |
+ * @param echo Should the user input be echoed to the user? If False, input will be masked |
|
| 59 |
+ * |
|
| 60 |
+ */ |
|
| 61 |
+void query_user_add (char *prompt, size_t prompt_len, |
|
| 62 |
+ char *resp, size_t resp_len, |
|
| 63 |
+ bool echo); |
|
| 64 |
+ |
|
| 65 |
+ |
|
| 66 |
+/** |
|
| 67 |
+ * Executes a configured setup, using the built-in method for querying the user. |
|
| 68 |
+ * This method uses the console/TTY directly. |
|
| 69 |
+ * |
|
| 70 |
+ * @param setup Pointer to the setup defining what to ask the user |
|
| 71 |
+ * |
|
| 72 |
+ * @return True if executing all the defined steps completed successfully |
|
| 73 |
+ */ |
|
| 74 |
+bool query_user_exec_builtin (); |
|
| 75 |
+ |
|
| 76 |
+ |
|
| 77 |
+#ifdef QUERY_USER_EXEC_ALTERNATIVE |
|
| 78 |
+/** |
|
| 79 |
+ * Executes a configured setup, using the compiled method for querying the user |
|
| 80 |
+ * |
|
| 81 |
+ * @param setup Pointer to the setup defining what to ask the user |
|
| 82 |
+ * |
|
| 83 |
+ * @return True if executing all the defined steps completed successfully |
|
| 84 |
+ */ |
|
| 85 |
+bool query_user_exec (); |
|
| 86 |
+ |
|
| 87 |
+#else /* QUERY_USER_EXEC_ALTERNATIVE not defined*/ |
|
| 88 |
+/** |
|
| 89 |
+ * Wrapper function enabling query_user_exec() if no alternative methods have |
|
| 90 |
+ * been enabled |
|
| 91 |
+ * |
|
| 92 |
+ */ |
|
| 93 |
+static bool query_user_exec () |
|
| 94 |
+{
|
|
| 95 |
+ return query_user_exec_builtin(); |
|
| 96 |
+} |
|
| 97 |
+#endif /* QUERY_USER_EXEC_ALTERNATIVE */ |
|
| 98 |
+ |
|
| 99 |
+ |
|
| 100 |
+/** |
|
| 101 |
+ * A plain "make Gert happy" wrapper. Same arguments as @query_user_add |
|
| 102 |
+ * |
|
| 103 |
+ * FIXME/TODO: Remove this when refactoring the complete user query process |
|
| 104 |
+ * to be called at start-up initialization of OpenVPN. |
|
| 105 |
+ * |
|
| 106 |
+ */ |
|
| 107 |
+static bool query_user_SINGLE (char *prompt, size_t prompt_len, |
|
| 108 |
+ char *resp, size_t resp_len, |
|
| 109 |
+ bool echo) |
|
| 110 |
+{
|
|
| 111 |
+ query_user_clear(); |
|
| 112 |
+ query_user_add(prompt, prompt_len, resp, resp_len, echo); |
|
| 113 |
+ return query_user_exec(); |
|
| 114 |
+} |
|
| 32 | 115 |
|
| 33 | 116 |
#endif |
| 34 | 117 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,261 @@ |
| 0 |
+/* |
|
| 1 |
+ * OpenVPN -- An application to securely tunnel IP networks |
|
| 2 |
+ * over a single UDP port, with support for SSL/TLS-based |
|
| 3 |
+ * session authentication and key exchange, |
|
| 4 |
+ * packet encryption, packet authentication, and |
|
| 5 |
+ * packet compression. |
|
| 6 |
+ * |
|
| 7 |
+ * Copyright (C) 2002-2016 OpenVPN Technologies, Inc. <sales@openvpn.net> |
|
| 8 |
+ * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> |
|
| 9 |
+ * Copyright (C) 2016 David Sommerseth <davids@openvpn.net> |
|
| 10 |
+ * |
|
| 11 |
+ * This program is free software; you can redistribute it and/or modify |
|
| 12 |
+ * it under the terms of the GNU General Public License version 2 |
|
| 13 |
+ * as published by the Free Software Foundation. |
|
| 14 |
+ * |
|
| 15 |
+ * This program is distributed in the hope that it will be useful, |
|
| 16 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 17 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 18 |
+ * GNU General Public License for more details. |
|
| 19 |
+ * |
|
| 20 |
+ * You should have received a copy of the GNU General Public License |
|
| 21 |
+ * along with this program (see the file COPYING included with this |
|
| 22 |
+ * distribution); if not, write to the Free Software Foundation, Inc., |
|
| 23 |
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 24 |
+ */ |
|
| 25 |
+ |
|
| 26 |
+/* |
|
| 27 |
+ * These functions covers handing user input/output using the default consoles |
|
| 28 |
+ * |
|
| 29 |
+ */ |
|
| 30 |
+ |
|
| 31 |
+#ifdef HAVE_CONFIG_H |
|
| 32 |
+#include "config.h" |
|
| 33 |
+#elif defined(_MSC_VER) |
|
| 34 |
+#include "config-msvc.h" |
|
| 35 |
+#endif |
|
| 36 |
+ |
|
| 37 |
+#include "syshead.h" |
|
| 38 |
+#include "console.h" |
|
| 39 |
+#include "error.h" |
|
| 40 |
+#include "buffer.h" |
|
| 41 |
+#include "misc.h" |
|
| 42 |
+ |
|
| 43 |
+#ifdef WIN32 |
|
| 44 |
+ |
|
| 45 |
+#include "win32.h" |
|
| 46 |
+ |
|
| 47 |
+/** |
|
| 48 |
+ * Get input from a Windows console. |
|
| 49 |
+ * |
|
| 50 |
+ * @param prompt Prompt to display to the user |
|
| 51 |
+ * @param echo Should the user input be displayed in the console |
|
| 52 |
+ * @param input Pointer to the buffer the user input will be saved |
|
| 53 |
+ * @param capacity Size of the buffer for the user input |
|
| 54 |
+ * |
|
| 55 |
+ * @return Return false on input error, or if service |
|
| 56 |
+ * exit event is signaled. |
|
| 57 |
+ */ |
|
| 58 |
+static bool get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity) |
|
| 59 |
+{
|
|
| 60 |
+ HANDLE in = INVALID_HANDLE_VALUE; |
|
| 61 |
+ HANDLE err = INVALID_HANDLE_VALUE; |
|
| 62 |
+ DWORD len = 0; |
|
| 63 |
+ |
|
| 64 |
+ ASSERT (prompt); |
|
| 65 |
+ ASSERT (input); |
|
| 66 |
+ ASSERT (capacity > 0); |
|
| 67 |
+ |
|
| 68 |
+ input[0] = '\0'; |
|
| 69 |
+ |
|
| 70 |
+ in = GetStdHandle (STD_INPUT_HANDLE); |
|
| 71 |
+ err = get_orig_stderr (); |
|
| 72 |
+ |
|
| 73 |
+ if (in != INVALID_HANDLE_VALUE |
|
| 74 |
+ && err != INVALID_HANDLE_VALUE |
|
| 75 |
+ && !win32_service_interrupt (&win32_signal) |
|
| 76 |
+ && WriteFile (err, prompt, strlen (prompt), &len, NULL)) |
|
| 77 |
+ {
|
|
| 78 |
+ bool is_console = (GetFileType (in) == FILE_TYPE_CHAR); |
|
| 79 |
+ DWORD flags_save = 0; |
|
| 80 |
+ int status = 0; |
|
| 81 |
+ WCHAR *winput; |
|
| 82 |
+ |
|
| 83 |
+ if (is_console) |
|
| 84 |
+ {
|
|
| 85 |
+ if (GetConsoleMode (in, &flags_save)) |
|
| 86 |
+ {
|
|
| 87 |
+ DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; |
|
| 88 |
+ if (echo) |
|
| 89 |
+ flags |= ENABLE_ECHO_INPUT; |
|
| 90 |
+ SetConsoleMode (in, flags); |
|
| 91 |
+ } else |
|
| 92 |
+ is_console = 0; |
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ if (is_console) |
|
| 96 |
+ {
|
|
| 97 |
+ winput = malloc (capacity * sizeof (WCHAR)); |
|
| 98 |
+ if (winput == NULL) |
|
| 99 |
+ return false; |
|
| 100 |
+ |
|
| 101 |
+ status = ReadConsoleW (in, winput, capacity, &len, NULL); |
|
| 102 |
+ WideCharToMultiByte (CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); |
|
| 103 |
+ free (winput); |
|
| 104 |
+ } else |
|
| 105 |
+ status = ReadFile (in, input, capacity, &len, NULL); |
|
| 106 |
+ |
|
| 107 |
+ string_null_terminate (input, (int)len, capacity); |
|
| 108 |
+ chomp (input); |
|
| 109 |
+ |
|
| 110 |
+ if (!echo) |
|
| 111 |
+ WriteFile (err, "\r\n", 2, &len, NULL); |
|
| 112 |
+ if (is_console) |
|
| 113 |
+ SetConsoleMode (in, flags_save); |
|
| 114 |
+ if (status && !win32_service_interrupt (&win32_signal)) |
|
| 115 |
+ return true; |
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ return false; |
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+#endif /* WIN32 */ |
|
| 122 |
+ |
|
| 123 |
+ |
|
| 124 |
+#ifdef HAVE_GETPASS |
|
| 125 |
+ |
|
| 126 |
+/** |
|
| 127 |
+ * Open the current console TTY for read/write operations |
|
| 128 |
+ * |
|
| 129 |
+ * @params write If true, the user wants to write to the console |
|
| 130 |
+ * otherwise read from the console |
|
| 131 |
+ * |
|
| 132 |
+ * @returns Returns a FILE pointer to either the TTY in read or write mode |
|
| 133 |
+ * or stdin/stderr, depending on the write flag |
|
| 134 |
+ * |
|
| 135 |
+ */ |
|
| 136 |
+static FILE * open_tty (const bool write) |
|
| 137 |
+{
|
|
| 138 |
+ FILE *ret; |
|
| 139 |
+ ret = fopen ("/dev/tty", write ? "w" : "r");
|
|
| 140 |
+ if (!ret) |
|
| 141 |
+ ret = write ? stderr : stdin; |
|
| 142 |
+ return ret; |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+/** |
|
| 146 |
+ * Closes the TTY FILE pointer, but only if it is not a stdin/stderr FILE object. |
|
| 147 |
+ * |
|
| 148 |
+ * @params fp FILE pointer to close |
|
| 149 |
+ * |
|
| 150 |
+ */ |
|
| 151 |
+static void close_tty (FILE *fp) |
|
| 152 |
+{
|
|
| 153 |
+ if (fp != stderr && fp != stdin) |
|
| 154 |
+ fclose (fp); |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+#endif /* HAVE_GETPASS */ |
|
| 158 |
+ |
|
| 159 |
+ |
|
| 160 |
+/** |
|
| 161 |
+ * Core function for getting input from console |
|
| 162 |
+ * |
|
| 163 |
+ * @params prompt The prompt to present to the user |
|
| 164 |
+ * @params echo Should the user see what is being typed |
|
| 165 |
+ * @params input Pointer to the buffer used to save the user input |
|
| 166 |
+ * @params capacity Size of the input buffer |
|
| 167 |
+ * |
|
| 168 |
+ * @returns Returns True if user input was gathered |
|
| 169 |
+ */ |
|
| 170 |
+static bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity) |
|
| 171 |
+{
|
|
| 172 |
+ bool ret = false; |
|
| 173 |
+ ASSERT (prompt); |
|
| 174 |
+ ASSERT (input); |
|
| 175 |
+ ASSERT (capacity > 0); |
|
| 176 |
+ input[0] = '\0'; |
|
| 177 |
+ |
|
| 178 |
+#if defined(WIN32) |
|
| 179 |
+ return get_console_input_win32 (prompt, echo, input, capacity); |
|
| 180 |
+#elif defined(HAVE_GETPASS) |
|
| 181 |
+ |
|
| 182 |
+ /* did we --daemon'ize before asking for passwords? |
|
| 183 |
+ * (in which case neither stdin or stderr are connected to a tty and |
|
| 184 |
+ * /dev/tty can not be open()ed anymore) |
|
| 185 |
+ */ |
|
| 186 |
+ if ( !isatty(0) && !isatty(2) ) |
|
| 187 |
+ {
|
|
| 188 |
+ int fd = open( "/dev/tty", O_RDWR ); |
|
| 189 |
+ if ( fd < 0 ) |
|
| 190 |
+ {
|
|
| 191 |
+ msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a " |
|
| 192 |
+ "controlling tty nor systemd - can't ask for '%s'. If you used --daemon, " |
|
| 193 |
+ "you need to use --askpass to make passphrase-protected keys work, and you " |
|
| 194 |
+ "can not use --auth-nocache.", prompt ); |
|
| 195 |
+ } |
|
| 196 |
+ close(fd); |
|
| 197 |
+ } |
|
| 198 |
+ |
|
| 199 |
+ if (echo) |
|
| 200 |
+ {
|
|
| 201 |
+ FILE *fp; |
|
| 202 |
+ |
|
| 203 |
+ fp = open_tty (true); |
|
| 204 |
+ fprintf (fp, "%s", prompt); |
|
| 205 |
+ fflush (fp); |
|
| 206 |
+ close_tty (fp); |
|
| 207 |
+ |
|
| 208 |
+ fp = open_tty (false); |
|
| 209 |
+ if (fgets (input, capacity, fp) != NULL) |
|
| 210 |
+ {
|
|
| 211 |
+ chomp (input); |
|
| 212 |
+ ret = true; |
|
| 213 |
+ } |
|
| 214 |
+ close_tty (fp); |
|
| 215 |
+ } else {
|
|
| 216 |
+ char *gp = getpass (prompt); |
|
| 217 |
+ if (gp) |
|
| 218 |
+ {
|
|
| 219 |
+ strncpynt (input, gp, capacity); |
|
| 220 |
+ memset (gp, 0, strlen (gp)); |
|
| 221 |
+ ret = true; |
|
| 222 |
+ } |
|
| 223 |
+ } |
|
| 224 |
+#else |
|
| 225 |
+ msg (M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); |
|
| 226 |
+#endif |
|
| 227 |
+ return ret; |
|
| 228 |
+} |
|
| 229 |
+ |
|
| 230 |
+ |
|
| 231 |
+/** |
|
| 232 |
+ * @copydoc query_user_exec() |
|
| 233 |
+ * |
|
| 234 |
+ * Default method for querying user using default stdin/stdout on a console. |
|
| 235 |
+ * This needs to be available as a backup interface for the alternative |
|
| 236 |
+ * implementations in case they cannot query through their implementation |
|
| 237 |
+ * specific methods. |
|
| 238 |
+ * |
|
| 239 |
+ * If no alternative implementation is declared, a wrapper in console.h will ensure |
|
| 240 |
+ * query_user_exec() will call this function instead. |
|
| 241 |
+ * |
|
| 242 |
+ */ |
|
| 243 |
+bool query_user_exec_builtin() |
|
| 244 |
+{
|
|
| 245 |
+ bool ret = true; /* Presume everything goes okay */ |
|
| 246 |
+ int i; |
|
| 247 |
+ |
|
| 248 |
+ /* Loop through configured query_user slots */ |
|
| 249 |
+ for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++) |
|
| 250 |
+ {
|
|
| 251 |
+ if (!get_console_input(query_user[i].prompt, query_user[i].echo, |
|
| 252 |
+ query_user[i].response, query_user[i].response_len) ) |
|
| 253 |
+ {
|
|
| 254 |
+ /* Force the final result state to failed on failure */ |
|
| 255 |
+ ret = false; |
|
| 256 |
+ } |
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ return ret; |
|
| 260 |
+} |
| ... | ... |
@@ -6,6 +6,8 @@ |
| 6 | 6 |
* packet compression. |
| 7 | 7 |
* |
| 8 | 8 |
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> |
| 9 |
+ * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> |
|
| 10 |
+ * Copyright (C) 2016 David Sommerseth <davids@openvpn.net> |
|
| 9 | 11 |
* |
| 10 | 12 |
* This program is free software; you can redistribute it and/or modify |
| 11 | 13 |
* it under the terms of the GNU General Public License version 2 |
| ... | ... |
@@ -1085,9 +1087,11 @@ get_user_pass_cr (struct user_pass *up, |
| 1085 | 1085 |
struct buffer user_prompt = alloc_buf_gc (128, &gc); |
| 1086 | 1086 |
|
| 1087 | 1087 |
buf_printf (&user_prompt, "NEED-OK|%s|%s:", prefix, up->username); |
| 1088 |
- |
|
| 1089 |
- if (!get_console_input (BSTR (&user_prompt), true, up->password, USER_PASS_LEN)) |
|
| 1090 |
- msg (M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix); |
|
| 1088 |
+ if (!query_user_SINGLE (BSTR(&user_prompt), BLEN(&user_prompt), |
|
| 1089 |
+ up->password, USER_PASS_LEN, false)) |
|
| 1090 |
+ {
|
|
| 1091 |
+ msg (M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix); |
|
| 1092 |
+ } |
|
| 1091 | 1093 |
|
| 1092 | 1094 |
if (!strlen (up->password)) |
| 1093 | 1095 |
strcpy (up->password, "ok"); |
| ... | ... |
@@ -1163,13 +1167,17 @@ get_user_pass_cr (struct user_pass *up, |
| 1163 | 1163 |
if (ac) |
| 1164 | 1164 |
{
|
| 1165 | 1165 |
char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); |
| 1166 |
- struct buffer packed_resp; |
|
| 1166 |
+ struct buffer packed_resp, challenge; |
|
| 1167 | 1167 |
|
| 1168 |
+ challenge = alloc_buf_gc (14+strlen(ac->challenge_text), &gc); |
|
| 1169 |
+ buf_printf (&challenge, "CHALLENGE: %s", ac->challenge_text); |
|
| 1168 | 1170 |
buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN); |
| 1169 |
- msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", ac->challenge_text); |
|
| 1170 |
- if (!get_console_input (ac->challenge_text, BOOL_CAST(ac->flags&CR_ECHO), |
|
| 1171 |
- response, USER_PASS_LEN)) |
|
| 1172 |
- msg (M_FATAL, "ERROR: could not read challenge response from stdin"); |
|
| 1171 |
+ |
|
| 1172 |
+ if (!query_user_SINGLE (BSTR(&challenge), BLEN(&challenge), |
|
| 1173 |
+ response, USER_PASS_LEN, BOOL_CAST(ac->flags&CR_ECHO))) |
|
| 1174 |
+ {
|
|
| 1175 |
+ msg (M_FATAL, "ERROR: could not read challenge response from stdin"); |
|
| 1176 |
+ } |
|
| 1173 | 1177 |
strncpynt (up->username, ac->user, USER_PASS_LEN); |
| 1174 | 1178 |
buf_printf (&packed_resp, "CRV1::%s::%s", ac->state_id, response); |
| 1175 | 1179 |
} |
| ... | ... |
@@ -1184,32 +1192,49 @@ get_user_pass_cr (struct user_pass *up, |
| 1184 | 1184 |
struct buffer user_prompt = alloc_buf_gc (128, &gc); |
| 1185 | 1185 |
struct buffer pass_prompt = alloc_buf_gc (128, &gc); |
| 1186 | 1186 |
|
| 1187 |
+ query_user_clear (); |
|
| 1187 | 1188 |
buf_printf (&user_prompt, "Enter %s Username:", prefix); |
| 1188 | 1189 |
buf_printf (&pass_prompt, "Enter %s Password:", prefix); |
| 1189 | 1190 |
|
| 1190 | 1191 |
if (username_from_stdin && !(flags & GET_USER_PASS_PASSWORD_ONLY)) |
| 1191 | 1192 |
{
|
| 1192 |
- if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) |
|
| 1193 |
- msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); |
|
| 1193 |
+ query_user_add (BSTR(&user_prompt), BLEN(&user_prompt), |
|
| 1194 |
+ up->username, USER_PASS_LEN, true); |
|
| 1195 |
+ } |
|
| 1196 |
+ |
|
| 1197 |
+ if (password_from_stdin) |
|
| 1198 |
+ {
|
|
| 1199 |
+ query_user_add (BSTR(&pass_prompt), BLEN(&pass_prompt), |
|
| 1200 |
+ up->password, USER_PASS_LEN, false); |
|
| 1201 |
+ } |
|
| 1202 |
+ |
|
| 1203 |
+ if( !query_user_exec () ) |
|
| 1204 |
+ {
|
|
| 1205 |
+ msg(M_FATAL, "ERROR: Failed retrieving username or password"); |
|
| 1206 |
+ } |
|
| 1207 |
+ |
|
| 1208 |
+ if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) |
|
| 1209 |
+ {
|
|
| 1194 | 1210 |
if (strlen (up->username) == 0) |
| 1195 | 1211 |
msg (M_FATAL, "ERROR: %s username is empty", prefix); |
| 1196 | 1212 |
} |
| 1197 | 1213 |
|
| 1198 |
- if (password_from_stdin && !get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) |
|
| 1199 |
- msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); |
|
| 1200 |
- |
|
| 1201 | 1214 |
#ifdef ENABLE_CLIENT_CR |
| 1202 | 1215 |
if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE) && response_from_stdin) |
| 1203 | 1216 |
{
|
| 1204 | 1217 |
char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); |
| 1205 |
- struct buffer packed_resp; |
|
| 1218 |
+ struct buffer packed_resp, challenge; |
|
| 1206 | 1219 |
char *pw64=NULL, *resp64=NULL; |
| 1207 | 1220 |
|
| 1208 |
- msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", auth_challenge); |
|
| 1221 |
+ challenge = alloc_buf_gc (14+strlen(auth_challenge), &gc); |
|
| 1222 |
+ buf_printf (&challenge, "CHALLENGE: %s", auth_challenge); |
|
| 1209 | 1223 |
|
| 1210 |
- if (!get_console_input (auth_challenge, BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO), |
|
| 1211 |
- response, USER_PASS_LEN)) |
|
| 1212 |
- msg (M_FATAL, "ERROR: could not read static challenge response from stdin"); |
|
| 1224 |
+ if (!query_user_SINGLE (BSTR(&challenge), BLEN(&challenge), |
|
| 1225 |
+ response, USER_PASS_LEN, |
|
| 1226 |
+ BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO))) |
|
| 1227 |
+ {
|
|
| 1228 |
+ msg (M_FATAL, "ERROR: could not retrieve static challenge response"); |
|
| 1229 |
+ } |
|
| 1213 | 1230 |
if (openvpn_base64_encode(up->password, strlen(up->password), &pw64) == -1 |
| 1214 | 1231 |
|| openvpn_base64_encode(response, strlen(response), &resp64) == -1) |
| 1215 | 1232 |
msg (M_FATAL, "ERROR: could not base64-encode password/static_response"); |
| ... | ... |
@@ -744,9 +744,10 @@ _pkcs11_openvpn_show_pkcs11_ids_pin_prompt ( |
| 744 | 744 |
ASSERT (token!=NULL); |
| 745 | 745 |
|
| 746 | 746 |
buf_printf (&pass_prompt, "Please enter '%s' token PIN or 'cancel': ", token->display); |
| 747 |
- |
|
| 748 |
- if (!get_console_input (BSTR (&pass_prompt), false, pin, pin_max)) {
|
|
| 749 |
- msg (M_FATAL, "Cannot read password from stdin"); |
|
| 747 |
+ if (!query_user_SINGLE(BSTR(&pass_prompt), BLEN(&pass_prompt), |
|
| 748 |
+ pin, pin_max, false)) |
|
| 749 |
+ {
|
|
| 750 |
+ msg (M_FATAL, "Could not retrieve the PIN"); |
|
| 750 | 751 |
} |
| 751 | 752 |
|
| 752 | 753 |
gc_free (&gc); |