OpenVPN
win32-util.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 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23/*
24 * Win32-specific OpenVPN code, targeted at the mingw
25 * development environment.
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include "syshead.h"
33
34#ifdef _WIN32
35
36#include "buffer.h"
37#include "win32-util.h"
38
39WCHAR *
40wide_string(const char *utf8, struct gc_arena *gc)
41{
42 int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
43 WCHAR *ucs16 = gc_malloc(n * sizeof(WCHAR), false, gc);
44 MultiByteToWideChar(CP_UTF8, 0, utf8, -1, ucs16, n);
45 return ucs16;
46}
47
48/* special to cmd.exe, which CreateProcess() uses to run .bat/.cmd (VU#123335) */
49#define CMD_QUOTE_TRIGGERS " &|<>^%()!"
50
51static bool
53{
54 for (const char *c = str; *c != '\0'; ++c)
55 {
56 if (strchr(CMD_QUOTE_TRIGGERS, *c) != NULL)
57 {
58 return true;
59 }
60 }
61 return false;
62}
63
64WCHAR *
65wide_cmd_line(const struct argv *a, struct gc_arena *gc)
66{
67 size_t nchars = 1;
68 size_t maxlen = 0;
69 size_t i;
70 struct buffer buf;
71 char *work = NULL;
72
73 if (!a)
74 {
75 return NULL;
76 }
77
78 for (i = 0; i < a->argc; ++i)
79 {
80 const char *arg = a->argv[i];
81 const size_t len = strlen(arg);
82 nchars += len + 3;
83 if (len > maxlen)
84 {
85 maxlen = len;
86 }
87 }
88
89 work = gc_malloc(maxlen + 1, false, gc);
91 buf = alloc_buf_gc(nchars, gc);
92
93 for (i = 0; i < a->argc; ++i)
94 {
95 const char *arg = a->argv[i];
96 strcpy(work, arg);
97 /* cmd.exe expands %VAR% and !VAR! even inside quotes, so a value like
98 * %X509_0_O% could turn back into a quote and start a new command.
99 * Replace those along with the double quotes and CRLF. */
101 if (i)
102 {
103 buf_printf(&buf, " ");
104 }
106 {
107 buf_printf(&buf, "\"%s\"", work);
108 }
109 else
110 {
111 buf_printf(&buf, "%s", work);
112 }
113 }
114
115 return wide_string(BSTR(&buf), gc);
116}
117
118char *
119utf16to8(const wchar_t *utf16, struct gc_arena *gc)
120{
121 char *utf8 = NULL;
122 int n = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL);
123 if (n > 0)
124 {
125 utf8 = gc_malloc(n, true, gc);
126 if (utf8)
127 {
128 WideCharToMultiByte(CP_UTF8, 0, utf16, -1, utf8, n, NULL, NULL);
129 }
130 }
131 return utf8;
132}
133
134/*
135 * Return true if filename is safe to be used on Windows,
136 * by avoiding the following reserved names:
137 *
138 * CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9,
139 * LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, and CLOCK$
140 *
141 * See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
142 */
143
144static bool
145cmp_prefix(const char *str, const bool n, const char *pre)
146{
147 size_t i = 0;
148
149 if (!str)
150 {
151 return false;
152 }
153
154 while (true)
155 {
156 const int c1 = pre[i];
157 int c2 = str[i];
158 ++i;
159 if (c1 == '\0')
160 {
161 if (n)
162 {
163 if (isdigit(c2))
164 {
165 c2 = str[i];
166 }
167 else
168 {
169 return false;
170 }
171 }
172 return c2 == '\0' || c2 == '.';
173 }
174 else if (c2 == '\0')
175 {
176 return false;
177 }
178 if (c1 != tolower(c2))
179 {
180 return false;
181 }
182 }
183}
184
185bool
186win_safe_filename(const char *fn)
187{
188 if (cmp_prefix(fn, false, "con"))
189 {
190 return false;
191 }
192 if (cmp_prefix(fn, false, "prn"))
193 {
194 return false;
195 }
196 if (cmp_prefix(fn, false, "aux"))
197 {
198 return false;
199 }
200 if (cmp_prefix(fn, false, "nul"))
201 {
202 return false;
203 }
204 if (cmp_prefix(fn, true, "com"))
205 {
206 return false;
207 }
208 if (cmp_prefix(fn, true, "lpt"))
209 {
210 return false;
211 }
212 if (cmp_prefix(fn, false, "clock$"))
213 {
214 return false;
215 }
216 return true;
217}
218
219const char *
221{
222 static char tmpdir[MAX_PATH];
223 WCHAR wtmpdir[MAX_PATH];
224
225 if (!GetTempPathW(_countof(wtmpdir), wtmpdir))
226 {
227 return NULL;
228 }
229
230 int ret = WideCharToMultiByte(CP_UTF8, 0, wtmpdir, -1, NULL, 0, NULL, NULL);
231 /* According to documentation ret is never < 0, but include it here just in case */
232 if (ret <= 0)
233 {
234 msg(M_WARN | M_ERRNO, "Conversion of path name failed.");
235 return NULL;
236 }
237 if ((unsigned int)ret > sizeof(tmpdir))
238 {
239 msg(M_WARN, "Could not get temporary directory. Path is too long."
240 " Consider using --tmp-dir");
241 return NULL;
242 }
243
244 WideCharToMultiByte(CP_UTF8, 0, wtmpdir, -1, tmpdir, sizeof(tmpdir), NULL, NULL);
245 return tmpdir;
246}
247
248bool
249win_path_in_dir(const WCHAR *path, const WCHAR *dir)
250{
251 size_t dir_len = wcslen(dir);
252 /* dir_len <= 1 guards the dir[dir_len - 1] access below and rejects a
253 * degenerate single-character directory (a normalized absolute path is
254 * always longer). */
255 if (dir_len <= 1 || wcsnicmp(dir, path, dir_len) != 0)
256 {
257 return false;
258 }
259
260 /* A plain prefix match is not sufficient: if dir is "C:\foo" then
261 * "C:\foo_evil\bar.dll" shares the prefix but is not inside "C:\foo".
262 * Require that the matched prefix ends on a path separator, i.e. either
263 * dir already ends with a separator or the character following the prefix
264 * in path is one. */
265 if (dir[dir_len - 1] == L'\\' || dir[dir_len - 1] == L'/')
266 {
267 return true;
268 }
269
270 WCHAR next = path[dir_len];
271 return next == L'\\' || next == L'/';
272}
273
274#endif /* _WIN32 */
bool buf_printf(struct buffer *buf, const char *format,...)
printf-style append to a buffer with overflow check.
Definition buffer.c:226
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Allocate memory and, optionally, zero it.
Definition buffer.c:318
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Allocate a buffer of the given size under garbage collection.
Definition buffer.c:77
bool string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace)
Modifies a string in place by replacing certain classes of characters of it with a specified characte...
Definition buffer.c:995
Buffer management functions and garbage collection.
#define CC_EXCLAMATION
exclamation mark
Definition buffer.h:1728
#define CC_PERCENT
percent sign
Definition buffer.h:1727
#define CC_DOUBLE_QUOTE
double quote
Definition buffer.h:1725
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:157
#define CC_CRLF
carriage return or newline
Definition buffer.h:1737
static void check_malloc_return(void *p)
Abort if a memory allocation returned NULL.
Definition buffer.h:2146
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:1708
#define msg(flags,...)
Definition error.h:152
#define M_WARN
Definition error.h:92
#define M_ERRNO
Definition error.h:95
Definition argv.h:35
char ** argv
Definition argv.h:39
size_t argc
Definition argv.h:38
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:76
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
struct gc_arena gc
Definition test_ssl.c:122
bool win_path_in_dir(const WCHAR *path, const WCHAR *dir)
Check whether path resides within directory dir.
Definition win32-util.c:249
char * utf16to8(const wchar_t *utf16, struct gc_arena *gc)
Definition win32-util.c:119
static bool cmp_prefix(const char *str, const bool n, const char *pre)
Definition win32-util.c:145
bool win_safe_filename(const char *fn)
Definition win32-util.c:186
const char * win_get_tempdir(void)
Definition win32-util.c:220
#define CMD_QUOTE_TRIGGERS
Definition win32-util.c:49
WCHAR * wide_string(const char *utf8, struct gc_arena *gc)
Definition win32-util.c:40
WCHAR * wide_cmd_line(const struct argv *a, struct gc_arena *gc)
Definition win32-util.c:65
static bool argv_element_needs_quotes(const char *str)
Definition win32-util.c:52