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-2026 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10 * Copyright (C) 2016-2026 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
59static bool
60get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
61{
62 ASSERT(prompt);
63 ASSERT(input);
64 ASSERT(capacity > 0);
65
66 input[0] = '\0';
67
68 HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
69 int orig_stderr = get_orig_stderr(); /* guaranteed to be always valid */
70 if ((in == INVALID_HANDLE_VALUE) || win32_service_interrupt(&win32_signal)
71 || (_write(orig_stderr, prompt, (unsigned int)strlen(prompt)) == -1))
72 {
73 msg(M_WARN | M_ERRNO, "get_console_input_win32(): unexpected error");
74 return false;
75 }
76
77 bool is_console = (GetFileType(in) == FILE_TYPE_CHAR);
78 DWORD flags_save = 0;
79 int status = 0;
80 WCHAR *winput;
81
82 if (is_console)
83 {
84 if (GetConsoleMode(in, &flags_save))
85 {
86 DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
87 if (echo)
88 {
89 flags |= ENABLE_ECHO_INPUT;
90 }
91 SetConsoleMode(in, flags);
92 }
93 else
94 {
95 is_console = 0;
96 }
97 }
98
99 DWORD len = 0;
100
101 if (is_console)
102 {
103 winput = malloc(capacity * sizeof(WCHAR));
104 if (winput == NULL)
105 {
106 return false;
107 }
108
109 status = ReadConsoleW(in, winput, capacity, &len, NULL);
110 WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL);
111 free(winput);
112 }
113 else
114 {
115 status = ReadFile(in, input, capacity, &len, NULL);
116 }
117
118 string_null_terminate(input, (int)len, capacity);
119 chomp(input);
120
121 if (!echo)
122 {
123 _write(orig_stderr, "\r\n", 2);
124 }
125 if (is_console)
126 {
127 SetConsoleMode(in, flags_save);
128 }
130 {
131 return true;
132 }
133
134 return false;
135}
136
137#endif /* _WIN32 */
138
139
140#ifdef HAVE_TERMIOS_H
141
152static FILE *
153open_tty(const bool write)
154{
155 FILE *ret;
156 ret = fopen("/dev/tty", write ? "w" : "r");
157 if (!ret)
158 {
159 ret = write ? stderr : stdin;
160 }
161 return ret;
162}
163
170static void
171close_tty(FILE *fp)
172{
173 if (fp != stderr && fp != stdin)
174 {
175 fclose(fp);
176 }
177}
178
179#endif /* HAVE_TERMIOS_H */
180
181
192static bool
193get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
194{
195 ASSERT(prompt);
196 ASSERT(input);
197 ASSERT(capacity > 0);
198 input[0] = '\0';
199
200#if defined(_WIN32)
201 return get_console_input_win32(prompt, echo, input, capacity);
202#elif !defined(HAVE_TERMIOS_H)
203 msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt);
204#else
205 bool restore_tty = false;
206 bool ret = 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,
219 "neither stdin nor stderr are a tty device and you have neither a "
220 "controlling tty nor systemd - can't ask for '%s'. If you used --daemon, "
221 "you need to use --askpass to make passphrase-protected keys work, and you "
222 "can not use --auth-nocache.",
223 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 return ret;
264#endif /* if defined(_WIN32) */
265}
266
279bool
281{
282 bool ret = true; /* Presume everything goes okay */
283 int i;
284
285 /* Loop through configured query_user slots */
286 for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++)
287 {
288 if (!get_console_input(query_user[i].prompt, query_user[i].echo, query_user[i].response,
289 query_user[i].response_len))
290 {
291 /* Force the final result state to failed on failure */
292 ret = false;
293 }
294 }
295
296 return ret;
297}
void string_null_terminate(char *str, int len, int capacity)
Null-terminate a fixed-length string buffer.
Definition buffer.c:566
void chomp(char *str)
Remove trailing newline and carriage-return characters from a string.
Definition buffer.c:584
Buffer management functions and garbage collection.
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:41
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:52
@ write
static int orig_stderr
Definition error.c:483
int get_orig_stderr(void)
Definition error.c:486
#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:36
bool win32_service_interrupt(struct win32_signal *ws)
Definition win32.c:716