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);
98 if (i)
99 {
100 buf_printf(&buf, " ");
101 }
103 {
104 buf_printf(&buf, "\"%s\"", work);
105 }
106 else
107 {
108 buf_printf(&buf, "%s", work);
109 }
110 }
111
112 return wide_string(BSTR(&buf), gc);
113}
114
115char *
116utf16to8(const wchar_t *utf16, struct gc_arena *gc)
117{
118 char *utf8 = NULL;
119 int n = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL);
120 if (n > 0)
121 {
122 utf8 = gc_malloc(n, true, gc);
123 if (utf8)
124 {
126 }
127 }
128 return utf8;
129}
130
131/*
132 * Return true if filename is safe to be used on Windows,
133 * by avoiding the following reserved names:
134 *
135 * CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9,
136 * LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, and CLOCK$
137 *
138 * See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
139 */
140
141static bool
142cmp_prefix(const char *str, const bool n, const char *pre)
143{
144 size_t i = 0;
145
146 if (!str)
147 {
148 return false;
149 }
150
151 while (true)
152 {
153 const int c1 = pre[i];
154 int c2 = str[i];
155 ++i;
156 if (c1 == '\0')
157 {
158 if (n)
159 {
160 if (isdigit(c2))
161 {
162 c2 = str[i];
163 }
164 else
165 {
166 return false;
167 }
168 }
169 return c2 == '\0' || c2 == '.';
170 }
171 else if (c2 == '\0')
172 {
173 return false;
174 }
175 if (c1 != tolower(c2))
176 {
177 return false;
178 }
179 }
180}
181
182bool
184{
185 if (cmp_prefix(fn, false, "con"))
186 {
187 return false;
188 }
189 if (cmp_prefix(fn, false, "prn"))
190 {
191 return false;
192 }
193 if (cmp_prefix(fn, false, "aux"))
194 {
195 return false;
196 }
197 if (cmp_prefix(fn, false, "nul"))
198 {
199 return false;
200 }
201 if (cmp_prefix(fn, true, "com"))
202 {
203 return false;
204 }
205 if (cmp_prefix(fn, true, "lpt"))
206 {
207 return false;
208 }
209 if (cmp_prefix(fn, false, "clock$"))
210 {
211 return false;
212 }
213 return true;
214}
215
216const char *
218{
219 static char tmpdir[MAX_PATH];
221
223 {
224 return NULL;
225 }
226
227 int ret = WideCharToMultiByte(CP_UTF8, 0, wtmpdir, -1, NULL, 0, NULL, NULL);
228 /* According to documentation ret is never < 0, but include it here just in case */
229 if (ret <= 0)
230 {
231 msg(M_WARN | M_ERRNO, "Conversion of path name failed.");
232 return NULL;
233 }
234 if ((unsigned int)ret > sizeof(tmpdir))
235 {
236 msg(M_WARN, "Could not get temporary directory. Path is too long."
237 " Consider using --tmp-dir");
238 return NULL;
239 }
240
242 return tmpdir;
243}
244
245bool
247{
248 size_t dir_len = wcslen(dir);
249 /* dir_len <= 1 guards the dir[dir_len - 1] access below and rejects a
250 * degenerate single-character directory (a normalized absolute path is
251 * always longer). */
252 if (dir_len <= 1 || wcsnicmp(dir, path, dir_len) != 0)
253 {
254 return false;
255 }
256
257 /* A plain prefix match is not sufficient: if dir is "C:\foo" then
258 * "C:\foo_evil\bar.dll" shares the prefix but is not inside "C:\foo".
259 * Require that the matched prefix ends on a path separator, i.e. either
260 * dir already ends with a separator or the character following the prefix
261 * in path is one. */
262 if (dir[dir_len - 1] == L'\\' || dir[dir_len - 1] == L'/')
263 {
264 return true;
265 }
266
267 WCHAR next = path[dir_len];
268 return next == L'\\' || next == L'/';
269}
270
271#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:1005
Buffer management functions and garbage collection.
#define CC_DOUBLE_QUOTE
double quote
Definition buffer.h:1661
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:151
#define CC_CRLF
carriage return or newline
Definition buffer.h:1673
static void check_malloc_return(void *p)
Abort if a memory allocation returned NULL.
Definition buffer.h:2082
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:1644
#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
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:246
char * utf16to8(const wchar_t *utf16, struct gc_arena *gc)
Definition win32-util.c:116
static bool cmp_prefix(const char *str, const bool n, const char *pre)
Definition win32-util.c:142
bool win_safe_filename(const char *fn)
Definition win32-util.c:183
const char * win_get_tempdir(void)
Definition win32-util.c:217
#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