Browse code

Fix console prompts with redirected log

When openvpn needs to prompt user for a password
(for example, to set management interface password),
the prompt is written to standard error device.

When log is redirected to a file, that prompt is written
to that file and not to the "original" stderr. Moreover, on recent
Insider build (21390.2025) openvpn exits with fatal error

get_console_input_win32(): unexpected error: No such device or address
(errno=6)

while attempting to write that prompt.

When redirecting stdout/stderr, we use _dup2() to associate stderr
descriptor with a log file. This call closes file associated
with stderr descriptor, which might explain why it has stopped
working (original stderr is closed and WriteFile() fails) and on
older versions it appears to work "by accident" - not failing
but use redirected stderr instead of original one.

Fix by creating new file descriptor with _dup() for stderr
before redirect and use this descriptor for writing prompts.

While on it, make code a bit more C99-ish by moving variables
declaration from the beginning of the scope to the actual
initialisation.

Signed-off-by: Lev Stipakov <lev@openvpn.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20210625010405.224-1-lstipakov@gmail.com>
URL: https://www.mail-archive.com/search?l=mid&q=20210625010405.224-1-lstipakov@gmail.com
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Lev Stipakov authored on 2021/06/25 10:04:05
Showing 3 changed files
... ...
@@ -62,23 +62,17 @@
62 62
 static bool
63 63
 get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
64 64
 {
65
-    HANDLE in = INVALID_HANDLE_VALUE;
66
-    HANDLE err = INVALID_HANDLE_VALUE;
67
-    DWORD len = 0;
68
-
69 65
     ASSERT(prompt);
70 66
     ASSERT(input);
71 67
     ASSERT(capacity > 0);
72 68
 
73 69
     input[0] = '\0';
74 70
 
75
-    in = GetStdHandle(STD_INPUT_HANDLE);
76
-    err = get_orig_stderr();
77
-
78
-    if (in == INVALID_HANDLE_VALUE
79
-        || err == INVALID_HANDLE_VALUE
71
+    HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
72
+    int orig_stderr = get_orig_stderr(); // guaranteed to be always valid
73
+    if ((in == INVALID_HANDLE_VALUE)
80 74
         || win32_service_interrupt(&win32_signal)
81
-        || !WriteFile(err, prompt, strlen(prompt), &len, NULL))
75
+        || (_write(orig_stderr, prompt, strlen(prompt)) == -1))
82 76
     {
83 77
         msg(M_WARN|M_ERRNO, "get_console_input_win32(): unexpected error");
84 78
         return false;
... ...
@@ -106,6 +100,8 @@ get_console_input_win32(const char *prompt, const bool echo, char *input, const
106 106
         }
107 107
     }
108 108
 
109
+    DWORD len = 0;
110
+
109 111
     if (is_console)
110 112
     {
111 113
         winput = malloc(capacity * sizeof(WCHAR));
... ...
@@ -128,7 +124,7 @@ get_console_input_win32(const char *prompt, const bool echo, char *input, const
128 128
 
129 129
     if (!echo)
130 130
     {
131
-        WriteFile(err, "\r\n", 2, &len, NULL);
131
+        _write(orig_stderr, "\r\n", 2);
132 132
     }
133 133
     if (is_console)
134 134
     {
... ...
@@ -491,22 +491,12 @@ close_syslog(void)
491 491
 }
492 492
 
493 493
 #ifdef _WIN32
494
+static int orig_stderr;
494 495
 
495
-static HANDLE orig_stderr;
496
-
497
-HANDLE
498
-get_orig_stderr(void)
496
+int get_orig_stderr()
499 497
 {
500
-    if (orig_stderr)
501
-    {
502
-        return orig_stderr;
503
-    }
504
-    else
505
-    {
506
-        return GetStdHandle(STD_ERROR_HANDLE);
507
-    }
498
+    return orig_stderr ? orig_stderr : _fileno(stderr);
508 499
 }
509
-
510 500
 #endif
511 501
 
512 502
 void
... ...
@@ -550,16 +540,12 @@ redirect_stdout_stderr(const char *file, bool append)
550 550
         }
551 551
 
552 552
         /* save original stderr for password prompts */
553
-        orig_stderr = GetStdHandle(STD_ERROR_HANDLE);
554
-
555
-#if 0 /* seems not be necessary with stdout/stderr redirection below*/
556
-        /* set up for redirection */
557
-        if (!SetStdHandle(STD_OUTPUT_HANDLE, log_handle)
558
-            || !SetStdHandle(STD_ERROR_HANDLE, log_handle))
553
+        orig_stderr = _dup(_fileno(stderr));
554
+        if (orig_stderr == -1)
559 555
         {
560
-            msg(M_ERR, "Error: cannot redirect stdout/stderr to --log file: %s", file);
556
+            msg(M_WARN | M_ERRNO, "Warning: cannot duplicate stderr, password prompts will appear in log file instead of console.");
557
+            orig_stderr = _fileno(stderr);
561 558
         }
562
-#endif
563 559
 
564 560
         /* direct stdout/stderr to point to log_handle */
565 561
         log_fd = _open_osfhandle((intptr_t)log_handle, _O_TEXT);
... ...
@@ -256,8 +256,8 @@ void close_syslog(void);
256 256
 void redirect_stdout_stderr(const char *file, bool append);
257 257
 
258 258
 #ifdef _WIN32
259
-/* get original stderr handle, even if redirected by --log/--log-append */
260
-HANDLE get_orig_stderr(void);
259
+/* get original stderr fd, even if redirected by --log/--log-append */
260
+int get_orig_stderr(void);
261 261
 
262 262
 #endif
263 263