| /* |
| * OpenVPN -- An application to securely tunnel IP networks |
| * over a single UDP port, with support for SSL/TLS-based |
| * session authentication and key exchange, |
| * packet encryption, packet authentication, and |
| * packet compression. |
| * |
| * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
| * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> |
| * Copyright (C) 2016-2018 David Sommerseth <davids@openvpn.net> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * This program 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, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| /* |
| * These functions covers handing user input/output using the default consoles |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #include "syshead.h" |
| #include "console.h" |
| #include "error.h" |
| #include "buffer.h" |
| #include "misc.h" |
| |
| #ifdef _WIN32 |
| |
| #include "win32.h" |
| |
| /** |
| * Get input from a Windows console. |
| * |
| * @param prompt Prompt to display to the user |
| * @param echo Should the user input be displayed in the console |
| * @param input Pointer to the buffer the user input will be saved |
| * @param capacity Size of the buffer for the user input |
| * |
| * @return Return false on input error, or if service |
| * exit event is signaled. |
| */ |
| static bool |
| get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity) |
| { |
| HANDLE in = INVALID_HANDLE_VALUE; |
| HANDLE err = INVALID_HANDLE_VALUE; |
| DWORD len = 0; |
| |
| ASSERT(prompt); |
| ASSERT(input); |
| ASSERT(capacity > 0); |
| |
| input[0] = '\0'; |
| |
| in = GetStdHandle(STD_INPUT_HANDLE); |
| err = get_orig_stderr(); |
| |
| if (in != INVALID_HANDLE_VALUE |
| && err != INVALID_HANDLE_VALUE |
| && !win32_service_interrupt(&win32_signal) |
| && WriteFile(err, prompt, strlen(prompt), &len, NULL)) |
| { |
| bool is_console = (GetFileType(in) == FILE_TYPE_CHAR); |
| DWORD flags_save = 0; |
| int status = 0; |
| WCHAR *winput; |
| |
| if (is_console) |
| { |
| if (GetConsoleMode(in, &flags_save)) |
| { |
| DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; |
| if (echo) |
| { |
| flags |= ENABLE_ECHO_INPUT; |
| } |
| SetConsoleMode(in, flags); |
| } |
| else |
| { |
| is_console = 0; |
| } |
| } |
| |
| if (is_console) |
| { |
| winput = malloc(capacity * sizeof(WCHAR)); |
| if (winput == NULL) |
| { |
| return false; |
| } |
| |
| status = ReadConsoleW(in, winput, capacity, &len, NULL); |
| WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); |
| free(winput); |
| } |
| else |
| { |
| status = ReadFile(in, input, capacity, &len, NULL); |
| } |
| |
| string_null_terminate(input, (int)len, capacity); |
| chomp(input); |
| |
| if (!echo) |
| { |
| WriteFile(err, "\r\n", 2, &len, NULL); |
| } |
| if (is_console) |
| { |
| SetConsoleMode(in, flags_save); |
| } |
| if (status && !win32_service_interrupt(&win32_signal)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| #endif /* _WIN32 */ |
| |
| |
| #ifdef HAVE_GETPASS |
| |
| /** |
| * Open the current console TTY for read/write operations |
| * |
| * @params write If true, the user wants to write to the console |
| * otherwise read from the console |
| * |
| * @returns Returns a FILE pointer to either the TTY in read or write mode |
| * or stdin/stderr, depending on the write flag |
| * |
| */ |
| static FILE * |
| open_tty(const bool write) |
| { |
| FILE *ret; |
| ret = fopen("/dev/tty", write ? "w" : "r"); |
| if (!ret) |
| { |
| ret = write ? stderr : stdin; |
| } |
| return ret; |
| } |
| |
| /** |
| * Closes the TTY FILE pointer, but only if it is not a stdin/stderr FILE object. |
| * |
| * @params fp FILE pointer to close |
| * |
| */ |
| static void |
| close_tty(FILE *fp) |
| { |
| if (fp != stderr && fp != stdin) |
| { |
| fclose(fp); |
| } |
| } |
| |
| #endif /* HAVE_GETPASS */ |
| |
| |
| /** |
| * Core function for getting input from console |
| * |
| * @params prompt The prompt to present to the user |
| * @params echo Should the user see what is being typed |
| * @params input Pointer to the buffer used to save the user input |
| * @params capacity Size of the input buffer |
| * |
| * @returns Returns True if user input was gathered |
| */ |
| static bool |
| get_console_input(const char *prompt, const bool echo, char *input, const int capacity) |
| { |
| bool ret = false; |
| ASSERT(prompt); |
| ASSERT(input); |
| ASSERT(capacity > 0); |
| input[0] = '\0'; |
| |
| #if defined(_WIN32) |
| return get_console_input_win32(prompt, echo, input, capacity); |
| #elif defined(HAVE_GETPASS) |
| |
| /* did we --daemon'ize before asking for passwords? |
| * (in which case neither stdin or stderr are connected to a tty and |
| * /dev/tty can not be open()ed anymore) |
| */ |
| if (!isatty(0) && !isatty(2) ) |
| { |
| int fd = open( "/dev/tty", O_RDWR ); |
| if (fd < 0) |
| { |
| 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 ); |
| } |
| close(fd); |
| } |
| |
| if (echo) |
| { |
| FILE *fp; |
| |
| fp = open_tty(true); |
| fprintf(fp, "%s", prompt); |
| fflush(fp); |
| close_tty(fp); |
| |
| fp = open_tty(false); |
| if (fgets(input, capacity, fp) != NULL) |
| { |
| chomp(input); |
| ret = true; |
| } |
| close_tty(fp); |
| } |
| else |
| { |
| char *gp = getpass(prompt); |
| if (gp) |
| { |
| strncpynt(input, gp, capacity); |
| secure_memzero(gp, strlen(gp)); |
| ret = true; |
| } |
| } |
| #else /* if defined(_WIN32) */ |
| msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); |
| #endif /* if defined(_WIN32) */ |
| return ret; |
| } |
| |
| |
| /** |
| * @copydoc query_user_exec() |
| * |
| * Default method for querying user using default stdin/stdout on a console. |
| * This needs to be available as a backup interface for the alternative |
| * implementations in case they cannot query through their implementation |
| * specific methods. |
| * |
| * If no alternative implementation is declared, a wrapper in console.h will ensure |
| * query_user_exec() will call this function instead. |
| * |
| */ |
| bool |
| query_user_exec_builtin(void) |
| { |
| bool ret = true; /* Presume everything goes okay */ |
| int i; |
| |
| /* Loop through configured query_user slots */ |
| for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++) |
| { |
| if (!get_console_input(query_user[i].prompt, query_user[i].echo, |
| query_user[i].response, query_user[i].response_len) ) |
| { |
| /* Force the final result state to failed on failure */ |
| ret = false; |
| } |
| } |
| |
| return ret; |
| } |