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