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-2025 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10 * Copyright (C) 2016-2025 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, see <https://www.gnu.org/licenses/>.
23 */
24
25/*
26 * These functions covers handing user input/output using the default consoles
27 *
28 */
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#include "syshead.h"
35#include "console.h"
36#include "error.h"
37#include "buffer.h"
38#include "misc.h"
39
40#ifdef HAVE_TERMIOS_H
41#include <termios.h>
42#endif
43
44#ifdef _WIN32
45
46#include "win32.h"
47
48#if defined(__GNUC__) || defined(__clang__)
49#pragma GCC diagnostic push
50#pragma GCC diagnostic ignored "-Wconversion"
51#endif
52
64static bool
65get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
66{
67 ASSERT(prompt);
68 ASSERT(input);
69 ASSERT(capacity > 0);
70
71 input[0] = '\0';
72
73 HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
74 int orig_stderr = get_orig_stderr(); /* guaranteed to be always valid */
75 if ((in == INVALID_HANDLE_VALUE) || win32_service_interrupt(&win32_signal)
76 || (_write(orig_stderr, prompt, strlen(prompt)) == -1))
77 {
78 msg(M_WARN | M_ERRNO, "get_console_input_win32(): unexpected error");
79 return false;
80 }
81
82 bool is_console = (GetFileType(in) == FILE_TYPE_CHAR);
83 DWORD flags_save = 0;
84 int status = 0;
85 WCHAR *winput;
86
87 if (is_console)
88 {
89 if (GetConsoleMode(in, &flags_save))
90 {
91 DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
92 if (echo)
93 {
94 flags |= ENABLE_ECHO_INPUT;
95 }
96 SetConsoleMode(in, flags);
97 }
98 else
99 {
100 is_console = 0;
101 }
102 }
103
104 DWORD len = 0;
105
106 if (is_console)
107 {
108 winput = malloc(capacity * sizeof(WCHAR));
109 if (winput == NULL)
110 {
111 return false;
112 }
113
114 status = ReadConsoleW(in, winput, capacity, &len, NULL);
115 WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL);
116 free(winput);
117 }
118 else
119 {
120 status = ReadFile(in, input, capacity, &len, NULL);
121 }
122
123 string_null_terminate(input, (int)len, capacity);
124 chomp(input);
125
126 if (!echo)
127 {
128 _write(orig_stderr, "\r\n", 2);
129 }
130 if (is_console)
131 {
132 SetConsoleMode(in, flags_save);
133 }
135 {
136 return true;
137 }
138
139 return false;
140}
141
142#if defined(__GNUC__) || defined(__clang__)
143#pragma GCC diagnostic pop
144#endif
145
146#endif /* _WIN32 */
147
148
149#ifdef HAVE_TERMIOS_H
150
161static FILE *
162open_tty(const bool write)
163{
164 FILE *ret;
165 ret = fopen("/dev/tty", write ? "w" : "r");
166 if (!ret)
167 {
168 ret = write ? stderr : stdin;
169 }
170 return ret;
171}
172
179static void
180close_tty(FILE *fp)
181{
182 if (fp != stderr && fp != stdin)
183 {
184 fclose(fp);
185 }
186}
187
188#endif /* HAVE_TERMIOS_H */
189
190
201static bool
202get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
203{
204 bool ret = false;
205 ASSERT(prompt);
206 ASSERT(input);
207 ASSERT(capacity > 0);
208 input[0] = '\0';
209
210#if defined(_WIN32)
211 return get_console_input_win32(prompt, echo, input, capacity);
212#elif defined(HAVE_TERMIOS_H)
213 bool restore_tty = false;
214 struct termios tty_tmp, tty_save;
215
216 /* did we --daemon'ize before asking for passwords?
217 * (in which case neither stdin or stderr are connected to a tty and
218 * /dev/tty can not be open()ed anymore)
219 */
220 if (!isatty(0) && !isatty(2))
221 {
222 int fd = open("/dev/tty", O_RDWR);
223 if (fd < 0)
224 {
225 msg(M_FATAL,
226 "neither stdin nor stderr are a tty device and you have neither a "
227 "controlling tty nor systemd - can't ask for '%s'. If you used --daemon, "
228 "you need to use --askpass to make passphrase-protected keys work, and you "
229 "can not use --auth-nocache.",
230 prompt);
231 }
232 close(fd);
233 }
234
235 FILE *fp = open_tty(true);
236 fprintf(fp, "%s", prompt);
237 fflush(fp);
238 close_tty(fp);
239
240 fp = open_tty(false);
241
242 if (!echo && (tcgetattr(fileno(fp), &tty_tmp) == 0))
243 {
244 tty_save = tty_tmp;
245 tty_tmp.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ISIG);
246 restore_tty = (tcsetattr(fileno(fp), TCSAFLUSH, &tty_tmp) == 0);
247 }
248
249 if (fgets(input, capacity, fp) != NULL)
250 {
251 chomp(input);
252 ret = true;
253 }
254
255 if (restore_tty)
256 {
257 if (tcsetattr(fileno(fp), TCSAFLUSH, &tty_save) == -1)
258 {
259 msg(M_WARN | M_ERRNO, "tcsetattr() failed to restore tty settings");
260 }
261
262 /* Echo the non-echoed newline */
263 close_tty(fp);
264 fp = open_tty(true);
265 fprintf(fp, "\n");
266 fflush(fp);
267 }
268
269 close_tty(fp);
270#else /* if defined(_WIN32) */
271 msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt);
272#endif /* if defined(_WIN32) */
273 return ret;
274}
275
276#if defined(__GNUC__) || defined(__clang__)
277#pragma GCC diagnostic push
278#pragma GCC diagnostic ignored "-Wconversion"
279#endif
280
293bool
295{
296 bool ret = true; /* Presume everything goes okay */
297 int i;
298
299 /* Loop through configured query_user slots */
300 for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++)
301 {
302 if (!get_console_input(query_user[i].prompt, query_user[i].echo, query_user[i].response,
303 query_user[i].response_len))
304 {
305 /* Force the final result state to failed on failure */
306 ret = false;
307 }
308 }
309
310 return ret;
311}
312
313#if defined(__GNUC__) || defined(__clang__)
314#pragma GCC diagnostic pop
315#endif
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:40
#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:51
@ write
static int orig_stderr
Definition error.c:484
int get_orig_stderr(void)
Definition error.c:487
#define M_FATAL
Definition error.h:90
#define msg(flags,...)
Definition error.h:152
#define ASSERT(x)
Definition error.h:219
#define M_WARN
Definition error.h:92
#define M_ERRNO
Definition error.h:95
char * response
The user's response.
Definition console.h:37
bool win32_service_interrupt(struct win32_signal *ws)
Definition win32.c:619