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-2018 OpenVPN Inc <sales@openvpn.net>
9  * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10  * Copyright (C) 2016-2018 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 _WIN32
44 
45 #include "win32.h"
46 
58 static bool
59 get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
60 {
61  HANDLE in = INVALID_HANDLE_VALUE;
62  HANDLE err = INVALID_HANDLE_VALUE;
63  DWORD len = 0;
64 
65  ASSERT(prompt);
66  ASSERT(input);
67  ASSERT(capacity > 0);
68 
69  input[0] = '\0';
70 
71  in = GetStdHandle(STD_INPUT_HANDLE);
72  err = get_orig_stderr();
73 
74  if (in != INVALID_HANDLE_VALUE
75  && err != INVALID_HANDLE_VALUE
77  && WriteFile(err, prompt, strlen(prompt), &len, NULL))
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  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  WriteFile(err, "\r\n", 2, &len, NULL);
124  }
125  if (is_console)
126  {
127  SetConsoleMode(in, flags_save);
128  }
129  if (status && !win32_service_interrupt(&win32_signal))
130  {
131  return true;
132  }
133  }
134 
135  return false;
136 }
137 
138 #endif /* _WIN32 */
139 
140 
141 #ifdef HAVE_GETPASS
142 
153 static FILE *
154 open_tty(const bool write)
155 {
156  FILE *ret;
157  ret = fopen("/dev/tty", write ? "w" : "r");
158  if (!ret)
159  {
160  ret = write ? stderr : stdin;
161  }
162  return ret;
163 }
164 
171 static void
172 close_tty(FILE *fp)
173 {
174  if (fp != stderr && fp != stdin)
175  {
176  fclose(fp);
177  }
178 }
179 
180 #endif /* HAVE_GETPASS */
181 
182 
193 static bool
194 get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
195 {
196  bool ret = false;
197  ASSERT(prompt);
198  ASSERT(input);
199  ASSERT(capacity > 0);
200  input[0] = '\0';
201 
202 #if defined(_WIN32)
203  return get_console_input_win32(prompt, echo, input, capacity);
204 #elif defined(HAVE_GETPASS)
205 
206  /* did we --daemon'ize before asking for passwords?
207  * (in which case neither stdin or stderr are connected to a tty and
208  * /dev/tty can not be open()ed anymore)
209  */
210  if (!isatty(0) && !isatty(2) )
211  {
212  int fd = open( "/dev/tty", O_RDWR );
213  if (fd < 0)
214  {
215  msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a "
216  "controlling tty nor systemd - can't ask for '%s'. If you used --daemon, "
217  "you need to use --askpass to make passphrase-protected keys work, and you "
218  "can not use --auth-nocache.", prompt );
219  }
220  close(fd);
221  }
222 
223  if (echo)
224  {
225  FILE *fp;
226 
227  fp = open_tty(true);
228  fprintf(fp, "%s", prompt);
229  fflush(fp);
230  close_tty(fp);
231 
232  fp = open_tty(false);
233  if (fgets(input, capacity, fp) != NULL)
234  {
235  chomp(input);
236  ret = true;
237  }
238  close_tty(fp);
239  }
240  else
241  {
242  char *gp = getpass(prompt);
243  if (gp)
244  {
245  strncpynt(input, gp, capacity);
246  secure_memzero(gp, strlen(gp));
247  ret = true;
248  }
249  }
250 #else /* if defined(_WIN32) */
251  msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt);
252 #endif /* if defined(_WIN32) */
253  return ret;
254 }
255 
256 
269 bool
271 {
272  bool ret = true; /* Presume everything goes okay */
273  int i;
274 
275  /* Loop through configured query_user slots */
276  for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++)
277  {
280  {
281  /* Force the final result state to failed on failure */
282  ret = false;
283  }
284  }
285 
286  return ret;
287 }
char * response
The user&#39;s response.
Definition: console.h:37
static void strncpynt(char *dest, const char *src, size_t maxlen)
Definition: buffer.h:348
HANDLE get_orig_stderr(void)
Definition: error.c:505
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition: buffer.h:401
bool win32_service_interrupt(struct win32_signal *ws)
Definition: win32.c:619
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:221
list flags
void string_null_terminate(char *str, int len, int capacity)
Definition: buffer.c:636
static bool get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
Get input from a Windows console.
#define malloc
Definition: cmocka.c:1795
void chomp(char *str)
Definition: buffer.c:653
#define QUERY_USER_NUMSLOTS
Definition: console.h:42
size_t response_len
Length the of the user response.
Definition: console.h:38
static SERVICE_STATUS status
Definition: automatic.c:43
#define msg
Definition: error.h:173
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 M_FATAL
Definition: error.h:94
static bool get_console_input(const char *prompt, const bool echo, char *input, const int capacity)
Core function for getting input from console.
#define free
Definition: cmocka.c:1850