OpenVPN
common.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/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) 2011-2026 Heiko Hund <heiko.hund@sophos.com>
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#include "service.h"
24#include "validate.h"
25// cppcheck-suppress missingInclude ; we run cppcheck on Linux w/o this file
26#include "eventmsg.h"
27
28#include <pathcch.h>
29
30LPCWSTR service_instance = L"";
31static wchar_t win_sys_path[MAX_PATH];
32
33static DWORD
34GetRegString(HKEY key, LPCWSTR value, LPWSTR data, DWORD size, LPCWSTR default_value)
35{
36 LONG status = RegGetValue(key, NULL, value, RRF_RT_REG_SZ, NULL, (LPBYTE)data, &size);
37
38 if (status == ERROR_FILE_NOT_FOUND && default_value)
39 {
40 size_t len = size / sizeof(data[0]);
41 if (swprintf(data, len, default_value))
42 {
43 status = ERROR_SUCCESS;
44 }
45 }
46
47 if (status != ERROR_SUCCESS)
48 {
49 SetLastError(status);
50 return MsgToEventLog(
52 L"Error querying registry value: HKLM\\SOFTWARE\\" _L(PACKAGE_NAME) L"%ls\\%ls",
53 service_instance, value);
54 }
55
56 return ERROR_SUCCESS;
57}
58
59
68static BOOL
69ensure_trailing_backslash(PWSTR dir, size_t size)
70{
71 HRESULT res = PathCchAddBackslash(dir, size);
72 return (res == S_OK || res == S_FALSE) ? TRUE : FALSE;
73}
74
75
76DWORD
78{
79 WCHAR reg_path[256];
80 WCHAR priority[64];
81 WCHAR append[2];
82 DWORD error;
83 HKEY key;
84 WCHAR install_path[MAX_PATH];
85 WCHAR default_value[MAX_PATH];
86
87 swprintf(reg_path, _countof(reg_path), L"SOFTWARE\\" _L(PACKAGE_NAME) L"%ls", service_instance);
88
89 LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &key);
90 if (status != ERROR_SUCCESS)
91 {
92 SetLastError(status);
93 return MsgToEventLog(M_SYSERR, L"Could not open Registry key HKLM\\%ls not found",
94 reg_path);
95 }
96
97 /* The default value of REG_KEY is the install path */
98 status = GetRegString(key, NULL, install_path, sizeof(install_path), NULL);
99 if (status != ERROR_SUCCESS)
100 {
101 error = status;
102 goto out;
103 }
104
105 swprintf(default_value, _countof(default_value), L"%ls\\bin\\openvpn.exe", install_path);
106 error = GetRegString(key, L"exe_path", s->exe_path, sizeof(s->exe_path), default_value);
107 if (error != ERROR_SUCCESS)
108 {
109 goto out;
110 }
111
112 swprintf(default_value, _countof(default_value), L"%ls\\config\\", install_path);
113 error = GetRegString(key, L"config_dir", s->config_dir, sizeof(s->config_dir), default_value);
114 if (error != ERROR_SUCCESS || !ensure_trailing_backslash(s->config_dir, _countof(s->config_dir)))
115 {
116 goto out;
117 }
118
119 swprintf(default_value, _countof(default_value), L"%ls\\bin\\", install_path);
120 error = GetRegString(key, L"bin_dir", s->bin_dir, sizeof(s->bin_dir), default_value);
121 if (error != ERROR_SUCCESS || !ensure_trailing_backslash(s->bin_dir, _countof(s->bin_dir)))
122 {
123 goto out;
124 }
125
126 error = GetRegString(key, L"config_ext", s->ext_string, sizeof(s->ext_string), L".ovpn");
127 if (error != ERROR_SUCCESS)
128 {
129 goto out;
130 }
131
132 swprintf(default_value, _countof(default_value), L"%ls\\log\\", install_path);
133 error = GetRegString(key, L"log_dir", s->log_dir, sizeof(s->log_dir), default_value);
134 if (error != ERROR_SUCCESS || !ensure_trailing_backslash(s->log_dir, _countof(s->log_dir)))
135 {
136 goto out;
137 }
138
139 error = GetRegString(key, L"priority", priority, sizeof(priority), L"NORMAL_PRIORITY_CLASS");
140 if (error != ERROR_SUCCESS)
141 {
142 goto out;
143 }
144
145 error = GetRegString(key, L"log_append", append, sizeof(append), L"0");
146 if (error != ERROR_SUCCESS)
147 {
148 goto out;
149 }
150
151 /* read if present, else use default */
152 error = GetRegString(key, L"ovpn_admin_group", s->ovpn_admin_group, sizeof(s->ovpn_admin_group),
154 if (error != ERROR_SUCCESS)
155 {
156 goto out;
157 }
158
159 error = GetRegString(key, L"ovpn_service_user", s->ovpn_service_user,
161 if (error != ERROR_SUCCESS)
162 {
163 goto out;
164 }
165
166 /* set process priority */
167 if (!_wcsicmp(priority, L"IDLE_PRIORITY_CLASS"))
168 {
169 s->priority = IDLE_PRIORITY_CLASS;
170 }
171 else if (!_wcsicmp(priority, L"BELOW_NORMAL_PRIORITY_CLASS"))
172 {
173 s->priority = BELOW_NORMAL_PRIORITY_CLASS;
174 }
175 else if (!_wcsicmp(priority, L"NORMAL_PRIORITY_CLASS"))
176 {
177 s->priority = NORMAL_PRIORITY_CLASS;
178 }
179 else if (!_wcsicmp(priority, L"ABOVE_NORMAL_PRIORITY_CLASS"))
180 {
181 s->priority = ABOVE_NORMAL_PRIORITY_CLASS;
182 }
183 else if (!_wcsicmp(priority, L"HIGH_PRIORITY_CLASS"))
184 {
185 s->priority = HIGH_PRIORITY_CLASS;
186 }
187 else
188 {
189 SetLastError(ERROR_INVALID_DATA);
190 error = MsgToEventLog(M_SYSERR, L"Unknown priority name: %ls", priority);
191 goto out;
192 }
193
194 /* set log file append/truncate flag */
195 if (append[0] == L'0')
196 {
197 s->append = FALSE;
198 }
199 else if (append[0] == L'1')
200 {
201 s->append = TRUE;
202 }
203 else
204 {
205 SetLastError(ERROR_INVALID_DATA);
206 error = MsgToEventLog(M_ERR, L"Log file append flag (given as '%ls') must be '0' or '1'",
207 append);
208 goto out;
209 }
210
211out:
212 RegCloseKey(key);
213 return error;
214}
215
216
217LPCWSTR
219{
220 DWORD error;
221 static WCHAR buf[256];
222 DWORD len;
223 LPWSTR tmp = NULL;
224
225 error = GetLastError();
226 len = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
227 | FORMAT_MESSAGE_IGNORE_INSERTS,
228 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL);
229
230 if (!len || !tmp)
231 {
232 swprintf(buf, _countof(buf), L"Unknown error (0x%lx)", error);
233 if (tmp)
234 {
235 LocalFree(tmp);
236 }
237 return buf;
238 }
239
240 /* trim trailing CR / LF / spaces safely */
241 while (len && (tmp[len - 1] == L'\r' || tmp[len - 1] == L'\n' || tmp[len - 1] == L' '))
242 {
243 tmp[--len] = L'\0';
244 }
245
246 swprintf(buf, _countof(buf), L"%ls (0x%lx)", tmp, error);
247
248 LocalFree(tmp);
249 return buf;
250}
251
252
253DWORD
254MsgToEventLog(DWORD flags, LPCWSTR format, ...)
255{
256 HANDLE hEventSource;
257 DWORD error = 0;
258 LPCWSTR err_msg = L"";
259
260 if (flags & MSG_FLAGS_SYS_CODE)
261 {
262 error = GetLastError();
263 err_msg = GetLastErrorText();
264 }
265
266 hEventSource = RegisterEventSource(NULL, APPNAME);
267 if (hEventSource != NULL)
268 {
269 va_list arglist;
270 WCHAR msg[2][256];
271
272 swprintf(msg[0], _countof(msg[0]), L"%ls%ls%ls: %ls", APPNAME, service_instance,
273 (flags & MSG_FLAGS_ERROR) ? L" error" : L"", err_msg);
274
275 va_start(arglist, format);
276 vswprintf(msg[1], _countof(msg[1]), format, arglist);
277 va_end(arglist);
278
279 const WCHAR *mesg[] = { msg[0], msg[1] };
280 ReportEvent(hEventSource,
281 (flags & MSG_FLAGS_ERROR) ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
282 0,
283 EVT_TEXT_2,
284 NULL,
285 2,
286 0,
287 mesg,
288 NULL);
289 DeregisterEventSource(hEventSource);
290 }
291
292 return error;
293}
294
295wchar_t *
296utf8to16_size(const char *utf8, int size)
297{
298 int n = MultiByteToWideChar(CP_UTF8, 0, utf8, size, NULL, 0);
299 if (n == 0)
300 {
301 return NULL;
302 }
303 wchar_t *utf16 = malloc(n * sizeof(wchar_t));
304 if (!utf16)
305 {
306 return NULL;
307 }
308 MultiByteToWideChar(CP_UTF8, 0, utf8, size, utf16, n);
309 return utf16;
310}
311
312const wchar_t *
314{
315 if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path)))
316 {
317 const wchar_t *default_sys_path = L"C:\\Windows\\system32";
318
319 wcscpy_s(win_sys_path, _countof(win_sys_path), default_sys_path);
320 win_sys_path[_countof(win_sys_path) - 1] = L'\0';
321 }
322
323 return win_sys_path;
324}
const wchar_t * get_win_sys_path(void)
Definition common.c:313
wchar_t * utf8to16_size(const char *utf8, int size)
Convert a UTF-8 string to UTF-16.
Definition common.c:296
static DWORD GetRegString(HKEY key, LPCWSTR value, LPWSTR data, DWORD size, LPCWSTR default_value)
Definition common.c:34
static BOOL ensure_trailing_backslash(PWSTR dir, size_t size)
Make sure that a dir path ends with a backslash.
Definition common.c:69
DWORD MsgToEventLog(DWORD flags, LPCWSTR format,...)
Definition common.c:254
LPCWSTR service_instance
Definition common.c:30
DWORD GetOpenvpnSettings(settings_t *s)
Definition common.c:77
LPCWSTR GetLastErrorText(void)
Definition common.c:218
static SERVICE_STATUS status
Definition interactive.c:52
#define M_ERR
Definition error.h:106
#define msg(flags,...)
Definition error.h:152
#define APPNAME
Definition service.h:36
#define M_SYSERR
Definition service.h:45
#define MSG_FLAGS_SYS_CODE
Definition service.h:43
#define MSG_FLAGS_ERROR
Definition service.h:42
Container for unidirectional cipher and HMAC key material.
Definition crypto.h:152
BOOL append
Definition service.h:74
WCHAR ext_string[16]
Definition service.h:69
WCHAR ovpn_admin_group[MAX_NAME]
Definition service.h:71
WCHAR bin_dir[MAX_PATH]
Definition service.h:68
WCHAR ovpn_service_user[MAX_NAME]
Definition service.h:72
WCHAR log_dir[MAX_PATH]
Definition service.h:70
WCHAR config_dir[MAX_PATH]
Definition service.h:67
DWORD priority
Definition service.h:73
WCHAR exe_path[MAX_PATH]
Definition service.h:66
#define _L(q)
Definition basic.h:38
#define OVPN_SERVICE_USER
Definition validate.h:32
#define OVPN_ADMIN_GROUP
Definition validate.h:30
static char * win_sys_path
Definition win32.c:102