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-2023 OpenVPN Inc <sales@openvpn.net>
9  * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10  * Copyright (C) 2016-2023 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 #endif
34 
35 #include "syshead.h"
36 #include "console.h"
37 #include "error.h"
38 #include "buffer.h"
39 #include "misc.h"
40 
41 #ifdef HAVE_TERMIOS_H
42 #include <termios.h>
43 #endif
44 
45 #ifdef _WIN32
46 
47 #include "win32.h"
48 
60 static bool
61 get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
62 {
63  ASSERT(prompt);
64  ASSERT(input);
65  ASSERT(capacity > 0);
66 
67  input[0] = '\0';
68 
69  HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
70  int orig_stderr = get_orig_stderr(); /* guaranteed to be always valid */
71  if ((in == INVALID_HANDLE_VALUE)
73  || (_write(orig_stderr, prompt, strlen(prompt)) == -1))
74  {
75  msg(M_WARN|M_ERRNO, "get_console_input_win32(): unexpected error");
76  return false;
77  }
78 
79  bool is_console = (GetFileType(in) == FILE_TYPE_CHAR);
80  DWORD flags_save = 0;
81  int status = 0;
82  WCHAR *winput;
83 
84  if (is_console)
85  {
86  if (GetConsoleMode(in, &flags_save))
87  {
88  DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
89  if (echo)
90  {
91  flags |= ENABLE_ECHO_INPUT;
92  }
93  SetConsoleMode(in, flags);
94  }
95  else
96  {
97  is_console = 0;
98  }
99  }
100 
101  DWORD len = 0;
102 
103  if (is_console)
104  {
105  winput = malloc(capacity * sizeof(WCHAR));
106  if (winput == NULL)
107  {
108  return false;
109  }
110 
111  status = ReadConsoleW(in, winput, capacity, &len, NULL);
112  WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL);
113  free(winput);
114  }
115  else
116  {
117  status = ReadFile(in, input, capacity, &len, NULL);
118  }
119 
120  string_null_terminate(input, (int)len, capacity);
121  chomp(input);
122 
123  if (!echo)
124  {
125  _write(orig_stderr, "\r\n", 2);
126  }
127  if (is_console)
128  {
129  SetConsoleMode(in, flags_save);
130  }
132  {
133  return true;
134  }
135 
136  return false;
137 }
138 
139 #endif /* _WIN32 */
140 
141 
142 #ifdef HAVE_TERMIOS_H
143 
154 static FILE *
155 open_tty(const bool write)
156 {
157  FILE *ret;
158  ret = fopen("/dev/tty", write ? "w" : "r");
159  if (!ret)
160  {
161  ret = write ? stderr : stdin;
162  }
163  return ret;
164 }
165 
172 static void
173 close_tty(FILE *fp)
174 {
175  if (fp != stderr && fp != stdin)
176  {
177  fclose(fp);
178  }
179 }
180 
181 #endif /* HAVE_TERMIOS_H */
182 
183 
194 static bool
195 get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
196 {
197  bool ret = false;
198  ASSERT(prompt);
199  ASSERT(input);
200  ASSERT(capacity > 0);
201  input[0] = '\0';
202 
203 #if defined(_WIN32)
204  return get_console_input_win32(prompt, echo, input, capacity);
205 #elif defined(HAVE_TERMIOS_H)
206  bool restore_tty = false;
207  struct termios tty_tmp, tty_save;
208 
209  /* did we --daemon'ize before asking for passwords?
210  * (in which case neither stdin or stderr are connected to a tty and
211  * /dev/tty can not be open()ed anymore)
212  */
213  if (!isatty(0) && !isatty(2) )
214  {
215  int fd = open( "/dev/tty", O_RDWR );
216  if (fd < 0)
217  {
218  msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a "
219  "controlling tty nor systemd - can't ask for '%s'. If you used --daemon, "
220  "you need to use --askpass to make passphrase-protected keys work, and you "
221  "can not use --auth-nocache.", prompt );
222  }
223  close(fd);
224  }
225 
226  FILE *fp = open_tty(true);
227  fprintf(fp, "%s", prompt);
228  fflush(fp);
229  close_tty(fp);
230 
231  fp = open_tty(false);
232 
233  if (!echo && (tcgetattr(fileno(fp), &tty_tmp) == 0))
234  {
235  tty_save = tty_tmp;
236  tty_tmp.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ISIG);
237  restore_tty = (tcsetattr(fileno(fp), TCSAFLUSH, &tty_tmp) == 0);
238  }
239 
240  if (fgets(input, capacity, fp) != NULL)
241  {
242  chomp(input);
243  ret = true;
244  }
245 
246  if (restore_tty)
247  {
248  if (tcsetattr(fileno(fp), TCSAFLUSH, &tty_save) == -1)
249  {
250  msg(M_WARN | M_ERRNO, "tcsetattr() failed to restore tty settings");
251  }
252 
253  /* Echo the non-echoed newline */
254  close_tty(fp);
255  fp = open_tty(true);
256  fprintf(fp, "\n");
257  fflush(fp);
258  }
259 
260  close_tty(fp);
261 #else /* if defined(_WIN32) */
262  msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt);
263 #endif /* if defined(_WIN32) */
264  return ret;
265 }
266 
267 
280 bool
282 {
283  bool ret = true; /* Presume everything goes okay */
284  int i;
285 
286  /* Loop through configured query_user slots */
287  for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++)
288  {
289  if (!get_console_input(query_user[i].prompt, query_user[i].echo,
290  query_user[i].response, query_user[i].response_len) )
291  {
292  /* Force the final result state to failed on failure */
293  ret = false;
294  }
295  }
296 
297  return ret;
298 }
orig_stderr
static int orig_stderr
Definition: error.c:505
error.h
M_ERRNO
#define M_ERRNO
Definition: error.h:100
M_FATAL
#define M_FATAL
Definition: error.h:95
win32.h
_query_user::echo
bool echo
True: The user should see what is being typed, otherwise mask it.
Definition: console.h:39
query_user
struct _query_user query_user[QUERY_USER_NUMSLOTS]
Global variable, declared in console.c.
Definition: console.c:41
get_console_input_win32
static bool get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
Get input from a Windows console.
Definition: console_builtin.c:61
console.h
win32_signal
Definition: win32.h:151
ASSERT
#define ASSERT(x)
Definition: error.h:201
write
@ write
Definition: interactive.c:206
misc.h
M_WARN
#define M_WARN
Definition: error.h:97
get_orig_stderr
int get_orig_stderr()
Definition: error.c:508
QUERY_USER_NUMSLOTS
#define QUERY_USER_NUMSLOTS
Definition: console.h:42
buffer.h
syshead.h
query_user_exec_builtin
bool query_user_exec_builtin(void)
Wrapper function enabling query_user_exec() if no alternative methods have been enabled.
Definition: console_builtin.c:281
_query_user::prompt
char * prompt
Prompt to present to the user.
Definition: console.h:35
string_null_terminate
void string_null_terminate(char *str, int len, int capacity)
Definition: buffer.c:641
chomp
void chomp(char *str)
Definition: buffer.c:658
_query_user::response
char * response
The user's response.
Definition: console.h:37
status
static SERVICE_STATUS status
Definition: interactive.c:52
get_console_input
static bool get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
Core function for getting input from console.
Definition: console_builtin.c:195
config.h
win32_service_interrupt
bool win32_service_interrupt(struct win32_signal *ws)
Definition: win32.c:622
msg
#define msg(flags,...)
Definition: error.h:150