OpenVPN
console_builtin.c
Go to the documentation of this file.
1 /*
2  * OpenVPN -- An application to securely tunnel IP networks
3  * over a single UDP port, with support for SSL/TLS-based
4  * session authentication and key exchange,
5  * packet encryption, packet authentication, and
6  * packet compression.
7  *
8  * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
9  * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10  * Copyright (C) 2016-2021 David Sommerseth <davids@openvpn.net>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2
14  * as published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 HAVE_TERMIOS_H
44 #include <termios.h>
45 #endif
46 
47 #ifdef _WIN32
48 
49 #include "win32.h"
50 
62 static bool
63 get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
64 {
65  ASSERT(prompt);
66  ASSERT(input);
67  ASSERT(capacity > 0);
68 
69  input[0] = '\0';
70 
71  HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
72  int orig_stderr = get_orig_stderr(); // guaranteed to be always valid
73  if ((in == INVALID_HANDLE_VALUE)
75  || (_write(orig_stderr, prompt, strlen(prompt)) == -1))
76  {
77  msg(M_WARN|M_ERRNO, "get_console_input_win32(): unexpected error");
78  return false;
79  }
80 
81  bool is_console = (GetFileType(in) == FILE_TYPE_CHAR);
82  DWORD flags_save = 0;
83  int status = 0;
84  WCHAR *winput;
85 
86  if (is_console)
87  {
88  if (GetConsoleMode(in, &flags_save))
89  {
90  DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
91  if (echo)
92  {
93  flags |= ENABLE_ECHO_INPUT;
94  }
95  SetConsoleMode(in, flags);
96  }
97  else
98  {
99  is_console = 0;
100  }
101  }
102 
103  DWORD len = 0;
104 
105  if (is_console)
106  {
107  winput = malloc(capacity * sizeof(WCHAR));
108  if (winput == NULL)
109  {
110  return false;
111  }
112 
113  status = ReadConsoleW(in, winput, capacity, &len, NULL);
114  WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL);
115  free(winput);
116  }
117  else
118  {
119  status = ReadFile(in, input, capacity, &len, NULL);
120  }
121 
122  string_null_terminate(input, (int)len, capacity);
123  chomp(input);
124 
125  if (!echo)
126  {
127  _write(orig_stderr, "\r\n", 2);
128  }
129  if (is_console)
130  {
131  SetConsoleMode(in, flags_save);
132  }
133  if (status && !win32_service_interrupt(&win32_signal))
134  {
135  return true;
136  }
137 
138  return false;
139 }
140 
141 #endif /* _WIN32 */
142 
143 
144 #ifdef HAVE_TERMIOS_H
145 
156 static FILE *
157 open_tty(const bool write)
158 {
159  FILE *ret;
160  ret = fopen("/dev/tty", write ? "w" : "r");
161  if (!ret)
162  {
163  ret = write ? stderr : stdin;
164  }
165  return ret;
166 }
167 
174 static void
175 close_tty(FILE *fp)
176 {
177  if (fp != stderr && fp != stdin)
178  {
179  fclose(fp);
180  }
181 }
182 
183 #endif /* HAVE_TERMIOS_H */
184 
185 
196 static bool
197 get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
198 {
199  bool ret = false;
200  ASSERT(prompt);
201  ASSERT(input);
202  ASSERT(capacity > 0);
203  input[0] = '\0';
204 
205 #if defined(_WIN32)
206  return get_console_input_win32(prompt, echo, input, capacity);
207 #elif defined(HAVE_TERMIOS_H)
208  bool restore_tty = false;
209  struct termios tty_tmp, tty_save;
210 
211  /* did we --daemon'ize before asking for passwords?
212  * (in which case neither stdin or stderr are connected to a tty and
213  * /dev/tty can not be open()ed anymore)
214  */
215  if (!isatty(0) && !isatty(2) )
216  {
217  int fd = open( "/dev/tty", O_RDWR );
218  if (fd < 0)
219  {
220  msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a "
221  "controlling tty nor systemd - can't ask for '%s'. If you used --daemon, "
222  "you need to use --askpass to make passphrase-protected keys work, and you "
223  "can not use --auth-nocache.", prompt );
224  }
225  close(fd);
226  }
227 
228  FILE *fp = open_tty(true);
229  fprintf(fp, "%s", prompt);
230  fflush(fp);
231  close_tty(fp);
232 
233  fp = open_tty(false);
234 
235  if (!echo && (tcgetattr(fileno(fp), &tty_tmp) == 0))
236  {
237  tty_save = tty_tmp;
238  tty_tmp.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ISIG);
239  restore_tty = (tcsetattr(fileno(fp), TCSAFLUSH, &tty_tmp) == 0);
240  }
241 
242  if (fgets(input, capacity, fp) != NULL)
243  {
244  chomp(input);
245  ret = true;
246  }
247 
248  if (restore_tty)
249  {
250  if (tcsetattr(fileno(fp), TCSAFLUSH, &tty_save) == -1)
251  {
252  msg(M_WARN | M_ERRNO, "tcsetattr() failed to restore tty settings");
253  }
254 
255  /* Echo the non-echoed newline */
256  close_tty(fp);
257  fp = open_tty(true);
258  fprintf(fp, "\n");
259  fflush(fp);
260  }
261 
262  close_tty(fp);
263 #else /* if defined(_WIN32) */
264  msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt);
265 #endif /* if defined(_WIN32) */
266  return ret;
267 }
268 
269 
282 bool
284 {
285  bool ret = true; /* Presume everything goes okay */
286  int i;
287 
288  /* Loop through configured query_user slots */
289  for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++)
290  {
291  if (!get_console_input(query_user[i].prompt, query_user[i].echo,
292  query_user[i].response, query_user[i].response_len) )
293  {
294  /* Force the final result state to failed on failure */
295  ret = false;
296  }
297  }
298 
299  return ret;
300 }
char * response
The user&#39;s response.
Definition: console.h:37
int get_orig_stderr()
Definition: error.c:496
bool win32_service_interrupt(struct win32_signal *ws)
Definition: win32.c:620
struct _query_user query_user[QUERY_USER_NUMSLOTS]
Global variable, declared in console.c.
Definition: console.c:43
char * prompt
Prompt to present to the user.
Definition: console.h:35
#define ASSERT(x)
Definition: error.h:204
list flags
void string_null_terminate(char *str, int len, int capacity)
Definition: buffer.c:633
static bool get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
Get input from a Windows console.
static int orig_stderr
Definition: error.c:494
#define malloc
Definition: cmocka.c:1795
void chomp(char *str)
Definition: buffer.c:650
#define QUERY_USER_NUMSLOTS
Definition: console.h:42
#define M_ERRNO
Definition: error.h:103
bool echo
True: The user should see what is being typed, otherwise mask it.
Definition: console.h:39
bool query_user_exec_builtin(void)
Wrapper function enabling query_user_exec() if no alternative methods have been enabled.
#define msg(flags,...)
Definition: error.h:153
#define M_FATAL
Definition: error.h:98
static bool get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
Core function for getting input from console.
#define M_WARN
Definition: error.h:100
#define free
Definition: cmocka.c:1850
static SERVICE_STATUS status
Definition: interactive.c:56