430ce8bd |
/*
* 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.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
430ce8bd |
* Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> |
49979459 |
* Copyright (C) 2016-2018 David Sommerseth <davids@openvpn.net> |
430ce8bd |
*
* 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.
* |
caa54ac3 |
* 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. |
430ce8bd |
*/
/*
* 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"
|
445b192a |
#ifdef _WIN32 |
430ce8bd |
#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.
*/ |
81d882d5 |
static bool
get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity) |
430ce8bd |
{
HANDLE in = INVALID_HANDLE_VALUE;
HANDLE err = INVALID_HANDLE_VALUE;
DWORD len = 0;
|
81d882d5 |
ASSERT(prompt);
ASSERT(input);
ASSERT(capacity > 0); |
430ce8bd |
input[0] = '\0';
|
81d882d5 |
in = GetStdHandle(STD_INPUT_HANDLE);
err = get_orig_stderr(); |
430ce8bd |
if (in != INVALID_HANDLE_VALUE
&& err != INVALID_HANDLE_VALUE |
81d882d5 |
&& !win32_service_interrupt(&win32_signal)
&& WriteFile(err, prompt, strlen(prompt), &len, NULL)) |
430ce8bd |
{ |
81d882d5 |
bool is_console = (GetFileType(in) == FILE_TYPE_CHAR); |
430ce8bd |
DWORD flags_save = 0;
int status = 0;
WCHAR *winput;
if (is_console) |
81d882d5 |
{
if (GetConsoleMode(in, &flags_save))
{ |
430ce8bd |
DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
if (echo) |
81d882d5 |
{ |
430ce8bd |
flags |= ENABLE_ECHO_INPUT; |
81d882d5 |
}
SetConsoleMode(in, flags);
}
else
{ |
430ce8bd |
is_console = 0; |
81d882d5 |
}
} |
430ce8bd |
if (is_console)
{ |
81d882d5 |
winput = malloc(capacity * sizeof(WCHAR)); |
430ce8bd |
if (winput == NULL) |
81d882d5 |
{ |
430ce8bd |
return false; |
81d882d5 |
} |
430ce8bd |
|
81d882d5 |
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);
} |
430ce8bd |
|
81d882d5 |
string_null_terminate(input, (int)len, capacity);
chomp(input); |
430ce8bd |
if (!echo) |
81d882d5 |
{
WriteFile(err, "\r\n", 2, &len, NULL);
} |
430ce8bd |
if (is_console) |
81d882d5 |
{
SetConsoleMode(in, flags_save);
}
if (status && !win32_service_interrupt(&win32_signal))
{ |
430ce8bd |
return true; |
81d882d5 |
} |
430ce8bd |
}
return false;
}
|
445b192a |
#endif /* _WIN32 */ |
430ce8bd |
#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
*
*/ |
81d882d5 |
static FILE *
open_tty(const bool write) |
430ce8bd |
{
FILE *ret; |
81d882d5 |
ret = fopen("/dev/tty", write ? "w" : "r"); |
430ce8bd |
if (!ret) |
81d882d5 |
{ |
430ce8bd |
ret = write ? stderr : stdin; |
81d882d5 |
} |
430ce8bd |
return ret;
}
/**
* Closes the TTY FILE pointer, but only if it is not a stdin/stderr FILE object.
*
* @params fp FILE pointer to close
*
*/ |
81d882d5 |
static void
close_tty(FILE *fp) |
430ce8bd |
{
if (fp != stderr && fp != stdin) |
81d882d5 |
{
fclose(fp);
} |
430ce8bd |
}
#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
*/ |
81d882d5 |
static bool
get_console_input(const char *prompt, const bool echo, char *input, const int capacity) |
430ce8bd |
{
bool ret = false; |
81d882d5 |
ASSERT(prompt);
ASSERT(input);
ASSERT(capacity > 0); |
430ce8bd |
input[0] = '\0';
|
445b192a |
#if defined(_WIN32) |
81d882d5 |
return get_console_input_win32(prompt, echo, input, capacity); |
430ce8bd |
#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)
*/ |
81d882d5 |
if (!isatty(0) && !isatty(2) ) |
430ce8bd |
{
int fd = open( "/dev/tty", O_RDWR ); |
81d882d5 |
if (fd < 0) |
430ce8bd |
{
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;
|
81d882d5 |
fp = open_tty(true);
fprintf(fp, "%s", prompt);
fflush(fp);
close_tty(fp); |
430ce8bd |
|
81d882d5 |
fp = open_tty(false);
if (fgets(input, capacity, fp) != NULL) |
430ce8bd |
{ |
81d882d5 |
chomp(input); |
430ce8bd |
ret = true;
} |
81d882d5 |
close_tty(fp);
}
else
{
char *gp = getpass(prompt); |
430ce8bd |
if (gp)
{ |
81d882d5 |
strncpynt(input, gp, capacity);
secure_memzero(gp, strlen(gp)); |
430ce8bd |
ret = true;
}
} |
81d882d5 |
#else /* if defined(_WIN32) */
msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt);
#endif /* if defined(_WIN32) */ |
430ce8bd |
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.
*
*/ |
81d882d5 |
bool |
e2a0cad4 |
query_user_exec_builtin(void) |
430ce8bd |
{
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++)
{ |
81d882d5 |
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;
} |
430ce8bd |
}
return ret;
} |