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-2018 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, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #include "service.h"
25 #include "validate.h"
26 
27 LPCTSTR service_instance = TEXT("");
28 static wchar_t win_sys_path[MAX_PATH];
29 
30 /*
31  * These are necessary due to certain buggy implementations of (v)snprintf,
32  * that don't guarantee null termination for size > 0.
33  */
34 BOOL
35 openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
36 {
37  int len = -1;
38  if (size > 0)
39  {
40  len = _vsntprintf(str, size, format, arglist);
41  str[size - 1] = 0;
42  }
43  return (len >= 0 && (size_t)len < size);
44 }
45 
46 BOOL
47 openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format, ...)
48 {
49  va_list arglist;
50  BOOL res = FALSE;
51  if (size > 0)
52  {
53  va_start(arglist, format);
54  res = openvpn_vsntprintf(str, size, format, arglist);
55  va_end(arglist);
56  }
57  return res;
58 }
59 
60 BOOL
61 openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...)
62 {
63  va_list arglist;
64  int len = -1;
65  if (size > 0)
66  {
67  va_start(arglist, format);
68  len = vswprintf(str, size, format, arglist);
69  va_end(arglist);
70  str[size - 1] = L'\0';
71  }
72  return (len >= 0 && len < size);
73 }
74 
75 static DWORD
76 GetRegString(HKEY key, LPCTSTR value, LPTSTR data, DWORD size, LPCTSTR default_value)
77 {
78  LONG status = RegGetValue(key, NULL, value, RRF_RT_REG_SZ,
79  NULL, (LPBYTE) data, &size);
80 
81  if (status == ERROR_FILE_NOT_FOUND && default_value)
82  {
83  size_t len = size/sizeof(data[0]);
84  if (openvpn_sntprintf(data, len, default_value))
85  {
86  status = ERROR_SUCCESS;
87  }
88  }
89 
90  if (status != ERROR_SUCCESS)
91  {
92  SetLastError(status);
93  return MsgToEventLog(M_SYSERR, TEXT("Error querying registry value: HKLM\\SOFTWARE\\" PACKAGE_NAME "%s\\%s"), service_instance, value);
94  }
95 
96  return ERROR_SUCCESS;
97 }
98 
99 
100 DWORD
102 {
103  TCHAR reg_path[256];
104  TCHAR priority[64];
105  TCHAR append[2];
106  DWORD error;
107  HKEY key;
108  TCHAR install_path[MAX_PATH];
109  TCHAR default_value[MAX_PATH];
110 
111  openvpn_sntprintf(reg_path, _countof(reg_path), TEXT("SOFTWARE\\" PACKAGE_NAME "%s"), service_instance);
112 
113  LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &key);
114  if (status != ERROR_SUCCESS)
115  {
116  SetLastError(status);
117  return MsgToEventLog(M_SYSERR, TEXT("Could not open Registry key HKLM\\%s not found"), reg_path);
118  }
119 
120  /* The default value of REG_KEY is the install path */
121  status = GetRegString(key, NULL, install_path, sizeof(install_path), NULL);
122  if (status != ERROR_SUCCESS)
123  {
124  error = status;
125  goto out;
126  }
127 
128  openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\bin\\openvpn.exe"),
129  install_path);
130  error = GetRegString(key, TEXT("exe_path"), s->exe_path, sizeof(s->exe_path), default_value);
131  if (error != ERROR_SUCCESS)
132  {
133  goto out;
134  }
135 
136  openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\config"), install_path);
137  error = GetRegString(key, TEXT("config_dir"), s->config_dir, sizeof(s->config_dir),
138  default_value);
139  if (error != ERROR_SUCCESS)
140  {
141  goto out;
142  }
143 
144  error = GetRegString(key, TEXT("config_ext"), s->ext_string, sizeof(s->ext_string),
145  TEXT(".ovpn"));
146  if (error != ERROR_SUCCESS)
147  {
148  goto out;
149  }
150 
151  openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\log"), install_path);
152  error = GetRegString(key, TEXT("log_dir"), s->log_dir, sizeof(s->log_dir), default_value);
153  if (error != ERROR_SUCCESS)
154  {
155  goto out;
156  }
157 
158  error = GetRegString(key, TEXT("priority"), priority, sizeof(priority),
159  TEXT("NORMAL_PRIORITY_CLASS"));
160  if (error != ERROR_SUCCESS)
161  {
162  goto out;
163  }
164 
165  error = GetRegString(key, TEXT("log_append"), append, sizeof(append), TEXT("0"));
166  if (error != ERROR_SUCCESS)
167  {
168  goto out;
169  }
170 
171  /* read if present, else use default */
172  error = GetRegString(key, TEXT("ovpn_admin_group"), s->ovpn_admin_group,
173  sizeof(s->ovpn_admin_group), OVPN_ADMIN_GROUP);
174  if (error != ERROR_SUCCESS)
175  {
176  goto out;
177  }
178  /* set process priority */
179  if (!_tcsicmp(priority, TEXT("IDLE_PRIORITY_CLASS")))
180  {
181  s->priority = IDLE_PRIORITY_CLASS;
182  }
183  else if (!_tcsicmp(priority, TEXT("BELOW_NORMAL_PRIORITY_CLASS")))
184  {
185  s->priority = BELOW_NORMAL_PRIORITY_CLASS;
186  }
187  else if (!_tcsicmp(priority, TEXT("NORMAL_PRIORITY_CLASS")))
188  {
189  s->priority = NORMAL_PRIORITY_CLASS;
190  }
191  else if (!_tcsicmp(priority, TEXT("ABOVE_NORMAL_PRIORITY_CLASS")))
192  {
193  s->priority = ABOVE_NORMAL_PRIORITY_CLASS;
194  }
195  else if (!_tcsicmp(priority, TEXT("HIGH_PRIORITY_CLASS")))
196  {
197  s->priority = HIGH_PRIORITY_CLASS;
198  }
199  else
200  {
201  SetLastError(ERROR_INVALID_DATA);
202  error = MsgToEventLog(M_SYSERR, TEXT("Unknown priority name: %s"), priority);
203  goto out;
204  }
205 
206  /* set log file append/truncate flag */
207  if (append[0] == TEXT('0'))
208  {
209  s->append = FALSE;
210  }
211  else if (append[0] == TEXT('1'))
212  {
213  s->append = TRUE;
214  }
215  else
216  {
217  SetLastError(ERROR_INVALID_DATA);
218  error = MsgToEventLog(M_ERR, TEXT("Log file append flag (given as '%s') must be '0' or '1'"), append);
219  goto out;
220  }
221 
222 out:
223  RegCloseKey(key);
224  return error;
225 }
226 
227 
228 LPCTSTR
230 {
231  static TCHAR buf[256];
232  DWORD len;
233  LPTSTR tmp = NULL;
234 
235  len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
236  NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL);
237 
238  if (len == 0 || (long) _countof(buf) < (long) len + 14)
239  {
240  buf[0] = TEXT('\0');
241  }
242  else
243  {
244  tmp[_tcslen(tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */
245  openvpn_sntprintf(buf, _countof(buf), TEXT("%s (0x%x)"), tmp, GetLastError());
246  }
247 
248  if (tmp)
249  {
250  LocalFree(tmp);
251  }
252 
253  return buf;
254 }
255 
256 
257 DWORD
258 MsgToEventLog(DWORD flags, LPCTSTR format, ...)
259 {
260  HANDLE hEventSource;
261  TCHAR msg[2][256];
262  DWORD error = 0;
263  LPCTSTR err_msg = TEXT("");
264  va_list arglist;
265 
266  if (flags & MSG_FLAGS_SYS_CODE)
267  {
268  error = GetLastError();
269  err_msg = GetLastErrorText();
270  }
271 
272  hEventSource = RegisterEventSource(NULL, APPNAME);
273  if (hEventSource != NULL)
274  {
275  openvpn_sntprintf(msg[0], _countof(msg[0]),
276  TEXT("%s%s%s: %s"), APPNAME, service_instance,
277  (flags & MSG_FLAGS_ERROR) ? TEXT(" error") : TEXT(""), err_msg);
278 
279  va_start(arglist, format);
280  openvpn_vsntprintf(msg[1], _countof(msg[1]), format, arglist);
281  va_end(arglist);
282 
283  const TCHAR *mesg[] = { msg[0], msg[1] };
284  ReportEvent(hEventSource, flags & MSG_FLAGS_ERROR ?
285  EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
286  0, 0, NULL, 2, 0, mesg, NULL);
287  DeregisterEventSource(hEventSource);
288  }
289 
290  return error;
291 }
292 
293 /* Convert a utf8 string to utf16. Caller should free the result */
294 wchar_t *
295 utf8to16(const char *utf8)
296 {
297  int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
298  wchar_t *utf16 = malloc(n * sizeof(wchar_t));
299  if (!utf16)
300  {
301  return NULL;
302  }
303  MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, n);
304  return utf16;
305 }
306 
307 const wchar_t *
309 {
310  const wchar_t *default_sys_path = L"C:\\Windows\\system32";
311 
312  if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path)))
313  {
314  wcsncpy(win_sys_path, default_sys_path, _countof(win_sys_path));
315  win_sys_path[_countof(win_sys_path) - 1] = L'\0';
316  }
317 
318  return win_sys_path;
319 }
BOOL openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format,...)
Definition: common.c:47
DWORD MsgToEventLog(DWORD flags, LPCTSTR format,...)
Definition: common.c:258
LPCTSTR service_instance
Definition: common.c:27
TCHAR config_dir[MAX_PATH]
Definition: service.h:67
static DWORD GetRegString(HKEY key, LPCTSTR value, LPTSTR data, DWORD size, LPCTSTR default_value)
Definition: common.c:76
#define MSG_FLAGS_ERROR
Definition: service.h:44
TCHAR exe_path[MAX_PATH]
Definition: service.h:66
BOOL append
Definition: service.h:72
DWORD priority
Definition: service.h:71
TCHAR ext_string[16]
Definition: service.h:68
static wchar_t win_sys_path[MAX_PATH]
Definition: common.c:28
list flags
#define malloc
Definition: cmocka.c:1795
#define PACKAGE_NAME
Definition: config.h:730
#define OVPN_ADMIN_GROUP
Definition: validate.h:32
wchar_t * utf8to16(const char *utf8)
Definition: common.c:295
static SERVICE_STATUS status
Definition: automatic.c:43
DWORD GetOpenvpnSettings(settings_t *s)
Definition: common.c:101
#define msg
Definition: error.h:173
LPCTSTR GetLastErrorText()
Definition: common.c:229
BOOL openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
Definition: common.c:35
TCHAR ovpn_admin_group[MAX_NAME]
Definition: service.h:70
BOOL openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format,...)
Definition: common.c:61
#define M_ERR
Definition: error.h:110
#define M_SYSERR
Definition: service.h:47
TCHAR log_dir[MAX_PATH]
Definition: service.h:69
#define MSG_FLAGS_SYS_CODE
Definition: service.h:45
const wchar_t * get_win_sys_path(void)
Definition: common.c:308
#define APPNAME
Definition: service.h:38
Container for unidirectional cipher and HMAC key material.
Definition: crypto.h:151