src/openvpn/console_builtin.c
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;
 }