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-2024 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10 * Copyright (C) 2016-2024 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
60static bool
61get_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
154static FILE *
155open_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
172static void
173close_tty(FILE *fp)
174{
175 if (fp != stderr && fp != stdin)
176 {
177 fclose(fp);
178 }
179}
180
181#endif /* HAVE_TERMIOS_H */
182
183
194static bool
195get_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
280bool
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}
void string_null_terminate(char *str, int len, int capacity)
Definition buffer.c:597
void chomp(char *str)
Definition buffer.c:614
struct _query_user query_user[QUERY_USER_NUMSLOTS]
Global variable, declared in console.c.
Definition console.c:41
#define QUERY_USER_NUMSLOTS
Definition console.h:42
bool query_user_exec_builtin(void)
Wrapper function enabling query_user_exec() if no alternative methods have been enabled.
static bool get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
Get input from a Windows console.
static bool get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
Core function for getting input from console.
static SERVICE_STATUS status
Definition interactive.c:53
@ write
static int orig_stderr
Definition error.c:505
int get_orig_stderr(void)
Definition error.c:508
#define M_FATAL
Definition error.h:89
#define msg(flags,...)
Definition error.h:144
#define ASSERT(x)
Definition error.h:195
#define M_WARN
Definition error.h:91
#define M_ERRNO
Definition error.h:94
char * response
The user's response.
Definition console.h:37
bool win32_service_interrupt(struct win32_signal *ws)
Definition win32.c:625